/* =========================================================================
   Design system do Agenda Beauty
   =========================================================================
   Este arquivo era um <style> de 390 linhas dentro de templates/base.html.
   Saiu de la para poder ser cacheado e para os tokens terem um lugar so.

   AS CORES DA CLINICA NAO MORAM AQUI. `--ds-primaria` e `--ds-secundaria`
   vem do banco, por tenant, e por isso continuam inline em base.html. Escrever
   uma cor fixa para elas aqui quebra o white-label.

   As decisoes por tras dos valores estao em PLANO-DESIGN.md (D1-D12).
   ========================================================================= */

/* =========================================================================
   Tipografia da identidade Agenda Beauty
   =========================================================================
   Outfit  -> marca, titulos, numeros grandes
   DM Sans -> interface, tabelas, microcopia (corpo padrao)
   DM Mono -> dados, horarios, rotulos em maiuscula

   As familias sao carregadas por CDN em partials/_brand_head.html. Aqui ficam
   os padroes de aplicacao, para valerem tambem nas paginas soltas de conta que
   nao passam pela config do Tailwind.
   ========================================================================= */
:root {
    --ab-fonte-marca:   'Outfit', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --ab-fonte-corpo:   'DM Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --ab-fonte-dados:   'DM Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}

body {
    font-family: var(--ab-fonte-corpo);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6,
.font-brand {
    font-family: var(--ab-fonte-marca);
    letter-spacing: -0.01em;
}

.font-data,
time.font-data,
.ds-secao-titulo,
.ds-cabecalho__linha time {
    font-family: var(--ab-fonte-dados);
}

/* Numeros de navegacao em DM Mono: alinham em coluna e batem o tom "clinico". */
.ds-paginacao__posicao {
    font-family: var(--ab-fonte-dados);
}

:root {
    /* --- texto ------------------------------------------------------- */
    --ds-tinta:          #111827;  /* titulo, valor em destaque */
    --ds-tinta-fraca:    #374151;  /* rotulo de campo, texto de botao secundario */
    --ds-apoio:          #6b7280;  /* subtitulo, texto de ajuda */
    --ds-fraco:          #9ca3af;  /* rotulo em maiuscula, placeholder */

    /* --- superficie e traco ------------------------------------------- */
    --ds-superficie:     #ffffff;
    --ds-areia:          #eee9e2;  /* fundo "areia" da identidade (telas de marca, faixas) */
    --ds-fundo:          #f9fafb;  /* fundo de painel dentro do cartao */
    --ds-borda:          #e5e7eb;  /* borda de cartao e regua */
    --ds-borda-campo:    #d1d5db;  /* borda de campo de formulario */
    --ds-regua:          #f3f4f6;  /* separador interno, mais leve que a borda */

    /* --- situacao (D4: nunca so a cor — o rotulo carrega o significado) */
    --ds-ok-fundo:       #dcfce7;
    --ds-ok-texto:       #166534;
    --ds-atencao-fundo:  #fef3c7;
    --ds-atencao-texto:  #92400e;
    --ds-erro-fundo:     #fee2e2;
    --ds-erro-texto:     #991b1b;
    --ds-neutro-fundo:   #f3f4f6;
    --ds-neutro-texto:   #4b5563;
    --ds-info-fundo:     #eef2ff;
    --ds-info-texto:     #4338ca;

    /* --- WhatsApp: toda acao de envio de mensagem usa o verde claro da marca */
    --ds-wa-fundo:       #e9f9ef;
    --ds-wa-fundo-hover: #d6f2e1;
    --ds-wa-borda:       #b3e6ca;
    --ds-wa-tinta:       #0a7c53;  /* texto do botao */
    --ds-wa-marca:       #25d366;  /* verde da marca, so no icone */

    /* --- erro de formulario (D11) ------------------------------------- */
    --ds-erro-borda:     #dc2626;  /* borda do campo que falhou */
    --ds-erro-msg:       #b91c1c;  /* texto da mensagem sob o campo */
    --ds-erro-aviso:     #fef2f2;  /* fundo do resumo no topo */
    --ds-erro-aviso-borda: #fecaca;

    /* --- forma -------------------------------------------------------- */
    --ds-raio-campo:     8px;
    --ds-raio-cartao:    12px;
    --ds-raio-chip:      10px;

    /* --- toque (D2: 44px e o piso de qualquer alvo) -------------------- */
    --ds-toque:          44px;
    --ds-toque-chip:     36px;     /* so para fila rolavel de filtros */

    /* --- tipografia --------------------------------------------------- */
    --ds-campo:          16px;     /* D9: no celular NUNCA abaixo disto */
    --ds-campo-desktop:  14px;
    --ds-rotulo:         14px;
    --ds-ajuda:          12.5px;
    --ds-rotulo-tabela:  11.2px;   /* o ::before do cartao de tabela */

    /* --- folga -------------------------------------------------------- */
    --ds-folga-campo:    6px;      /* entre rotulo, campo, ajuda e erro */
    --ds-folga-entre:    20px;     /* entre um campo e o proximo */

    /* --- motion (D14) ------------------------------------------------- */
    --ds-dur:            160ms;
    --ds-dur-secao:      220ms;
    --ds-ease:           cubic-bezier(0.22, 1, 0.36, 1);
}

/* No desktop o campo pode voltar a 14px: o zoom do Safari movel que motiva
   os 16px (D9) nao existe la. O piso de 44px continua valendo. */
@media (min-width: 640px) {
    :root { --ds-campo: var(--ds-campo-desktop); }
}

/* ========================================================================= */
/* Abaixo: o CSS que ja existia em base.html, movido sem alteracao.          */
/* ========================================================================= */

/* Enquanto o Alpine nao inicia, x-show ainda nao escondeu nada. Sem esta
   regra o bloco de filtros PISCA aberto no carregamento — o exato efeito que o
   colapso veio evitar. */
[x-cloak] { display: none !important; }

/* --- moldura do app (shell) ----------------------------------------------
   Substitui o `h-screen` do Tailwind em base.html.

   A altura do APP mora no `html`, nao no `.ds-shell`. Se a moldura pede
   100vh/100dvh sozinha, qualquer faixa acima dela (trial, suporte) soma e o
   `overflow: hidden` do body corta o rodape. No desktop isso so "fecha" em
   F11: a chrome do navegador some e a folga extra esconde o corte.

   Ordem no `html`:
   - `100vh` — fallback sem svh/dvh
   - `100svh` — viewport PEQUENO (janela visivel com abas/endereco)
   - `100dvh` — acompanha a barra de endereco no celular
   - `var(--vvh)` — pixel exato do visualViewport, escrito pelo script em
     base.html. E o que faz a moldura caber na janela, nao no monitor.

   O body vira coluna: as faixas ficam no tamanho natural; `.ds-shell` come
   o resto (`flex: 1` + `min-height: 0`). */
html:has(.ds-shell) {
    overflow: hidden;
    overflow-x: clip;
    height: 100vh;
    height: 100svh;
    height: 100dvh;
    height: var(--vvh, 100dvh);
    overscroll-behavior: none;
}
html:has(.ds-shell) body {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    height: 100%;
    min-height: 0;
    overscroll-behavior: none;
}
html:has(.ds-shell) body.bg-gray-100.min-h-screen {
    min-height: 0;
}
.ds-shell {
    display: flex;
    flex: 1 1 0%;
    min-height: 0;
    max-height: 100%;
}
/* No celular a gaveta e `fixed inset-y-0` (viewport de layout). Se o
   layout for maior que a janela visivel, ela passa do rodape. */
html:has(.ds-shell) [data-sidebar-drawer] {
    max-height: var(--vvh, 100dvh);
}
.ds-shell > main {
    min-width: 0;
    min-height: 0;
    flex: 1 1 auto;
    height: auto;
    align-self: stretch;
    overflow-y: auto;
    /* `clip`, nao `hidden`: no WebKit, overflow-x:hidden no ancestral faz
       `position:fixed` da barra de acoes se comportar como absolute no
       conteudo que rola — a barra some no meio de um formulario longo
       (cadastro), embora uma lista curta ainda a mostre. `clip` corta o
       estouro horizontal sem criar bloco de posicionamento. */
    overflow-x: clip;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-y: contain;
    scroll-padding-bottom: 40vh;
}

/* Accessibility: visible keyboard focus indicator */
*:focus-visible {
    outline: 2px solid #6366f1;
    outline-offset: 2px;
}
/* Smooth scrollbar for sidebar */
.sidebar-scroll::-webkit-scrollbar {
    width: 4px;
}
.sidebar-scroll::-webkit-scrollbar-thumb {
    background: #4b5563;
    border-radius: 2px;
}

/* Form field polish: consistent hover/focus/disabled states and a custom
   dropdown arrow so every <select> matches the rest of the design system,
   applied once here instead of per-page so all forms stay consistent. */
input[type="text"],
input[type="search"],
input[type="email"],
input[type="tel"],
input[type="url"],
input[type="number"],
input[type="password"],
input[type="date"],
input[type="datetime-local"],
input[type="time"],
select,
textarea {
    transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
input[type="text"]:hover:not(:disabled):not(:focus),
input[type="search"]:hover:not(:disabled):not(:focus),
input[type="email"]:hover:not(:disabled):not(:focus),
input[type="tel"]:hover:not(:disabled):not(:focus),
input[type="url"]:hover:not(:disabled):not(:focus),
input[type="number"]:hover:not(:disabled):not(:focus),
input[type="password"]:hover:not(:disabled):not(:focus),
input[type="date"]:hover:not(:disabled):not(:focus),
input[type="datetime-local"]:hover:not(:disabled):not(:focus),
input[type="time"]:hover:not(:disabled):not(:focus),
select:hover:not(:disabled):not(:focus),
textarea:hover:not(:disabled):not(:focus) {
    border-color: #9ca3af;
}
input:disabled, select:disabled, textarea:disabled {
    background-color: #f3f4f6;
    color: #9ca3af;
    cursor: not-allowed;
}
textarea {
    line-height: 1.5;
}
::placeholder {
    color: #9ca3af;
    opacity: 1;
}
/* Custom chevron for single-select dropdowns (native arrow looks inconsistent
   across browsers); multi-selects keep native rendering. */
select:not([multiple]) {
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    background-size: 1.1em;
    padding-right: 2.5rem !important;
}

/* -- Responsive table -> card list (mobile-first design system component) --
   Usage: add class="responsive-table" to a <table>, and data-label="Coluna"
   to every <td> that should show its column name as a label below 640px.
   Leave data-label off a <td> (e.g. an actions cell, or an empty-state
   colspan cell) to keep it rendering as a plain block instead of a
   label/value row. Add class="card-actions" to an actions <td> to get a
   separator line above it once it drops onto its own row. */
@media (max-width: 639px) {
    table.responsive-table thead { display: none; }
    /* tfoot entra na lista junto com tbody. Sem ele o grupo ficava em
       `display: table-footer-group` com filhos ja convertidos em bloco: o
       rodape encolhia para a largura do conteudo — 190px numa tabela de
       334px — e os rotulos saiam cortados a esquerda. Aparecia no detalhe do
       pacote e vale para as outras duas telas com tfoot (relatorio de
       desempenho de vendas e o recibo). */
    table.responsive-table,
    table.responsive-table tbody,
    table.responsive-table tfoot,
    table.responsive-table tr,
    table.responsive-table td {
        display: block !important;
        width: 100%;
    }
    /* O rodape nao e um cartao — e o fechamento da lista. Ganha so o recuo
       lateral dos cartoes para os rotulos nao ficarem colados na borda. */
    table.responsive-table tfoot td {
        padding-left: 1rem !important;
        padding-right: 1rem !important;
    }
    table.responsive-table tbody tr {
        margin-bottom: 0.75rem;
        border: 1px solid #e5e7eb;
        border-radius: 0.75rem;
        padding: 0.25rem 1rem;
        background: #fff;
    }
    table.responsive-table tbody tr:last-child { margin-bottom: 0; }
    table.responsive-table td {
        padding: 0.5rem 0 !important;
        border: none !important;
    }
    /* !important: a data-label cell is meant to surface in the card even if
       it carries a "hidden md:table-cell" utility for narrower real-table
       breakpoints (640-1023px) - there's no column-width constraint once
       rows become cards, so nothing should stay hidden here. */
    table.responsive-table td[data-label]:not([data-label=""]) {
        display: flex !important;
        align-items: center;
        justify-content: space-between;
        gap: 1rem;
        text-align: right;
    }
    table.responsive-table td[data-label]:not([data-label=""])::before {
        content: attr(data-label);
        font-size: 0.7rem;
        font-weight: 600;
        letter-spacing: 0.03em;
        text-transform: uppercase;
        color: #9ca3af;
        text-align: left;
        flex-shrink: 0;
    }
    table.responsive-table td.card-actions {
        border-top: 1px solid #f3f4f6 !important;
        margin-top: 0.25rem;
        padding-top: 0.75rem !important;
    }

    /* Piso tatil de 44px na celula de acoes.
       As acoes sao icones escritos como
           <a class="text-gray-500 p-1 -m-1"><i class="fas fa-eye"></i></a>
       O p-1 da 4px de folga e o -m-1 a retira de volta: a area de toque fica
       em ~24px. Sao 70 ocorrencias de py-1/py-1.5 espalhadas por 23 das 34
       telas que ja usam este componente — corrigir aqui evita edita-las uma a
       uma, e evita que a proxima tela nasca com o mesmo problema.
       A margem vai a zero de proposito: o -m-1 anularia o ganho. */
    table.responsive-table td.card-actions a,
    table.responsive-table td.card-actions button,
    table.responsive-table td.card-actions input[type="submit"] {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        margin: 0 !important;
    }

    /* Nada dentro do cartao pode se recusar a encolher.
       Com justify-content: space-between, um valor que nao quebra (e-mail
       longo, whitespace-nowrap) empurra a largura para fora da tela em vez de
       passar para a linha de baixo. */
    table.responsive-table td[data-label]:not([data-label=""]) {
        min-width: 0;
        overflow-wrap: anywhere;
        /* O valor costuma ser um NO DE TEXTO solto, nao um elemento. Dentro de
           um flex ele vira um item anonimo, e item anonimo nao aceita
           min-width: 0 — nao ha seletor que o alcance. Sem isto o e-mail longo
           era CORTADO no meio da palavra em vez de quebrar. Com wrap, quando
           nao cabe ao lado do rotulo ele desce para a linha de baixo inteiro. */
        flex-wrap: wrap;
    }
    table.responsive-table td .whitespace-nowrap,
    table.responsive-table td.whitespace-nowrap {
        white-space: normal !important;
    }

    /* truncate e max-w existem para segurar a LARGURA DA COLUNA. No modo
       cartao nao ha coluna: cada valor tem a linha inteira. Mantidos, eles
       cortam o texto sem motivo — o e-mail da cliente aparecia como
       "adrianacoimbra@gmail.c", com o resto perdido e sem nem o "...". Sao 55
       ocorrencias nas telas que usam o componente. */
    table.responsive-table td,
    table.responsive-table td * {
        max-width: none !important;
    }
    table.responsive-table td.truncate,
    table.responsive-table td .truncate {
        overflow: visible !important;
        text-overflow: clip !important;
        white-space: normal !important;
    }

    /* ── Anatomia do cartao (vinda do mockup aprovado) ──────────────────
       O modo cartao funcionava, mas rendia um cartao burocratico: TODA coluna
       virava uma linha "ROTULO ....... valor", inclusive o nome da cliente.
       Um registro ocupava ~640px e cabia 1,3 por tela.

       O mockup separa o que e IDENTIDADE do que e DADO: a primeira coluna vira
       o titulo do cartao, sem rotulo, porque "CLIENTE: Maria" e uma forma
       arrastada de escrever "Maria". O resto continua rotulo/valor. */
    table.responsive-table tbody tr {
        padding: 0.875rem 1rem;
    }
    table.responsive-table td {
        padding: 0.1875rem 0 !important;
    }
    table.responsive-table tbody td[data-label]:first-child {
        display: block !important;
        text-align: left !important;
        font-size: 1rem;
        font-weight: 600;
        color: #111827;
        padding-bottom: 0.5rem !important;
    }
    table.responsive-table tbody td[data-label]:first-child::before {
        content: none !important;
    }
    table.responsive-table td[data-label]:not([data-label=""]) {
        font-size: 0.875rem;
        color: #374151;
    }

    /* Acoes: botoes que dividem a largura, nao icones soltos.
       Tres icones nus encostados a esquerda nao se leem como acoes — leem-se
       como decoracao do ultimo dado. Com borda e largura dividida viram o que
       sao, e de quebra cada um passa a ocupar um terco da tela em vez de 24px. */
    table.responsive-table td.card-actions > div {
        display: flex;
        gap: 0.5rem;
        width: 100%;
    }
    table.responsive-table td.card-actions > div > a,
    table.responsive-table td.card-actions > div > button,
    table.responsive-table td.card-actions > div > form {
        flex: 1 1 0;
        min-width: 0;
    }
    table.responsive-table td.card-actions > div > a,
    table.responsive-table td.card-actions > div > button {
        border: 1px solid #d1d5db;
        border-radius: 0.5rem;
        background: #ffffff;
    }
    table.responsive-table td.card-actions > div > form > button {
        width: 100%;
        border: 1px solid #d1d5db;
        border-radius: 0.5rem;
        background: #ffffff;
        min-height: 44px;
    }

    /* O link que E o valor tambem e um alvo de toque.
       O piso de 44px acima cobre a celula de ACOES. Faltava o outro caso: o
       nome da cliente, o numero da venda, o codigo do lote — valores que
       linkam para o detalhe e sao, na pratica, a forma mais comum de abrir um
       registro. Mediam 24px de altura, a altura da linha de texto.
       A alternativa seria esticar a area de toque com um ::after invisivel,
       mantendo o tamanho visual. Ficaria pior: as linhas do cartao distam ~6px
       uma da outra, entao a area esticada invadiria a linha vizinha e o toque
       passaria a abrir o registro errado. Crescer de verdade e mais honesto. */
    table.responsive-table td[data-label] a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    /* ── Arrastar para revelar as acoes, como no Gmail ──────────────────
       Ativado por JS (base.html) em toda `tr` de `responsive-table` que
       tem um `td.card-actions` — nao precisa de marcacao nova em cada
       tela. Sao ~40 listas com o mesmo trio ver/editar/excluir sempre
       visivel numa faixa por baixo do cartao; o gesto some com a faixa
       sem editar uma tela de cada vez, e a proxima lista que usar
       `responsive-table` + `card-actions` ja nasce com o gesto de graca.

       O JS so faz DUAS coisas: acrescenta a classe `.swipe-pronto` na
       `tr` e move `td` (nao-acoes) com `style.transform` durante o
       arrasto — nada de recriar elementos, entao a marcacao que os
       testes conferem (href, aria-label, texto) continua identica.

       A `tr` vira o "quadro": `overflow:hidden` recorta a acoes pela
       mesma borda arredondada do cartao, e `position:relative` a torna a
       referencia de `.card-actions`, que passa a ficar ATRAS, encostada
       na borda direita (margens negativas cancelam o padding do cartao).
       As celulas de dado ganham fundo branco solido e `z-index:1`: em
       repouso elas cobrem as acoes por cima; arrastadas para a esquerda,
       descobrem o que esta atras. */
    table.responsive-table tr.swipe-pronto {
        position: relative;
        overflow: hidden;
    }
    table.responsive-table tr.swipe-pronto > td:not(.card-actions) {
        position: relative;
        z-index: 1;
        background: #fff;
    }
    table.responsive-table tr.swipe-pronto > td.card-actions {
        position: absolute;
        top: -0.25rem;
        bottom: -0.25rem;
        right: -1rem;
        z-index: 0;
        display: flex !important;
        align-items: center;
        justify-content: flex-end;
        gap: 0.25rem;
        padding: 0 0.75rem !important;
        margin: 0 !important;
        border-top: none !important;
        background: #f9fafb;
        width: auto;
    }

    /* Mesmo gesto, para as listas de cartao "2 linhas" (clientes, anamneses,
       procedimentos etc.) que NAO sao `responsive-table` — sao um `<a>` por
       item, redesenhadas fora do modo cartao da tabela porque cinco linhas
       rotuladas nao cabiam na tela. Essas telas perderam as acoes de
       editar/excluir do celular (so sobrou o toque para abrir o detalhe);
       `.swipe-linha` devolve as mesmas acoes do desktop, escondidas atras do
       cartao, com a MESMA logica de acima: `.swipe-acoes` fica atras,
       `:not(.swipe-acoes)` cobre por cima e desliza com `style.transform`. */
    .swipe-linha.swipe-pronto {
        position: relative;
        overflow: hidden;
        background: #f9fafb;
    }
    .swipe-linha.swipe-pronto > *:not(.swipe-acoes) {
        position: relative;
        z-index: 1;
        background: #fff;
    }
    .swipe-linha.swipe-pronto > .swipe-acoes {
        position: absolute;
        top: 0;
        bottom: 0;
        right: 0;
        z-index: 0;
        display: flex;
        align-items: center;
        justify-content: flex-end;
        gap: 1.25rem;
        padding: 0 1.125rem;
        width: auto;
    }

    /* ── Icones do gesto no padrao de "Agendamentos do Mes" ─────────────
       calendar.html (a lista que originou o gesto) usa blocos SOLIDOS de
       56px por acao — fundo azul para ver, ambar para editar, vermelho
       para excluir, icone branco — em vez de um icone colorido solto sobre
       fundo neutro. As ~44 listas que usam o componente global tinham cada
       uma sua propria cor de icone (herdada do design antigo, sem tile).

       Em vez de editar a marcacao de cada lista, a cor sai do proprio
       icone Font Awesome que a acao ja carrega (`:has()`) — nenhuma tela
       precisou de uma classe nova. Funciona tanto para `td.card-actions`
       (tabelas) quanto para `.swipe-acoes` (cartoes fora de tabela), e
       tanto para `<a>` quanto para `<button>`/`<form>` (exclusao via
       POST). */
    table.responsive-table tr.swipe-pronto > td.card-actions,
    .swipe-linha.swipe-pronto > .swipe-acoes {
        padding: 0 !important;
        gap: 0 !important;
        align-items: stretch;
    }
    /* `width:auto`: a regra de fora (~linha 373) da a este <div> `width:100%`
       para a faixa SEMPRE VISIVEL do modo cartao (botoes dividindo a
       largura da celula). Em modo swipe a celula-mae vira
       `position:absolute; width:auto` (encolhe pro conteudo) — um filho a
       100% de um pai sem largura definida e o classico caso ambiguo do CSS,
       e o navegador resolvia esticando o <div> por uma fatia enorme da
       linha. So aparecia feio quando havia UMA acao so (Agendar sozinho:
       a "faixa" virava uma barra roxa quase do tamanho do cartao) —
       com 2-3 icones cinza o exagero passava batido. */
    table.responsive-table tr.swipe-pronto > td.card-actions > div,
    .swipe-linha.swipe-pronto > .swipe-acoes > div {
        display: flex;
        align-items: stretch;
        gap: 0 !important;
        width: auto;
        height: 100%;
    }
    /* `flex: none`: a mesma regra de fora da cada `a`/`button` `flex:1 1 0`
       (flex-basis:0, cresce pra dividir o <div> em partes iguais) — com
       flex-basis diferente de `auto`, o `width:56px !important` abaixo e
       ignorado pelo algoritmo de flex (flex-basis manda no eixo principal),
       entao a acao continuava crescendo pra preencher o container ambiguo
       acima em vez de ficar nos 56px do "azulejo" solido. */
    table.responsive-table tr.swipe-pronto > td.card-actions a,
    table.responsive-table tr.swipe-pronto > td.card-actions button,
    table.responsive-table tr.swipe-pronto > td.card-actions form,
    .swipe-linha.swipe-pronto > .swipe-acoes a,
    .swipe-linha.swipe-pronto > .swipe-acoes button,
    .swipe-linha.swipe-pronto > .swipe-acoes form {
        flex: none;
        width: 56px !important;
        height: 100% !important;
        display: flex;
        align-items: center;
        justify-content: center;
        color: #fff !important;
        background: #64748b;
        padding: 0 !important;
        margin: 0 !important;
        border: none !important;
        font-size: 1rem;
    }
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-eye),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-eye),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-eye),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-eye) {
        background: #3b82f6;
    }
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-edit),
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-pen),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-edit),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-pen),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-edit),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-pen),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-edit),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-pen) {
        background: #f59e0b;
    }
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-trash),
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-trash-alt),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-trash),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-trash-alt),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-trash),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-trash-alt),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-trash),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-trash-alt) {
        background: #ef4444;
    }
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-file-pdf),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-file-pdf),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-file-pdf),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-file-pdf) {
        background: #6366f1;
    }
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-check),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-check),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-check),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-check) {
        background: #10b981;
    }
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-whatsapp),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-whatsapp),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-whatsapp),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-whatsapp) {
        background: #25d366;
    }
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-calendar-plus),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-calendar-plus),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-calendar-plus),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-calendar-plus) {
        background: #5b3fd6;
    }
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-crosshairs),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-crosshairs),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-crosshairs),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-crosshairs) {
        background: #0891b2;
    }
    /* Remarcar. Azul, e nao o ambar de cancelar: mover o horario nao e o
       caminho sem volta, e o azulejo nao deve sugerir que seja. */
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-calendar-day),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-calendar-day),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-calendar-day),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-calendar-day) {
        background: #3b82f6;
    }
    /* Registrar pagamento (lista de vendas). Verde de dinheiro entrando —
       sem regra, `fa-dollar-sign` caia no cinza padrao. */
    table.responsive-table tr.swipe-pronto > td.card-actions a:has(i.fa-dollar-sign),
    table.responsive-table tr.swipe-pronto > td.card-actions button:has(i.fa-dollar-sign),
    .swipe-linha.swipe-pronto > .swipe-acoes a:has(i.fa-dollar-sign),
    .swipe-linha.swipe-pronto > .swipe-acoes button:has(i.fa-dollar-sign) {
        background: #10b981;
    }
}

