/* Feedback e tato — fundação compartilhada pelos três produtos.
   Par de web/shared/static/js/forms.js. Consome tokens.css e as convenções de
   app.css/controls.css; não introduz paleta, fonte, raio ou sombra novos.

   Cobre: aviso em toast, dica em tooltip, erro de campo personalizado e a
   camada que faz um site responsivo responder como aplicativo no toque.

   Degradação: o aviso é renderizado pelo servidor como <p class="flash"> ou
   <p class="error">, dentro do conteúdo. O JS só o promove a toast. Sem JS o
   aviso continua no lugar de sempre, legível, com o mesmo papel ARIA. */

/* --- Aviso em toast ------------------------------------------------------- */
/* A região é criada pelo JS; o <p> do servidor entra inteiro, com o ícone que
   o helper {{ icon }} já renderizou. Nada de texto duplicado. */

.toasts {
  position: fixed;
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: min(26rem, calc(100vw - 2 * var(--page-gutter)));
  pointer-events: none;
  right: var(--page-gutter);
  bottom: calc(var(--page-gutter) + env(safe-area-inset-bottom, 0px));
}
.toasts:empty { display: none; }

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  position: relative;
  overflow: hidden;
  padding: var(--space-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
  background: var(--brand-white);
  box-shadow: var(--shadow-3);
  pointer-events: auto;
  /* Faixa de estado à esquerda: cor sem recolorir o cartão inteiro. */
  border-left: 4px solid var(--brand-beige);
  animation: toast-in var(--dur-overlay) var(--ease-standard);
  touch-action: pan-x;
}
.toast[data-kind="success"] { border-left-color: var(--brand-blue); }
.toast[data-kind="error"] { border-left-color: var(--brand-orange); }
.toast[data-kind="info"] { border-left-color: var(--brand-beige); }
/* O <p> do servidor perde a caixa de .flash/.error: aqui quem tem caixa é o
   toast. Mantém texto, ícone e papel ARIA. */
.toast > p {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--brand-blue);
  font-size: var(--text-sm);
  line-height: var(--leading-body);
}
.toast > p .ico { flex: none; }
.toast-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-badge);
  background: none;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background-color var(--dur-press) linear, color var(--dur-press) linear;
}
.toast-close:hover { background: var(--surface-sunken); color: var(--brand-blue); }
.toast-close .ui-ico { width: 1rem; height: 1rem; }
/* Barra de tempo: o aviso que vai sumir avisa que vai sumir. */
.toast-timer {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 2px;
  width: 100%;
  transform-origin: left center;
  background: var(--brand-orange);
  animation: toast-timer var(--toast-life, 6s) linear forwards;
}
.toast[data-paused="true"] .toast-timer { animation-play-state: paused; }
.toast[data-leaving="true"] { animation: toast-out var(--dur-state) var(--ease-exit) forwards; }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(12px) scale(0.98); }
  to { opacity: 1; transform: none; }
}
@keyframes toast-out {
  to { opacity: 0; transform: translateX(12%) scale(0.98); }
}
@keyframes toast-timer {
  from { transform: scaleX(1); }
  to { transform: scaleX(0); }
}

/* O aviso alinha ícone e texto por gap, não por um espaço no template: assim o
   texto do elemento é exatamente a mensagem do servidor, sem espaço pendurado
   na frente — o que importa para leitor de tela e para automação. */
:where(.flash, .error) {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
:where(.flash, .error) .ico { flex: none; }

/* --- Dica em tooltip ----------------------------------------------------- */
/* CSS puro: nada de JS para mostrar texto curto. Aparece no ponteiro e no foco
   por teclado. `aria-label`/`title` continuam sendo a fonte para leitor de
   tela — a dica é reforço visual, nunca a única via. */

[data-tip] { position: relative; }
[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  z-index: 50;
  bottom: calc(100% + 8px);
  left: 50%;
  width: max-content;
  max-width: 15rem;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-badge);
  background: var(--surface-deep);
  color: var(--brand-white);
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: normal;
  text-align: center;
  text-transform: none;
  white-space: pre-line;
  box-shadow: var(--shadow-2);
  opacity: 0;
  transform: translate(-50%, 4px);
  pointer-events: none;
  transition: opacity var(--dur-state) var(--ease-standard), transform var(--dur-state) var(--ease-standard);
}
[data-tip][data-tip-place="below"]::after { bottom: auto; top: calc(100% + 8px); transform: translate(-50%, -4px); }
[data-tip][data-tip-place="right"]::after { bottom: auto; top: 50%; left: calc(100% + 8px); transform: translate(0, -50%) translateX(-4px); }
[data-tip]:hover::after,
[data-tip]:focus-visible::after { opacity: 1; transform: translate(-50%, 0); }
[data-tip][data-tip-place="below"]:hover::after,
[data-tip][data-tip-place="below"]:focus-visible::after { transform: translate(-50%, 0); }
[data-tip][data-tip-place="right"]:hover::after,
[data-tip][data-tip-place="right"]:focus-visible::after { transform: translate(0, -50%); }
/* No toque não existe hover: a dica nunca aparece e não pode ser a única
   informação. Desligada de propósito para não piscar ao tocar. */