/* ── Icones de acao da tabela no desktop: um so estilo ──────────────────
   Fase 3.7 do PLANO-DESIGN.md.

   Ate aqui, acima de 640px, cada lista escrevia o proprio trio
   ver/editar/excluir e cada uma com o seu tom: `fa-eye` em `text-gray-500`,
   `fa-edit` em `text-blue-500`, `fa-trash` em `text-red-500`, com `p-1 -m-1`
   (ou `py-1`, ou `w-8 h-8`) de folga. Lado a lado pareciam tres rabiscos de
   cores diferentes colados no ultimo dado — 53 telas, nenhuma igual a outra.

   Aqui os icones da celula `.card-actions` viram botoes quadrados iguais,
   cinza em repouso; a cor so aparece no hover, e o vermelho fica reservado a
   exclusao. A celula e alcancada pela estrutura (`td.card-actions` + o `<i>`
   que a acao ja carrega), do mesmo jeito do gesto de arrastar acima — sem
   tocar nas telas.

   Fora do alcance de proposito: controles com TEXTO visivel ("Agendar",
   "Copiar", "Ver"), marcados com `.acao-texto` na tela. E o mobile: o bloco
   inteiro e `min-width: 640px`, disjunto das regras de cartao/arrasto. */
@media (min-width: 640px) {
    /* Lista de atendimentos e o padrao: cabecalho em caixa-alta, linha
       hoveravel, primeiro dado (o nome) em destaque. As telas ja usam
       `responsive-table`; isto so alinha o que o Tailwind deixava solto. */
    table.responsive-table thead {
        background: var(--ds-fundo);
    }
    table.responsive-table thead th {
        padding: 12px 16px;
        text-align: left;
        font-size: 11px;
        font-weight: 600;
        letter-spacing: .04em;
        text-transform: uppercase;
        color: var(--ds-apoio);
        font-family: var(--ab-fonte-dados);
        white-space: nowrap;
    }
    table.responsive-table tbody td {
        padding: 16px;
        vertical-align: middle;
        color: var(--ds-tinta-fraca);
    }
    table.responsive-table tbody tr:hover {
        background: var(--ds-fundo);
    }

    table.responsive-table td.card-actions > div {
        display: flex;
        align-items: center;
        gap: 4px;
    }
    table.responsive-table td.card-actions a:has(> i):not(.acao-texto),
    table.responsive-table td.card-actions button:has(> i):not(.acao-texto) {
        width: 34px;
        height: 34px;
        padding: 0;
        margin: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border: 0;
        border-radius: var(--ds-raio-campo);
        background: transparent;
        color: var(--ds-apoio);
        cursor: pointer;
        transition: background-color .12s ease, color .12s ease;
    }
    table.responsive-table td.card-actions a:has(> i):not(.acao-texto) > i,
    table.responsive-table td.card-actions button:has(> i):not(.acao-texto) > i {
        font-size: 14px;
        margin: 0;
    }
    table.responsive-table td.card-actions a:has(> i):not(.acao-texto):hover,
    table.responsive-table td.card-actions button:has(> i):not(.acao-texto):hover {
        background: var(--ds-neutro-fundo);
        color: var(--ds-tinta);
    }
    table.responsive-table td.card-actions a:has(> i):not(.acao-texto):focus-visible,
    table.responsive-table td.card-actions button:has(> i):not(.acao-texto):focus-visible {
        outline: 2px solid var(--ds-primaria);
        outline-offset: 1px;
    }
    /* Estado inerte (ex.: superusuario nao pode ser excluido) — <span> no
       lugar do <a>: mesmo tamanho, sem hover, tom apagado. */
    table.responsive-table td.card-actions > div > span:has(> i) {
        width: 34px;
        height: 34px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        color: var(--ds-borda-campo);
    }
    table.responsive-table td.card-actions > div > span:has(> i) > i { font-size: 14px; }
}

/* Exclusao: unica acao em vermelho, e so no hover. Bloco :has() a parte —
   um navegador sem :has() invalida o seletor e descartaria a REGRA INTEIRA;
   isolado, o pior caso e o icone de lixeira seguir cinza tambem no hover. */
@media (min-width: 640px) {
    table.responsive-table td.card-actions a:not(.acao-texto):hover:has(> i.fa-trash),
    table.responsive-table td.card-actions a:not(.acao-texto):hover:has(> i.fa-trash-alt),
    table.responsive-table td.card-actions a:not(.acao-texto):hover:has(> i.fa-trash-can),
    table.responsive-table td.card-actions button:not(.acao-texto):hover:has(> i.fa-trash),
    table.responsive-table td.card-actions button:not(.acao-texto):hover:has(> i.fa-trash-alt),
    table.responsive-table td.card-actions button:not(.acao-texto):hover:has(> i.fa-trash-can) {
        background: var(--ds-erro-fundo);
        color: var(--ds-erro-texto);
    }
}

/* ── Acoes fixas no rodape (celular) ────────────────────────────────────
   A acao principal de uma tela — Salvar, Criar, Confirmar — ficava no fim de
   um formulario longo. No celular isso significa rolar ate o fim para agir, e
   rolar de volta para conferir. Abaixo de 640px a barra de acoes gruda no
   rodape e fica sempre ao alcance do polegar.

   Aplicacao: `class="acoes-fixas"` no container que ja agrupa os botoes. Ele
   continua sendo um bloco normal no desktop — nada muda a partir de sm.

   `env(safe-area-inset-bottom)` mantem a barra acima da faixa do iPhone; sem
   isso o botao fica por baixo do indicador de home e o toque nao chega nele.

   O espaco no fim do conteudo e dado pelo JS logo abaixo, e nao aqui: a altura
   da barra varia com o numero de botoes e com a quebra de linha do rotulo, e
   um valor fixo em CSS ora sobraria ora deixaria a barra cobrindo o ultimo
   campo do formulario. */
@media (max-width: 639px) {
    .acoes-fixas {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 40;
        display: flex;
        /* !important porque varias dessas linhas ja trazem `flex-col
           sm:flex-row`: no celular elas empilhavam os botoes, e a barra fixa
           virava um bloco de 157px comendo um quinto da tela. Mesma
           especificidade que a utilitaria do Tailwind, e a ordem de injecao do
           CDN nao e nossa para garantir. */
        flex-direction: row !important;
        flex-wrap: nowrap;
        align-items: center;
        gap: 0.5rem;
        margin: 0;
        padding: 0.75rem 1rem;
        padding-bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
        background: #ffffff;
        border-top: 1px solid #e5e7eb;
        box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.07);
        /* O JS em base.html ancora `bottom` no visualViewport: no Chrome e
           no Safari o teclado cobre `bottom: 0` do layout viewport, e a
           barra some no cadastro (campos com foco) embora a lista continue
           mostrando. */
    }
    /* Cada botao divide a largura e respeita o piso tatil. */
    .acoes-fixas > a,
    .acoes-fixas > button,
    .acoes-fixas > input[type="submit"],
    .acoes-fixas > span[aria-disabled] {
        flex: 1 1 0;
        min-width: 0;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        white-space: nowrap;
        margin: 0;
    }
    /* A acao principal pesa mais que "Cancelar". */
    .acoes-fixas > .acao-primaria,
    .acoes-fixas > button[type="submit"] {
        flex: 2 1 0;
    }
    /* Indicador de envio nao e acao: ocupava uma coluna inteira da barra com
       o texto "Enviando...", mesmo parado. O proprio botao ja sinaliza o
       envio em curso. */
    .acoes-fixas > .htmx-indicator {
        display: none;
    }
    /* Paginacao servida como barra fixa (param `fixa` de componentes/paginacao):
       herda o motor de .acoes-fixas (base.html move para o body e mede o
       espaco no fim do conteudo), mas "Anterior | Pagina X de Y | Proxima"
       fica espacado — nao esticado como dois botoes de formulario. O
       `justify-content: space-between` vem de .ds-paginacao (regra posterior
       neste arquivo); aqui so o que a barra fixa reintroduz. */
    .ds-paginacao.acoes-fixas {
        margin-top: 0;
    }
    .ds-paginacao.acoes-fixas > .ds-paginacao__posicao {
        flex: 0 0 auto;
        font-size: 12px;
    }
}

/* ── Correcoes que dependem de :has() ───────────────────────────────────
   EM BLOCO SEPARADO, e isso nao e organizacao: um navegador sem suporte a
   :has() considera o seletor INVALIDO e descarta a REGRA INTEIRA. Se estas
   estivessem na mesma lista das regras acima, elas cairiam juntas — um
   navegador antigo perderia ate o que hoje ja funciona. Isoladas, o pior caso
   e ficar exatamente como era antes.
   Suporte: Safari 15.4+, Chrome 105+, Firefox 121+. */