@media (hover: none) {
  [data-tip]::after { content: none; }
}

/* --- Erro de campo personalizado ---------------------------------------- */
/* O texto vem do JS (forms.js), em português, a partir de `data-error` ou do
   rótulo do campo. A validação nativa do navegador continua sendo a regra;
   só a mensagem e a apresentação são nossas. */

.field-error:empty { display: none; }
.field-error[data-shown="true"] {
  animation: field-error-in var(--dur-state) var(--ease-standard);
}
@keyframes field-error-in {
  from { opacity: 0; transform: translateY(-2px); }
  to { opacity: 1; transform: none; }
}
/* Campo inválido: laranja é o alerta da marca, e a borda engrossa para não
   depender só de cor. */
:where(input, select, textarea)[aria-invalid="true"] {
  border-color: var(--brand-orange);
  border-width: 2px;
}
label:has(> [aria-invalid="true"]) { color: var(--brand-blue); }
/* Formulário em envio: avisa que está trabalhando e impede envio duplo. */
form[data-submitting="true"] { cursor: progress; }
form[data-submitting="true"] button[type="submit"] { opacity: 0.65; pointer-events: none; }

/* --- Sensação de aplicativo no toque ------------------------------------ */
/* Um site responsivo não vira aplicativo só diminuindo: o que convence é o
   tempo de resposta ao dedo, a rolagem que não vaza e o teclado certo. */

:where(a, button, label, summary, .ui-trigger, .ui-opt, .ui-day, .toast-close, input[type="checkbox"], input[type="radio"]) {
  /* Remove o atraso de 300ms herdado do duplo-toque para zoom. */
  touch-action: manipulation;
  /* E o retângulo cinza do Android/iOS, substituído pelos nossos estados. */
  -webkit-tap-highlight-color: transparent;
}

/* Rolagem interna não arrasta a página atrás (nem dispara pull-to-refresh). */
:where(.ui-list, .ui-pop, .table-scroll, .toasts) {
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

@media (pointer: coarse) {
  /* 16px é o limiar abaixo do qual o iOS dá zoom ao focar um campo. O zoom
     desloca o layout e parece defeito; a tipografia cede antes disso.
     `!important` aqui é deliberado e o instrumento mais estreito: os produtos
     pedem 14px no campo operacional com seletores mais específicos
     (`.app-main input:not(...)` no Financeiro) e esta folha carrega antes
     deles. Não é preferência estética, é comportamento de plataforma — por
     isso vence, e só na largura de toque. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
  select,
  textarea,
  .ui-trigger,
  .ui-opt { font-size: 16px !important; }

  /* Pressão visível: sem hover, o estado :active é o único retorno imediato. */
  :where(.btn, button, .ui-opt, .ui-day, .card, .choice):active { transform: scale(0.985); }
  :where(.ui-opt, .ui-day):active { background: var(--surface-warm); }

  /* Nada depende de hover para ser alcançável. */
  :where(.table) tbody tr:active { background: var(--surface-sunken); }
}

/* Arrastar para descartar: o puxador do sheet e o toast seguem o dedo. */
[data-dragging="true"] { transition: none !important; }

@media (prefers-reduced-motion: reduce) {
  .toast, .toast[data-leaving="true"] { animation: none; }
  .toast-timer { animation-duration: 0s; animation-play-state: paused; transform: scaleX(1); }
  .field-error[data-shown="true"] { animation: none; }
  [data-tip]::after { transition: none; }
  :where(.btn, button, .ui-opt, .ui-day, .card, .choice):active { transform: none; }
}

/* --- Mobile: toast vem de cima e é descartável com o dedo ---------------- */
@media (max-width: 639px) {
  .toasts {
    top: calc(var(--space-3) + env(safe-area-inset-top, 0px));
    right: var(--space-3);
    bottom: auto;
    left: var(--space-3);
    width: auto;
  }
  .toast { padding: var(--space-4); border-radius: var(--radius-card); }
  .toast > p { font-size: 0.9375rem; }
  @keyframes toast-in {
    from { opacity: 0; transform: translateY(-12px) scale(0.98); }
    to { opacity: 1; transform: none; }
  }
  .toast-close { width: 40px; height: 40px; }
}