@media (max-width: 639px) {
    /* O cartao nascia DENTRO de um rolador lateral.
       Em 27 das 34 telas, <div class="overflow-x-auto"> e o pai direto da
       tabela (ver clients/client_list.html e finance/sale_list.html). A linha
       vira cartao, mas o pai continua podendo rolar de lado — entao qualquer
       filho que estoure rola em vez de quebrar linha. E a razao de as telas
       "responsivas" continuarem quebrando no celular. */
    .overflow-x-auto:has(> table.responsive-table) {
        overflow-x: visible;
    }

    /* Separador da linha de acoes em quem nunca ganhou .card-actions (18 das
       34 telas). A celula de acoes e reconhecivel sem ambiguidade: e a ultima
       da linha, contem link ou botao, e NAO tem data-label — a ausencia do
       rotulo e o que a distingue de uma celula de dado, conforme documentado
       no bloco acima. */
    table.responsive-table tbody td:last-child:not([data-label]):has(a, button) {
        border-top: 1px solid #f3f4f6 !important;
        margin-top: 0.25rem;
        padding-top: 0.75rem !important;
    }
    table.responsive-table tbody td:last-child:not([data-label]):has(a, button) a,
    table.responsive-table tbody td:last-child:not([data-label]):has(a, button) button,
    table.responsive-table tbody td:last-child:not([data-label]):has(a, button) input[type="submit"] {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        margin: 0 !important;
    }
}

/* ── Full-screen-on-mobile modal (design system component) ──────────────
   Usage: add class="modal-panel" to a modal's inner panel (the rounded
   white card, e.g. the div with rounded-2xl/max-w-*). Below 640px it
   expands edge-to-edge and becomes a flex column so a scrollable body
   (class="modal-body") can sit between a fixed header/footer. Reserved for
   content-heavy modals (forms, previews, option pickers) where a small
   centered dialog would clip content or force nested scrolling. Short
   confirm/delete dialogs are intentionally left as compact centered cards —
   see the audit notes for why. */
@media (max-width: 639px) {
    .modal-panel {
        width: 100% !important;
        max-width: 100% !important;
        height: 100%;
        max-height: 100vh !important;
        max-height: 100dvh !important;
        margin: 0 !important;
        border-radius: 0 !important;
        display: flex;
        flex-direction: column;
    }
    .modal-panel .modal-body {
        overflow-y: auto;
        flex: 1 1 auto;
    }

    /* ── Modal centrado nunca fica inalcancavel ─────────────────────────
       Vale para o cartao centrado "curto" que o bloco acima deliberadamente
       NAO transforma em tela cheia. Curto e uma suposicao sobre o conteudo,
       nao uma garantia: com o teclado aberto num aparelho baixo (iPhone SE,
       667px) a area util cai pela metade, e `align-items: center` num
       container sem rolagem corta o cartao em CIMA e embaixo ao mesmo tempo.
       O topo cortado nao volta com scroll nenhum — e o modo de falha
       classico do flex centrado — entao o botao "Salvar" fica inalcancavel.

       Sao 9 telas hoje com exatamente esta anatomia (calendar, sale_form,
       appointment_detail, procedure_form, room_settings, settings,
       template_list, reminder_approval e os dois dialogos globais deste
       arquivo), todas escritas na mao com o mesmo trio de utilitarias. A
       regra aqui pega as nove e as proximas, sem edita-las uma a uma.

       `margin: auto` no cartao, e nao `align-items: center`, e o que
       preserva a aparencia: sobrando espaco ele continua centrado; faltando,
       as margens colapsam e o container rola em vez de cortar. O cartao
       continua compacto — nada aqui o transforma em tela cheia. */
    .fixed.inset-0.items-center.justify-center {
        align-items: flex-start;
        overflow-y: auto;
        padding-top: 16px;
        padding-bottom: 16px;
    }
    .fixed.inset-0.items-center.justify-center > * {
        margin-top: auto;
        margin-bottom: auto;
    }
}

/* =========================================================================
   COMPONENTES
   =========================================================================
   Estas classes existem para acabar com a sopa de utilitarias: hoje o mesmo
   botao primario aparece escrito de 12 jeitos diferentes em ~115 lugares.
   Use os includes em templates/componentes/ — nao copie as classes na mao.
   ========================================================================= */

/* --- botao (Fase 2.2) ---------------------------------------------------- */
.ds-botao {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: var(--ds-toque);       /* D2: 44px e o piso */
    padding: 0 16px;
    border: 1px solid transparent;
    border-radius: var(--ds-raio-campo);
    font-size: 14px;
    font-weight: 500;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    touch-action: manipulation;
    transition: transform var(--ds-dur) var(--ds-ease), opacity var(--ds-dur) var(--ds-ease);
}
.ds-botao:hover { text-decoration: none; }
.ds-botao:focus-visible { transform: translateY(-1px); }
.ds-botao:active:not([disabled]):not([aria-disabled="true"]) {
    transform: translateY(1px);
    opacity: .92;
}
.ds-botao.is-loading,
.ds-botao[aria-busy="true"] {
    pointer-events: none;
    opacity: .7;
}

/* Estado de espera do botao: a lotus entra e o rotulo troca ("Criar minha
   clinica" -> "Enviando seu e-mail..."). Opt-in pelo parametro `carregando`
   de componentes/botao.html; sem ele nada muda.

   Existe porque o POST do cadastro publico chama a API do Resend, e ate a
   proxima tela pintar so o indicador do navegador se move — imperceptivel no
   celular. Sem sinal na tela a pessoa clica de novo.

   `color: currentColor` porque a lotus e pintada em `--ds-primaria` por
   padrao, e dentro de um botao primario (fundo na cor da marca) ela sairia
   invisivel. */
.ds-botao__carregando { display: none; }
.ds-botao__carregando .ds-carregando__marca { color: currentColor; }
.ds-botao[aria-busy="true"] .ds-botao__carregando { display: inline-flex; }
.ds-botao[aria-busy="true"] > i { display: none; }

.ds-botao[disabled],
.ds-botao[aria-disabled="true"] {
    opacity: .5;
    cursor: not-allowed;
    pointer-events: none;
}

.ds-botao--primaria {
    background: var(--ds-primaria);
    color: #ffffff;
}
/* Identidade D: hover -> "indigo profundo". color-mix escurece a primaria da
   clinica (white-label) sem precisar de um segundo token por tenant. */
.ds-botao--primaria:hover {
    background: color-mix(in srgb, var(--ds-primaria) 82%, #000);
    color: #ffffff;
    opacity: 1;
}
@supports not (color: color-mix(in srgb, red, blue)) {
    .ds-botao--primaria:hover { opacity: .9; }
}

.ds-botao--secundaria {
    background: var(--ds-superficie);
    color: var(--ds-tinta-fraca);
    border-color: var(--ds-borda-campo);
}
.ds-botao--secundaria:hover { background: var(--ds-fundo); color: var(--ds-tinta-fraca); }

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

/* ── Acao do WhatsApp: verde claro da marca ────────────────────────────────
   Toda acao que dispara uma mensagem — "enviar lembrete", "confirmar", "abrir
   no WhatsApp", "cobrar" — sai do cinza neutro para o verde claro do WhatsApp,
   com o icone no verde da marca.

   Alcance duplo, para nao editar 30 telas:
     1. `componentes/botao.html` com `tipo="whatsapp"` -> `.ds-botao--whatsapp`.
     2. Qualquer <a>/<button> DENTRO DO <main> que ja carregue o icone
        `fa-whatsapp` como filho direto. O `main` deixa de fora o item
        "WhatsApp" da navegacao lateral (que tambem tem esse icone).

   `:has()` ja e usado neste arquivo (Fase 3.7) e o piso de navegador do
   projeto o cobre. Especificidade (0,1,3) para o seletor com `main` — vence a
   utilitaria do Tailwind sem precisar de `!important`. */
.ds-botao--whatsapp,
main a:has(> i.fa-whatsapp),
main button:has(> i.fa-whatsapp),
main a[href*="wa.me"] {
    background-color: var(--ds-wa-fundo);
    border: 1px solid var(--ds-wa-borda);
    color: var(--ds-wa-tinta);
}
.ds-botao--whatsapp:hover,
main a:has(> i.fa-whatsapp):hover,
main button:has(> i.fa-whatsapp):hover,
main a[href*="wa.me"]:hover {
    background-color: var(--ds-wa-fundo-hover);
    color: var(--ds-wa-tinta);
    opacity: 1;
}
.ds-botao--whatsapp > i,
main a:has(> i.fa-whatsapp) > i.fa-whatsapp,
main button:has(> i.fa-whatsapp) > i.fa-whatsapp,
main a[href*="wa.me"] > i {
    color: var(--ds-wa-marca);
}

/* So icone: quadrado de 44px, sem padding lateral. O rotulo vai em
   aria-label — um botao sem texto acessivel e invisivel para leitor de tela. */
.ds-botao--icone {
    width: var(--ds-toque);
    padding: 0;
    gap: 0;
}

/* Ocupa a linha inteira. No celular e o normal; no desktop, raro. */
.ds-botao--bloco { width: 100%; }

/* Atalho para botoes ad-hoc que ainda nao passaram pelo include:
   mesma altura, raio e tipografia do design system, sem mudar a cor
   de cada tela. */
button.bg-primary,
a.bg-primary {
    min-height: var(--ds-toque);
    border-radius: var(--ds-raio-campo);
    font-weight: 500;
    line-height: 1.2;
    text-align: center;
}
/* O min-height acima estica o retangulo; sem flex o texto do <a>
   fica no topo — visivel no desktop, onde .acoes-fixas nao centra.
   `flex-col` fica de fora: chips de data e o QR code empilham rotulo.
   `.hidden` fica de fora: um botao que a tela escondeu de proposito
   (padrao `hidden sm:inline-flex`, a acao que no celular vive na barra
   fixa) reaparecia porque este `display` vence o `.hidden` do Tailwind —
   e a tela mostrava a acao duas vezes. */
button.bg-primary:not(.flex-col):not(.hidden),
a.bg-primary:not(.flex-col):not(.hidden) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
button.bg-primary:active,
a.bg-primary:active {
    opacity: .9;
}

/* Chip de filtro: a UNICA coisa que pode ficar abaixo de 44px (D2), porque
   vem em fila rolavel com folga entre os alvos. */
.ds-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: var(--ds-toque-chip);
    padding: 0 14px;
    border: 1px solid var(--ds-borda-campo);
    border-radius: 999px;
    background: var(--ds-superficie);
    color: var(--ds-tinta-fraca);
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
}
.ds-chip--ativo {
    border-color: var(--ds-primaria);
    background: var(--ds-info-fundo);
    color: var(--ds-info-texto);
}

/* --- campo de formulario (Fase 2.3) -------------------------------------- */
/* Ordem obrigatoria dentro do grupo (D12): rotulo -> campo -> ajuda -> erro.
   A ajuda explica como preencher; o erro diz o que deu errado. */
.ds-campo-grupo {
    display: flex;
    flex-direction: column;
    gap: var(--ds-folga-campo);
}

.ds-rotulo {
    font-size: var(--ds-rotulo);
    font-weight: 500;
    color: var(--ds-tinta-fraca);
}

/* D10: marca-se o obrigatorio, nunca o opcional. O asterisco e decorativo —
   quem carrega o significado para o leitor de tela e o aria-required no campo
   mais o texto escondido ao lado. */
.ds-obrigatorio { color: var(--ds-erro-borda); }

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

/* D9: --ds-campo vale 16px no celular. Abaixo disso o Safari do iPhone da
   zoom sozinho ao focar o campo, e o zoom quebra a regra de nao rolar de lado
   (D3) no exato momento de digitar. No desktop o token volta a 14px. */
.ds-campo {
    width: 100%;
    min-height: var(--ds-toque);
    padding: 10px 12px;
    border: 1px solid var(--ds-borda-campo);
    border-radius: var(--ds-raio-campo);
    font-size: var(--ds-campo);
    font-family: inherit;
    line-height: 1.4;
    color: var(--ds-tinta);
    background-color: var(--ds-superficie);
}

.ds-campo--erro { border-color: var(--ds-erro-borda); }

textarea.ds-campo {
    min-height: 88px;
    resize: vertical;
}

/* --- campo de arquivo ------------------------------------------------------
   O botao "Escolher arquivo" e desenhado pelo navegador, e cada um desenha o
   seu: cinza do Chrome, azul do Firefox, arredondado do Safari. So o
   pseudo-elemento ::file-selector-button alcanca ele. Aqui ele vira o mesmo
   botao secundario do design system.

   Regra deliberada: NADA e aplicado a caixa do input. `input[type="file"]`
   tem especificidade maior que uma classe utilitaria do Tailwind, entao
   mexer em padding ou borda aqui sobrescreveria silenciosamente o que cada
   tela ja declarou. */
input[type="file"] {
    cursor: pointer;
    max-width: 100%;
    color: var(--ds-tinta-fraca);
    font-family: inherit;
    font-size: var(--ds-rotulo);
}

input[type="file"]::file-selector-button {
    -webkit-appearance: none;
    appearance: none;
    display: inline-flex;
    align-items: center;
    margin: 0 12px 0 0;
    padding: 0 14px;
    min-height: var(--ds-toque-chip);  /* dentro de um campo de 44px, 36px cabe */
    border: 1px solid var(--ds-borda-campo);
    border-radius: var(--ds-raio-campo);
    background: var(--ds-superficie);
    color: var(--ds-tinta-fraca);
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    line-height: 1.2;
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease;
}

input[type="file"]::file-selector-button:hover {
    background: var(--ds-fundo);
    border-color: var(--ds-fraco);
}

input[type="file"]:focus-visible::file-selector-button {
    outline: 2px solid var(--ds-primaria);
    outline-offset: 1px;
}

/* Com a classe do design system a caixa e nossa, entao aqui podemos ajustar:
   4px de folga + os 36px do botao fecham exatamente os 44px de alvo de toque
   (D2), sem empilhar o padding de campo de texto por cima do botao. */
input[type="file"].ds-campo {
    display: flex;
    align-items: center;
    padding: 4px 6px;
}

input[type="file"][disabled] {
    cursor: not-allowed;
    opacity: .6;
}
input[type="file"][disabled]::file-selector-button {
    cursor: not-allowed;
    pointer-events: none;
}

/* Safari antigo e Chrome antigo ainda usam o prefixo -webkit-. Nao da para
   juntar com o seletor padrao: um seletor desconhecido invalida a regra
   inteira, e o bloco acima se perderia nos navegadores modernos. */
input[type="file"]::-webkit-file-upload-button {
    -webkit-appearance: none;
    appearance: none;
    margin: 0 12px 0 0;
    padding: 0 14px;
    min-height: var(--ds-toque-chip);
    border: 1px solid var(--ds-borda-campo);
    border-radius: var(--ds-raio-campo);
    background: var(--ds-superficie);
    color: var(--ds-tinta-fraca);
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    line-height: 1.2;
    cursor: pointer;
}

/* O input escondido atras de um <label> proprio (logo da clinica, fotos do
   HOF) nao deve ganhar cursor nem altura de nada — ele nao aparece. */
input[type="file"].sr-only,
input[type="file"].hidden {
    cursor: auto;
}

/* --- campo de arquivo: zona de arrastar-e-soltar --------------------------
   O ::file-selector-button acima resolve o botao nativo, mas continua sendo
   uma LINHA — "Escolher arquivo · nenhum arquivo selecionado". Para imagem
   (logo da clinica, foto do HOF, anexo de chamado) o alvo precisa ser maior
   e aceitar arrastar, porque e assim que a maioria chega ao campo. As duas
   formas coexistem de proposito: esta e para `componentes/campo.html`
   (tipo="file") e para os poucos campos de imagem fora dele; a de cima
   continua valendo para quem usa `<input type="file">` cru.

   `x-data` local (sem estado global) — cada campo desta tela e independente,
   entao duas zonas na mesma pagina nao disputam o mesmo `arrastando`. */
.ds-campo-arquivo__zona {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    width: 100%;
    padding: 28px 16px;
    border: 2px dashed var(--ds-borda-campo);
    border-radius: var(--ds-raio-cartao, 12px);
    background: var(--ds-fundo);
    text-align: center;
    cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}
.ds-campo-arquivo__zona:hover,
.ds-campo-arquivo__zona:focus-within {
    border-color: var(--ds-primaria);
}
/* Ativa durante o arrasto (`@dragover`) — mesmo tom da faixa "hoje" do
   calendario, para reaproveitar uma associacao que a tela ja ensinou. */
.ds-campo-arquivo__zona--ativa {
    border-color: var(--ds-primaria);
    background: color-mix(in srgb, var(--ds-primaria) 6%, var(--ds-superficie));
}
.ds-campo-arquivo--erro .ds-campo-arquivo__zona {
    border-color: var(--ds-erro-borda);
}
.ds-campo-arquivo__zona > i {
    font-size: 28px;
    color: var(--ds-primaria);
    margin-bottom: 4px;
}
.ds-campo-arquivo__texto {
    font-size: var(--ds-campo);
    font-weight: 600;
    color: var(--ds-tinta);
}
.ds-campo-arquivo__ou {
    font-size: var(--ds-ajuda);
    color: var(--ds-apoio);
    margin: 2px 0;
}
/* O botao "Procurar arquivo" e DECORATIVO: `pointer-events: none` faz o
   clique atravessar ate a zona, que e quem abre o seletor. Um segundo
   `onclick` no botao abriria o dialogo duas vezes em navegadores que
   despacham os dois eventos (o do botao e o do pai, por bubbling); um so
   listener, na zona inteira, evita essa duplicacao sem precisar de
   `stopPropagation`. `tabindex="-1"` tira ele do Tab — quem navega por
   teclado alcanca o `<input>` de verdade, que continua focavel via `sr-only`. */
.ds-campo-arquivo__zona .ds-botao {
    margin-top: 6px;
    pointer-events: none;
}
.ds-campo-arquivo__nome {
    margin-top: 10px;
    font-size: var(--ds-ajuda);
    color: var(--ds-tinta-fraca);
    font-weight: 500;
    word-break: break-all;
}

.ds-ajuda {
    font-size: var(--ds-ajuda);
    color: var(--ds-apoio);
}

.ds-erro {
    display: flex;
    align-items: flex-start;
    gap: 5px;
    font-size: var(--ds-ajuda);
    color: var(--ds-erro-msg);
}
.ds-erro > i { margin-top: .15em; flex-shrink: 0; }

/* D11: erro em dois lugares. Este e o resumo no topo do formulario, para quem
   enviou e nao sabe onde olhar. Ele NOMEIA os campos — "corrija os erros
   abaixo" nao ajuda ninguem num formulario de 14 campos. */
.ds-resumo-erro {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    padding: 11px 13px;
    border: 1px solid var(--ds-erro-aviso-borda);
    border-radius: var(--ds-raio-chip);
    background: var(--ds-erro-aviso);
}
.ds-resumo-erro__titulo {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--ds-erro-texto);
}
.ds-resumo-erro__lista {
    margin: 3px 0 0;
    padding: 0;
    list-style: none;
    font-size: var(--ds-ajuda);
    color: var(--ds-erro-msg);
}

/* Secao de formulario: uma coluna no celular, duas no desktop. Nunca tres. */
.ds-secao-titulo {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--ds-rotulo-tabela);
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--ds-fraco);
}
.ds-secao-titulo::after {
    content: "";
    flex-grow: 1;
    height: 1px;
    background: var(--ds-borda);
}

.ds-grade {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--ds-folga-entre);
}
@media (min-width: 768px) {
    .ds-grade { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .ds-grade > .ds-campo-grupo--largo { grid-column: 1 / -1; }
}

/* --- situacao (Fase 2.4) -------------------------------------------------- */
/* D4: NUNCA so a cor. O rotulo carrega o significado, para quem nao distingue
   as cores. Hoje o sistema tem 10+ pares de cor inconsistentes para as mesmas
   quatro situacoes (green-100/700 vs green-100/800, gray-100/600 vs /700...). */
.ds-situacao {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    flex-shrink: 0;              /* nunca encolhe: quem trunca e o nome ao lado */
    padding: 3px 9px;
    border-radius: var(--ds-raio-chip);
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}
.ds-situacao--ok      { background: var(--ds-ok-fundo);      color: var(--ds-ok-texto); }
.ds-situacao--atencao { background: var(--ds-atencao-fundo); color: var(--ds-atencao-texto); }
.ds-situacao--erro    { background: var(--ds-erro-fundo);    color: var(--ds-erro-texto); }
.ds-situacao--neutro  { background: var(--ds-neutro-fundo);  color: var(--ds-neutro-texto); }
.ds-situacao--info    { background: var(--ds-info-fundo);    color: var(--ds-info-texto); }

/* --- estado vazio (Fase 2.5) ---------------------------------------------- */
/* Diz o que FAZER, nao so que esta vazio. Sao 154 telas com um "Nenhum..."
   improvisado, cada uma com sua diagramacao. */
.ds-vazio {
    padding: 24px 16px;
    border: 1px dashed var(--ds-borda-campo);
    border-radius: var(--ds-raio-cartao);
    background: var(--ds-superficie);
    text-align: center;
}
.ds-vazio__icone {
    width: var(--ds-toque);
    height: var(--ds-toque);
    margin: 0 auto 10px;
    border-radius: 50%;
    background: var(--ds-info-fundo);
    color: var(--ds-info-texto);
    display: flex;
    align-items: center;
    justify-content: center;
}
.ds-vazio__titulo { font-size: 15px; font-weight: 600; color: var(--ds-tinta); }
.ds-vazio__texto  { margin-top: 4px; font-size: 13px; color: var(--ds-apoio); }
.ds-vazio__acao   { margin-top: 14px; display: inline-flex; }

/* --- cabecalho de pagina (Fase 2.6) --------------------------------------- */
.ds-cabecalho {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 24px;
}
.ds-cabecalho__texto { flex-grow: 1; min-width: 0; }
.ds-cabecalho__titulo {
    font-size: 20px;
    font-weight: 600;
    color: var(--ds-tinta);
    text-wrap: pretty;
}
.ds-cabecalho__linha { margin-top: 2px; font-size: 13.5px; color: var(--ds-apoio); }
.ds-cabecalho__acoes { display: flex; gap: 8px; flex-shrink: 0; }
/* No celular a acao primaria ocupa a linha inteira em vez de espremer o titulo. */
@media (max-width: 639px) {
    .ds-cabecalho__acoes { width: 100%; }
    .ds-cabecalho__acoes > * { flex-grow: 1; }
}

/* --- paginacao (Fase 2.7) ------------------------------------------------- */
/* Alvos de 44px (D2). Nada de setas minusculas. */
.ds-paginacao {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 16px;
}
.ds-paginacao__posicao {
    font-size: 13px;
    color: var(--ds-apoio);
    text-align: center;
    font-variant-numeric: tabular-nums;
}
/* Ocupa o lugar do botao ausente na primeira e na ultima pagina, para que o
   "Pagina 2 de 4" nao pule de lado ao navegar. */
.ds-paginacao__espaco { min-width: 44px; }

/* --- carregando (loader animado) -----------------------------------------
   A lotus da marca (mesmas 6 petalas de componentes/logo.html) em loop, no
   lugar do generico `fa-spinner fa-spin`. Cada petala acende em sequencia
   enquanto o simbolo gira devagar e o ponto central pulsa — a mesma
   coreografia do loader desenhado para a marca (`ab-chase`/`ab-spin`/
   `ab-pulse`), so que com nomes proprios para nao colidir com outro CSS.

   `rotate` aqui e a propriedade CSS independente (nao `transform: rotate`),
   de proposito: assim cada `<path>` mantem seu angulo fixo e a keyframe so
   precisa animar `transform: scale()` — as duas nao se atropelam. Suporte:
   Safari 15.4+, Chrome 104+, Firefox 121+ (mesmo piso de D13, ja assumido
   neste arquivo).

   Use o include, nao estas classes na mao: componentes/carregando.html. */
@keyframes ds-carregando-girar { to { transform: rotate(360deg); } }
@keyframes ds-carregando-petala {
    0%, 70%, 100% { opacity: .16; transform: scale(.9); }
    12% { opacity: 1; transform: scale(1); }
    34% { opacity: .4; transform: scale(.96); }
}
@keyframes ds-carregando-pulso {
    0%, 100% { transform: scale(.72); opacity: .5; }
    50% { transform: scale(1); opacity: 1; }
}
.ds-carregando {
    display: inline-flex;
    align-items: center;
    gap: 10px;
}
/* `--bloco` preenche a area e centra o simbolo nos dois eixos. Sem o
   `display: flex` (a base e `inline-flex`) e sem altura, `justify-content`
   e `text-align` nao tinham o que centrar e a lotus encostava no topo/esquerda
   da area — era o "nao esta no meio da tela". `min-height` da o espaco
   vertical para o `justify-content: center` ter efeito; `flex: 1` deixa a
   area crescer quando o pai e um flex container com altura. */
.ds-carregando--bloco {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    min-height: 240px;
    padding: 28px 16px;
    text-align: center;
}
.ds-carregando__marca {
    flex-shrink: 0;
    overflow: visible;
    color: var(--ds-primaria);
    animation: ds-carregando-girar 6s linear infinite;
}
.ds-carregando--claro .ds-carregando__marca { color: #ffffff; }
.ds-carregando__marca circle {
    transform-origin: 100px 100px;
    animation: ds-carregando-pulso 1.5s ease-in-out infinite;
}
.ds-carregando__marca path {
    transform-origin: 100px 100px;
    animation: ds-carregando-petala 1.5s ease-in-out infinite;
}
.ds-carregando__marca path:nth-of-type(1) { rotate: 0deg; animation-delay: 0s; }
.ds-carregando__marca path:nth-of-type(2) { rotate: 60deg; animation-delay: .25s; }
.ds-carregando__marca path:nth-of-type(3) { rotate: 120deg; animation-delay: .5s; }
.ds-carregando__marca path:nth-of-type(4) { rotate: 180deg; animation-delay: .75s; }
.ds-carregando__marca path:nth-of-type(5) { rotate: 240deg; animation-delay: 1s; }
.ds-carregando__marca path:nth-of-type(6) { rotate: 300deg; animation-delay: 1.25s; }
.ds-carregando__texto {
    font-size: var(--ds-ajuda);
    color: var(--ds-apoio);
}
.ds-carregando--claro .ds-carregando__texto { color: rgba(255, 255, 255, .82); }
@media (prefers-reduced-motion: reduce) {
    .ds-carregando__marca,
    .ds-carregando__marca circle,
    .ds-carregando__marca path {
        animation: none;
    }
}

/* --- esqueleto do formulario (Fase 3.3) ---------------------------------- */
.ds-formulario {
    display: flex;
    flex-direction: column;
    gap: 28px;
}
.ds-formulario__secao {
    display: flex;
    flex-direction: column;
    gap: 16px;
}
/* Rodape de acoes. No celular a classe `acoes-fixas` (ja existente) prende a
   barra no fim da tela; aqui fica so o alinhamento do desktop. */
.ds-formulario__acoes {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding-top: 4px;
}

/* A utilitaria `hidden` do Tailwind vem de um <style> injetado em tempo de
   execucao pelo CDN, entao ela normalmente ganha de .ds-campo-grupo por ordem
   de fonte. "Normalmente" nao serve para esconder campo: esta regra torna o
   resultado independente da ordem de carga. */
.ds-campo-grupo.hidden { display: none; }


/* --- secao sanfonada de formulario longo --------------------------------
   Medido em 393px (Pixel 5), antes desta regra existir:

       Configuracoes da Clinica   3727px = 5,1 telas · 43 campos · 7 secoes
       Configuracoes do Sistema   3379px = 4,6 telas · 23 campos · 8 secoes

   Chegar em "Backup" custava rolar por Agendamentos, Calendario, Notificacoes,
   WhatsApp, Financeiro e Estoque. No computador as secoes se distribuem em
   colunas e o custo nao aparece — foi por isso que passou.

   No celular cada secao vira uma linha tocavel; a partir de 640px todas se
   abrem e a tela volta a ser exatamente o que era.

   POR QUE <details> E NAO x-show
   -------------------------------
   Campo `required` dentro de bloco com `display:none` faz o Chrome abortar o
   envio com "An invalid form control is not focusable" NO CONSOLE — a tela nao
   reage e nada explica por que. Com <details>, o proprio navegador abre a
   secao para mostrar a mensagem de validacao (comportamento padronizado no
   HTML e disponivel no Chrome desde a 130). Sobra ainda o teclado, o leitor de
   tela e o "buscar na pagina", que <details> resolve de graca e uma div com
   x-show nao resolve.

   A regra de 640px usa !important de proposito: ela precisa vencer o
   `details:not([open]) > *:not(summary) { display: none }` da folha do
   navegador, que tem a mesma especificidade em jogo aqui. Sem isso, uma secao
   fechada no celular continuaria fechada ao girar para paisagem.
   ------------------------------------------------------------------------ */
.ds-secao > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-height: var(--ds-toque);      /* D2: 44px e o piso */
    padding: 12px 20px;
    cursor: pointer;
    font-weight: 600;
    color: var(--ds-tinta);
    list-style: none;                 /* tira o triangulo padrao */
}
.ds-secao > summary::-webkit-details-marker { display: none; }

.ds-secao > summary .ds-secao__seta {
    color: var(--ds-fraco);
    transition: transform .15s ease;
    flex-shrink: 0;
}
.ds-secao[open] > summary .ds-secao__seta { transform: rotate(180deg); }

/* Separador entre o titulo e o corpo, so quando aberta: fechada, a linha
   pareceria uma secao vazia. */
.ds-secao[open] > summary { border-bottom: 1px solid var(--ds-regua); }

@media (min-width: 640px) {
    /* No computador nao ha sanfona: o cabecalho proprio da secao (<h3>) volta
       a ser o titulo, e o corpo fica sempre visivel. */
    .ds-secao > summary { display: none; }
    .ds-secao > .ds-secao__corpo { display: block !important; }
}

/* --- filtro de periodo (datas inicio/fim) -------------------------------- */
/* ── Barra de filtro (relatorios) ───────────────────────────────────────
   Celular: uma coluna, rotulo em cima do campo. Desktop: volta pra linha.

   Era um grid de tres colunas fixas (`1fr 1fr auto`), o que sup√µe
   exatamente tres filhos — De, Ate e as acoes. Servia so pro relatorio de
   agenda, o unico que usava o componente. Os outros 15 tem formatos
   diferentes: um `<select>` de periodo e as acoes; mes + status + acoes;
   atalhos ("Hoje", "7 dias") + duas datas + acoes. Com colunas fixas, o
   quarto filho cairia numa segunda fileira desalinhada.

   Vira flex no desktop: cada campo cresce, as acoes ficam do tamanho do
   conteudo, e o componente passa a aceitar qualquer quantidade de campos.
   No celular segue empilhando — que e o ponto todo, ja que `flex-wrap`
   sem rotulo (o que as 15 telas faziam) vira uma pilha de campos onde nao
   da pra saber qual data e "de" e qual e "ate". */
.ds-filtro-datas {
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
    align-items: end;
}
@media (min-width: 640px) {
    .ds-filtro-datas {
        display: flex;
        flex-wrap: wrap;
        gap: 16px;
    }
    /* `160px` de base: abaixo disso um campo de data fica ilegivel, e o
       flex prefere quebrar a linha a espremer. */
    .ds-filtro-datas > * {
        flex: 1 1 160px;
        min-width: 0;
    }
    .ds-filtro-datas > .ds-filtro-datas__acoes {
        flex: 0 0 auto;
    }
}
.ds-filtro-datas label {
    display: block;
    margin-bottom: 4px;
    font-size: 13px;
    color: var(--ds-tinta-fraca, #4b5563);
}
.ds-filtro-datas .ds-campo,
.ds-filtro-datas input,
.ds-filtro-datas select {
    width: 100%;
    min-height: var(--ds-toque);
    min-width: 0;
}
.ds-filtro-datas .ds-filtro-datas__acoes {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}

/* Filtrar e Exportar tinham tamanhos diferentes na MESMA linha em 6
   relatorios (`py-2` no Filtrar contra `py-1.5` no CSV/Excel; em
   clients.html era `py-1` contra `py-1.5`). Igualar aqui, e nao em cada
   template, e o que permite manter a COR tematica de cada relatorio
   intacta — a regra so mexe em altura e alinhamento. */
.ds-filtro-datas .ds-filtro-datas__acoes > button,
.ds-filtro-datas .ds-filtro-datas__acoes > a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: var(--ds-toque);
    padding: 0 16px;
    border-radius: var(--ds-raio-campo);
    font-size: 14px;
    line-height: 1.2;
    white-space: nowrap;
}
/* No celular as acoes dividem a largura em vez de virar uma fileira de
   botoes de larguras diferentes. */
@media (max-width: 639px) {
    .ds-filtro-datas .ds-filtro-datas__acoes > button,
    .ds-filtro-datas .ds-filtro-datas__acoes > a {
        flex: 1 1 0;
        min-width: 0;
    }
}

/* Campos nunca estouram a tela no Chrome Mobile. */
input, select, textarea {
    max-width: 100%;
}
.ds-campo-grupo, .ds-grade {
    min-width: 0;
}

/* ── Piso de toque para quem NAO usa .ds-campo / .ds-botao ──────────────
   O sistema TEM um padrao canonico — `.ds-campo` e `.ds-botao`, ambos com
   `min-height: var(--ds-toque)` (44px). O problema e a adocao: 19 campos
   usam `.ds-campo` e 367 escrevem Tailwind cru, cada tela com a sua
   combinacao (py-1, py-1.5, py-2, py-2.5, py-3; px-2 ate px-6; `rounded`
   ou `rounded-lg`). Dos 367, so 52 chegavam aos 44px por conta propria.

   Na mesma pagina isso vira o sintoma visivel: um campo de 32px ao lado de
   um de 42px, um botao "Salvar" mais baixo que o "Cancelar" do lado.

   Reescrever 367 campos em 100+ arquivos seria um patch gigante e fragil, e
   nao impediria a proxima tela de nascer torta. Um piso aqui normaliza tudo
   de uma vez, inclusive o que ainda vai ser escrito. O piso NAO mexe em
   padding, cor, borda ou raio: cada tela mantem a propria aparencia, so nao
   pode mais ficar abaixo do alvo de toque.

   `min-height` (nao `height`) de proposito: um campo que ja e maior — um
   `<textarea rows="4">`, um botao com rotulo em duas linhas — continua do
   tamanho que era.

   FICAM DE FORA, e cada exclusao tem motivo:
     hidden          nao tem caixa; 44px criaria espaco fantasma no layout.
     checkbox/radio  sao 16px por design (w-4 h-4 em 253 lugares); esticar
                     um checkbox para 44px deforma o controle. O alvo de
                     toque deles vem do <label> que os embrulha.
     range           o "trilho" cresceria e desalinharia do polegar.
     color           o Chrome desenha o swatch com a altura da caixa; 44px
                     vira um retangulo de cor gigante.
     [multiple],
     [size]          lista de varias linhas, ja e alta por definicao. */
@media (max-width: 639px) {
    input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]),
    select:not([multiple]):not([size]),
    textarea,
    button {
        min-height: var(--ds-toque);
    }

    /* O `<select>` original do combobox de busca (base.html) fica invisivel
       e esticado por cima do botao falso — se ganhasse o piso, empurraria a
       altura do wrapper. Quem precisa do piso ali e o botao, que ja o
       recebe pela regra acima. */
    select[data-select-busca] {
        min-height: 0;
    }

    /* Fecha o vao entre "tem 44px de altura" e "parece um campo do mesmo
       sistema". Efeito pequeno de proposito: 310 campos ja usam `rounded-lg`,
       que E os 8px de `--ds-raio-campo` — para esses a regra nao muda nada.
       Ela existe pelos 28 que usam `rounded` (4px) e destoam ao lado dos
       outros. Nenhum campo do sistema usa raio deliberadamente diferente
       (rounded-xl/full em <input> = 0 ocorrencias), entao nao ha desenho
       proprio sendo desfeito aqui. */
    input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]),
    select:not([multiple]):not([size]),
    textarea {
        border-radius: var(--ds-raio-campo);
    }
}

/* ── Atalhos (Tarefas Rapidas) ──────────────────────────────────────────
   Ladrilho de icone-em-cima-do-rotulo, quatro por linha no celular.

   Quatro e o limite fisico, e nao uma preferencia: em 360px a celula fica
   com ~76px (60px uteis). O rotulo so cabe ali porque usa a largura TODA —
   um botao horizontal, com o icone ao lado do texto, precisaria de ~84px so
   para "Financeiro". Foi a conta que decidiu o formato.

   Passando de quatro atalhos, o quarto lugar vira o botao "⋯" e o resto vai
   para um painel (JS em base.html). Assim a faixa e SEMPRE uma linha: a
   alternativa — deixar quebrar em duas ou tres — era o que fazia o bloco
   comer meia tela no painel, que tem oito atalhos. */
.ds-atalhos {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 8px;
}
/* `> a, > button, > form` e nao `> *`: seletor de elemento soma
   especificidade (0,1,1) e vence as utilitarias do Tailwind (`.p-3`,
   `.gap-2`, 0,1,0) que os ladrilhos ja carregam. Com `> *` daria EMPATE, e
   quem chega depois vence — o Tailwind, que o CDN injeta em tempo de
   execucao, depois deste arquivo. O ladrilho ficaria com o padding de 12px
   e nao caberia em quatro colunas. */
.ds-atalhos > a,
.ds-atalhos > button,
.ds-atalhos > form {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 68px;
    padding: 8px 4px;
    text-align: center;
    border-radius: 12px;
    transition: transform var(--ds-dur) var(--ds-ease), opacity var(--ds-dur) var(--ds-ease);
}
.ds-atalhos > a:focus-visible,
.ds-atalhos > button:focus-visible {
    transform: translateY(-2px);
}
.ds-atalhos > a:active,
.ds-atalhos > button:active {
    transform: translateY(1px);
    opacity: .92;
}
/* O rotulo pode ocupar duas linhas em 60px ("Relatórios", "Pagamento") sem
   empurrar a altura do ladrilho para fora da linha. */
.ds-atalhos > a > p,
.ds-atalhos > button > p,
.ds-atalhos > a > span:last-child,
.ds-atalhos > button > span:last-child {
    font-size: 11px;
    line-height: 1.15;
    font-weight: 500;
    overflow-wrap: anywhere;
}
.ds-atalhos__mais {
    cursor: pointer;
    background: #f9fafb;
    border: 1px solid #e5e7eb;
    color: #4b5563;
}
/* O painel dos escondidos: uma coluna, rotulo inteiro, sem aperto nenhum —
   e justamente o lugar onde nao falta largura.

   `justify-content: flex-start` e o conserto de um desalinhamento que so
   aparecia aqui: os ladrilhos chegam com `justify-center` do Tailwind (na
   grade eles SAO centrados, e ali isso esta certo), e a regra acima so
   trocava a DIRECAO para linha. O eixo principal continuava centralizado,
   entao cada linha ficava com o par icone+rotulo no meio — e como o rotulo
   varia de "Termos" a "Intercorrencias", cada icone comecava num X
   diferente. A lista virava uma escada. */
.ds-atalhos-painel__lista > a,
.ds-atalhos-painel__lista > button,
.ds-atalhos-painel__lista > form {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 12px;
    width: 100%;
    min-height: 52px;
    padding: 8px 16px;
    text-align: left;
    border-radius: 0;
}
/* Coluna fixa para o icone. Sem ela os rotulos ainda comecariam em X
   diferentes de uma tela para outra: o ladrilho traz o icone ora num
   `span` de 36px (`w-9`), ora num de 40px (`w-10`), ora como um `<i>` solto
   com a largura do proprio glifo — e "Mapa Facial" ao lado de um sorriso
   estreito nao alinha com "Planejamento" ao lado de um mapa largo. */
.ds-atalhos-painel__lista > a > :first-child,
.ds-atalhos-painel__lista > button > :first-child,
.ds-atalhos-painel__lista > form > :first-child {
    flex: 0 0 36px;
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
}
/* O rotulo ocupa o resto da linha, e nao so o que o texto mede: assim o
   toque cobre a largura inteira e o alvo nao depende do tamanho da palavra. */
.ds-atalhos-painel__lista > a > :last-child,
.ds-atalhos-painel__lista > button > :last-child {
    flex: 1 1 auto;
    min-width: 0;
    text-align: left;
}
.ds-atalhos-painel__lista > a > p,
.ds-atalhos-painel__lista > button > p,
.ds-atalhos-painel__lista > a > span:last-child,
.ds-atalhos-painel__lista > button > span:last-child {
    font-size: 14px;
    font-weight: 500;
}

/* A partir de sm a largura sobra: todos aparecem, sem "⋯". Quem devolve
   os escondidos para a grade e o JS de base.html — eles foram MOVIDOS para
   a folha, e nao apenas ocultados, entao nenhuma regra daqui alcancaria
   eles. Este bloco so cuida do que sobrou: a grade se solta das quatro
   colunas e o proprio "⋯" some. */
@media (min-width: 640px) {
    .ds-atalhos {
        grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
        gap: 12px;
    }
    .ds-atalhos__mais {
        display: none !important;
    }
}

/* ── Confete do aniversariante do mes ──────────────────────────────────────
   Overlay decorativo para o cartao de um agendamento cujo cliente faz
   aniversario no MES corrente. Puro CSS (transform + opacity, sem layout por
   frame) — funciona igual no cartao do painel e nos blocos da agenda, sem
   depender de JS. O container precisa de `position` != static e, de
   preferencia, `overflow: hidden`. Some com prefers-reduced-motion.
   Global porque e usado no dashboard e na agenda (FullCalendar). */
.aniv-confete {
    position: absolute;
    inset: 0;
    overflow: hidden;
    border-radius: inherit;
    pointer-events: none;
    z-index: 0;
}
.aniv-confete > i {
    position: absolute;
    top: -12px;
    width: 6px;
    height: 9px;
    border-radius: 1px;
    background: var(--aniv-cor, #ec4899);
    opacity: 0;
    animation: aniv-cai var(--aniv-dur, 2.8s) linear var(--aniv-atraso, 0s) infinite;
}
.aniv-confete > i:nth-child(6n+1) { left: 8%;  --aniv-cor: #ec4899; --aniv-dur: 2.6s; --aniv-atraso: 0s;   }
.aniv-confete > i:nth-child(6n+2) { left: 23%; --aniv-cor: #8b5cf6; --aniv-dur: 3.3s; --aniv-atraso: .55s; }
.aniv-confete > i:nth-child(6n+3) { left: 39%; --aniv-cor: #f59e0b; --aniv-dur: 2.9s; --aniv-atraso: 1.1s; }
.aniv-confete > i:nth-child(6n+4) { left: 57%; --aniv-cor: #10b981; --aniv-dur: 3.1s; --aniv-atraso: .3s;  }
.aniv-confete > i:nth-child(6n+5) { left: 74%; --aniv-cor: #3b82f6; --aniv-dur: 2.7s; --aniv-atraso: .85s; }
.aniv-confete > i:nth-child(6n)   { left: 90%; --aniv-cor: #ec4899; --aniv-dur: 3.5s; --aniv-atraso: 1.5s; }

/* Fundo colorido (cartao do proximo, na cor do tenant): parte das pecas
   branca/dourada para nao sumir no fundo. */
.aniv-confete--claro > i:nth-child(6n+1) { --aniv-cor: #ffffff; }
.aniv-confete--claro > i:nth-child(6n+4) { --aniv-cor: #ffffff; }
.aniv-confete--claro > i:nth-child(6n+2) { --aniv-cor: #ffe17a; }

@keyframes aniv-cai {
    0%   { opacity: 0; transform: translate3d(0, -12px, 0) rotate(0deg); }
    10%  { opacity: 1; }
    50%  { transform: translate3d(7px, 150px, 0) rotate(200deg); }
    90%  { opacity: 1; }
    100% { opacity: 0; transform: translate3d(-7px, 320px, 0) rotate(460deg); }
}

@media (prefers-reduced-motion: reduce) {
    .aniv-confete { display: none; }
}

/* ── Microinteracoes (D14) ──────────────────────────────────────────────
   Celular E desktop. So transform/opacity. Hover so com ponteiro fino;
   no toque o feedback e :active (afunda) e :focus-visible (sobe).
   Fonte: Web Interface Guidelines da Vercel (Animation + Anti-patterns). */
.ds-chip-dia,
.ds-chip-hora {
    touch-action: manipulation;
    transition: transform var(--ds-dur) var(--ds-ease), opacity var(--ds-dur) var(--ds-ease);
}
.ds-chip-dia:focus-visible,
.ds-chip-hora:focus-visible {
    transform: translateY(-1px);
}
.ds-chip-dia:active,
.ds-chip-hora:active {
    transform: translateY(1px);
    opacity: .92;
}

@media (hover: hover) and (pointer: fine) {
    .ds-chip-dia:hover,
    .ds-chip-hora:hover,
    .ds-botao:hover:not([disabled]):not([aria-disabled="true"]) {
        transform: translateY(-1px);
    }
    .ds-atalhos > a:hover,
    .ds-atalhos > button:hover {
        transform: translateY(-2px);
    }
}

@keyframes ds-revela {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}
.ds-revela {
    animation: ds-revela var(--ds-dur-secao) var(--ds-ease) both;
}

@media (prefers-reduced-motion: reduce) {
    :root {
        --ds-dur: 1ms;
        --ds-dur-secao: 1ms;
    }
    .ds-revela { animation: none; }
    .ds-chip-dia,
    .ds-chip-hora,
    .ds-botao,
    .ds-atalhos > a,
    .ds-atalhos > button {
        transition: none;
    }
    .ds-botao:hover,
    .ds-botao:active,
    .ds-botao:focus-visible,
    .ds-atalhos > a:hover,
    .ds-atalhos > a:active,
    .ds-atalhos > a:focus-visible,
    .ds-atalhos > button:hover,
    .ds-atalhos > button:active,
    .ds-atalhos > button:focus-visible,
    .ds-chip-dia:hover,
    .ds-chip-dia:active,
    .ds-chip-dia:focus-visible,
    .ds-chip-hora:hover,
    .ds-chip-hora:active,
    .ds-chip-hora:focus-visible {
        transform: none;
    }
}

/* ==========================================================================
   D14 (extensão) — listas em cascata, cartões e transição de página

   Pedido de motion design. Três peças, nenhuma nova dependência: só
   transform/opacity (mesma regra do bloco acima) e os tokens --ds-dur* já
   existentes, então prefers-reduced-motion continua resolvido no MESMO
   lugar (eles caem para 1ms lá em cima) — só reforço abaixo o que precisa
   de "animation: none" de verdade, não só duração curta.
   ========================================================================== */

/* ── 1. Linhas de lista entram em cascata, não todas de uma vez ──────────
   Mesma keyframe `ds-revela` do cartão de login/formulário, aplicada direto
   em `.swipe-linha` (cartão de 2 linhas — clientes, vendas, procedimentos…)
   e em `table.responsive-table tbody tr` (linha de tabela no desktop, MESMA
   marcação virando cartão no celular). CSS puro: uma linha que chega depois
   via HTMX já nasce com a regra, sem precisar de JS reaplicando classe.
   O atraso é só nos primeiros ~10 itens — o resto da lista, fora da
   primeira tela, não precisa esperar a vez: anima junto do primeiro assim
   que existe no DOM. */
.swipe-linha,
table.responsive-table tbody tr {
    animation: ds-revela var(--ds-dur-secao) var(--ds-ease) both;
}
.swipe-linha:nth-child(2),  table.responsive-table tbody tr:nth-child(2)  { animation-delay: 30ms; }
.swipe-linha:nth-child(3),  table.responsive-table tbody tr:nth-child(3)  { animation-delay: 60ms; }
.swipe-linha:nth-child(4),  table.responsive-table tbody tr:nth-child(4)  { animation-delay: 90ms; }
.swipe-linha:nth-child(5),  table.responsive-table tbody tr:nth-child(5)  { animation-delay: 120ms; }
.swipe-linha:nth-child(6),  table.responsive-table tbody tr:nth-child(6)  { animation-delay: 150ms; }
.swipe-linha:nth-child(7),  table.responsive-table tbody tr:nth-child(7)  { animation-delay: 180ms; }
.swipe-linha:nth-child(8),  table.responsive-table tbody tr:nth-child(8)  { animation-delay: 210ms; }
.swipe-linha:nth-child(9),  table.responsive-table tbody tr:nth-child(9)  { animation-delay: 240ms; }
.swipe-linha:nth-child(10), table.responsive-table tbody tr:nth-child(10) { animation-delay: 270ms; }

/* ── 2. Cartão/linha reage ao toque e ao mouse ────────────────────────────
   `.swipe-linha` e a linha de tabela nao tinham NENHUM feedback ao tocar —
   só o `.ds-botao` (D14 original) tinha. `:active` escurece de leve (mesmo
   espirito do botão, sem transform: o gesto de arrastar já move a linha
   via JS, e um scale/translate daqui brigaria com aquele). O hover-lift
   fica atrás de `(hover: hover)` — só ganha quem tem mouse de verdade. */
.swipe-linha,
table.responsive-table tbody tr {
    transition: background-color var(--ds-dur) var(--ds-ease), transform var(--ds-dur) var(--ds-ease);
}
.swipe-linha:active { background-color: var(--ds-fundo); }
@media (hover: hover) and (pointer: fine) {
    .swipe-linha:hover { transform: translateY(-1px); }
}

/* ── 3. Transição entre páginas (View Transitions API) ────────────────────
   Django navega documento inteiro — sem SPA, sem router — mas o navegador
   já sabe fazer o cross-fade sozinho: só precisa optar. `navigation: auto`
   vale tanto para o clique num link quanto para HTMX, que reaproveita a
   MESMA API para as trocas parciais (globalViewTransitions, ligado em
   base.html) — por isso não existe uma segunda regra para HTMX aqui.
   Sem suporte no navegador (Firefox, Safari antigo), a navegação some e
   volta como sempre — degrada sozinho, sem branch de JS nenhum. */
@view-transition {
    navigation: auto;
}
::view-transition-old(root) {
    animation: ds-saida-pagina var(--ds-dur) var(--ds-ease) both;
}
::view-transition-new(root) {
    animation: ds-entrada-pagina var(--ds-dur-secao) var(--ds-ease) both;
}
@keyframes ds-entrada-pagina {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes ds-saida-pagina {
    to { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .swipe-linha,
    table.responsive-table tbody tr {
        animation: none;
        animation-delay: 0ms;
    }
    .swipe-linha:hover,
    table.responsive-table tbody tr:hover {
        transform: none;
    }
    ::view-transition-old(root),
    ::view-transition-new(root) {
        animation: none;
    }
}

/* ==========================================================================
   D14 — A CONVERSA DO WHATSAPP

   A tela de Conversas usava cartões de borda cinza para os dois lados do fio:
   os dois iguais, hora numa linha própria, e a leitura exigia conferir o
   alinhamento para saber quem falou. Quem abre esta tela já sabe ler a
   conversa do próprio WhatsApp — o desenho aqui usa a mesma convenção em vez
   de inventar outra: balão com rabinho, entrada branca à esquerda, saída na
   cor do tenant à direita, hora DENTRO do balão.

   As cores de saída saem de `--ds-primaria` (a cor da clínica), então a tela
   continua branca de marca sem nada codificado aqui.
   ========================================================================== */

/* O fundo do fio. A trama é um `radial-gradient` repetido e não uma imagem:
   precisa sobreviver ao runtime serverless sem um arquivo estático a mais, e
   um PNG de textura pesaria mais que o CSS da tela inteira. Bem discreta de
   propósito — ela separa a conversa do cartão branco em volta e para por aí. */
.ds-fio {
    background-color: var(--ds-fundo);
    background-image: radial-gradient(circle at 1px 1px, rgba(17, 24, 39, 0.045) 1px, transparent 0);
    background-size: 18px 18px;
}

.ds-balao {
    position: relative;
    max-width: 85%;
    padding: 8px 11px 6px;
    border-radius: 12px;
    box-shadow: 0 1px 1px rgba(17, 24, 39, 0.07);
    /* Uma URL longa (o copia-e-cola do Pix tem ~150 caracteres sem espaço)
       estourava a largura do balão e criava rolagem horizontal no fio. */
    overflow-wrap: anywhere;
}

.ds-balao__texto {
    font-size: 15px;
    line-height: 1.45;
    color: var(--ds-tinta);
    white-space: pre-wrap;
}

.ds-balao__meta {
    margin-top: 2px;
    font-size: 11px;
    line-height: 1.2;
    text-align: right;
}

/* --- mídia dentro do balão (áudio, imagem, vídeo, documento) --------------
   O player nativo é de propósito: `<audio controls>` já traz play, barra e
   tempo, com teclado e leitor de tela funcionando, e no celular usa o controle
   que a pessoa conhece. Reescrever isso em JS custaria acessibilidade para
   ganhar aparência.

   `max-width: 100%` em tudo porque a mídia é a coisa mais fácil de estourar a
   largura do balão no celular — e o balão é `max-width` de uma fração da tela.  */
.ds-balao__midia {
    margin-bottom: 6px;
}
.ds-balao__midia:last-child {
    margin-bottom: 2px;
}
.ds-balao__audio {
    display: block;
    width: 100%;
    /* Piso confortável para o alvo de toque do play sem deixar o balão gigante. */
    min-width: 200px;
    max-width: 260px;
    height: 40px;
}
.ds-balao__imagem,
.ds-balao__video {
    display: block;
    max-width: 100%;
    max-height: 280px;
    border-radius: 10px;
    background: var(--ds-fundo);
}
.ds-balao__midia-nota {
    font-size: 11px;
    color: var(--ds-apoio);
}
.ds-balao__arquivo {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    /* Piso tátil de 44px do design system: baixar um anexo é um toque. */
    min-height: 44px;
    max-width: 100%;
    padding: 0 10px;
    border-radius: 10px;
    background: var(--ds-fundo);
    color: inherit;
    text-decoration: none;
}
.ds-balao__arquivo-nome {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
}
/* Sem arquivo o balão não desaparece: ele conta o que houve. Ver o comentário
   em `MidiaDeMensagem` — buraco silencioso no histórico é pior que balão sem
   player. */
.ds-balao__midia-ausente {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 36px;
    padding: 0 10px;
    border-radius: 10px;
    border: 1px dashed var(--ds-borda);
    font-size: 12px;
    color: var(--ds-apoio);
}
.ds-balao--saida .ds-balao__arquivo,
.ds-balao--saida .ds-balao__midia-ausente {
    /* O balão de saída é pintado na cor do tenant; o fundo neutro do anexo
       ficaria como um retângulo estranho em cima dele. */
    background: rgba(255, 255, 255, .18);
    border-color: rgba(255, 255, 255, .35);
    color: inherit;
}
.ds-balao--saida .ds-balao__midia-nota {
    color: inherit;
    opacity: .8;
}

.ds-balao--entrada {
    align-self: flex-start;
    background: var(--ds-superficie);
    border-top-left-radius: 3px;
}

.ds-balao--entrada .ds-balao__meta {
    color: var(--ds-fraco);
}

.ds-balao--saida {
    align-self: flex-end;
    /* 14% da cor da clínica: fica legível com tinta escura em cima em qualquer
       tom que o tenant escolha, inclusive num primário já claro — foi por isso
       que a cor cheia não serve aqui. */
    background: color-mix(in srgb, var(--ds-primaria) 14%, #ffffff);
    border-top-right-radius: 3px;
}

.ds-balao--saida .ds-balao__meta {
    color: color-mix(in srgb, var(--ds-primaria) 75%, #4b5563);
}

.ds-balao--falhou {
    background: var(--ds-erro-fundo);
}

.ds-balao--falhou .ds-balao__meta {
    color: var(--ds-erro-texto);
    font-weight: 600;
}

/* O rabinho. Dois triângulos por `border`, sem pseudo-elemento com imagem:
   o mesmo desenho do WhatsApp e nada para baixar. */
.ds-balao--entrada::before,
.ds-balao--saida::before {
    content: '';
    position: absolute;
    top: 0;
    width: 0;
    height: 0;
    border: 6px solid transparent;
}

.ds-balao--entrada::before {
    left: -6px;
    border-top-color: var(--ds-superficie);
    border-right-color: var(--ds-superficie);
}

.ds-balao--saida::before {
    right: -6px;
    border-top-color: color-mix(in srgb, var(--ds-primaria) 14%, #ffffff);
    border-left-color: color-mix(in srgb, var(--ds-primaria) 14%, #ffffff);
}

.ds-balao--falhou::before {
    border-top-color: var(--ds-erro-fundo);
    border-left-color: var(--ds-erro-fundo);
}

@media (min-width: 640px) {
    .ds-balao {
        max-width: 74%;
    }
    .ds-balao__texto {
        font-size: 13.5px;
    }
}

/* Ação do cabeçalho: quadrada, de largura FIXA. O `.ds-botao` com texto
   disputava largura com o nome do contato, e o resultado era o nome cortado em
   "Thais Co…" enquanto o telefone quebrava em duas linhas. */
.ds-conversa__acao {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 38px;
    height: 38px;
    border-radius: 10px;
    border: 1px solid var(--ds-borda);
    background: var(--ds-superficie);
    color: var(--ds-apoio);
    font-size: 14px;
    cursor: pointer;
    transition: background var(--ds-dur) var(--ds-ease), color var(--ds-dur) var(--ds-ease);
}

.ds-conversa__acao:hover {
    background: var(--ds-regua);
    color: var(--ds-tinta-fraca);
}

.ds-conversa__acao--perigo:hover {
    background: var(--ds-erro-fundo);
    color: var(--ds-erro-texto);
    border-color: var(--ds-erro-aviso-borda);
}

/* A bandeja de anexos: o que dá para mandar além de texto. Cartão vertical
   (ícone, rótulo, uma linha de ajuda) porque a ajuda é o que responde a
   pergunta real — "o horário de quando?", "cobrar como?". */
.ds-anexo {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    width: 100%;
    min-height: 72px;
    padding: 10px;
    border: 1px solid var(--ds-borda);
    border-radius: var(--ds-raio-cartao);
    background: var(--ds-superficie);
    text-align: left;
    cursor: pointer;
    transition: border-color var(--ds-dur) var(--ds-ease), background var(--ds-dur) var(--ds-ease);
}

.ds-anexo:hover:not(:disabled) {
    border-color: var(--ds-primaria);
    background: var(--ds-fundo);
}

.ds-anexo:disabled {
    cursor: not-allowed;
    opacity: 0.55;
}

.ds-anexo__icone {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    font-size: 13px;
}

.ds-anexo__rotulo {
    font-size: 13px;
    font-weight: 700;
    color: var(--ds-tinta);
    line-height: 1.25;
}

.ds-anexo__ajuda {
    font-size: 11.5px;
    color: var(--ds-apoio);
    line-height: 1.25;
}
