/* ============================================================================
   dash-shark — OVERLAYS E ESTADOS
   Sistema de dashboard da Shark Pro Trade. Camada de superficies temporarias.
   ----------------------------------------------------------------------------
   Cobre tudo que aparece POR CIMA do conteudo ou NO LUGAR dele:
     scrim, modal, drawer, popover, menu, tooltip, toast,
     skeleton, empty, error, loading, banner, splash, transicao de pagina.

   DEPENDE de dash-shark.tokens.css — carregue os tokens ANTES deste arquivo.
   Sem eles, todo var(--ds-*) cai no vazio e o resultado e' texto preto no preto.

   Nao ha' um unico z-index literal aqui: camada sempre por --ds-z-*, e quando
   um elemento precisa ficar UM degrau abaixo de outro, usamos calc() em cima
   do token. Assim, se a ordem de camadas mudar nos tokens, ela muda inteira.

   O scrim tem token proprio (--ds-z-scrim, 150) e fica abaixo de gaveta (200)
   E de modal (300). Ancorar ele em "modal - 1" parece natural e esconde a
   gaveta atras do scrim quando alguem esquece a variante --drawer.

   Convencoes:
     .ds-bloco / .ds-bloco-elemento / .ds-bloco--modificador / .is-estado
   Estados de runtime (o que o JS liga e desliga) usam is-: .is-open,
   .is-active, .is-leaving, .is-disabled.

   TRAMA: barras, trilhos, fundos de estado vazio e skeleton sao preenchidos
   com dither (radial/repeating-linear-gradient em --ds-mesh-dot/--ds-mesh-size)
   em vez de cor chapada. Textura e' dado, nao enfeite.

   MOVIMENTO: nenhuma duracao literal em transition/animation — sempre
   var(--ds-dur-*), que ja' zera em prefers-reduced-motion. Ha' TRES excecoes,
   e todas pelo mesmo motivo: nao sao ritmo de animacao, sao PRAZO, e um prazo
   que zera deixa de ser prazo.
     --_ds-toast-tempo   (§7)  quanto falta pro toast sumir — e' informacao
     --_ds-splash-limite (§12) prazo pro splash se dispensar se o JS falhar
     --_ds-page-limite   (§12) prazo pro conteudo que saiu voltar se o JS falhar
   As duas ultimas sao rede de seguranca: sem elas, um erro de JS deixa o
   produto numa tela preta ou em branco permanente. Nao troque por --ds-dur-*.

   COR SOBRE COR: onde houver rosa, ha' duas variaveis e nao uma —
   --ds-pink (linha, borda, halo) e --ds-pink-deep (fundo de texto). A conta de
   contraste esta escrita nas secoes 5 e 11. Unificar as duas quebra o AAA.
   ========================================================================= */


/* ==========================================================================
   1. SCRIM — o fundo que apaga a pagina
   O scrim nao e' so' um veu preto: ele desfoca o que esta' atras (vidro) e
   recebe a mesma trama do resto do sistema, entao o dashboard "vira textura"
   enquanto o modal esta' aberto.
   ========================================================================== */

.ds-scrim {
  position: fixed;
  inset: 0;
  z-index: var(--ds-z-scrim);

  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--ds-space-6);

  background: var(--ds-glass-strong);
  -webkit-backdrop-filter: blur(var(--ds-blur-md)) saturate(120%);
  backdrop-filter: blur(var(--ds-blur-md)) saturate(120%);

  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--ds-dur-2) var(--ds-ease),
    visibility 0s linear var(--ds-dur-2);
}

/* trama por cima do desfoque: dither fino, quase subliminar */
.ds-scrim::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  background-image: radial-gradient(var(--ds-mesh-dot) 0.5px, transparent 0.5px);
  background-size: var(--ds-mesh-size) var(--ds-mesh-size);
}

.ds-scrim.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition:
    opacity var(--ds-dur-2) var(--ds-ease),
    visibility 0s linear 0s;
}

/* scrim de drawer: fica sob a camada de drawer e nao centraliza nada */
.ds-scrim--drawer {
  z-index: calc(var(--ds-z-drawer) - 1);
  padding: 0;
  align-items: stretch;
  justify-content: stretch;
}

/* dialogos altos: ancora no topo pra nao "pular" quando o conteudo cresce */
.ds-scrim--start { align-items: flex-start; }

/* scrim limpo, sem trama, pra quando houver imagem/video atras */
.ds-scrim--plain::after { content: none; }


/* ==========================================================================
   2. MODAL — a superficie de decisao
   Entra com y + scale + blur ao mesmo tempo: a peca chega "focando", como
   lente. Sai pelo caminho inverso. Um neon so': under-glow azul (informativo).
   ========================================================================== */

.ds-modal {
  position: relative;
  z-index: var(--ds-z-modal);

  display: flex;
  flex-direction: column;
  width: min(560px, 100%);
  max-height: min(84vh, 760px);

  background: var(--ds-surface);
  border: 1px solid var(--ds-line);
  border-radius: var(--ds-r-xl);
  box-shadow: var(--ds-e4), var(--ds-underglow-blue);

  opacity: 0;
  transform: translateY(14px) scale(0.985);
  filter: blur(6px);
  transition:
    opacity var(--ds-dur-3) var(--ds-ease),
    transform var(--ds-dur-3) var(--ds-ease),
    filter var(--ds-dur-3) var(--ds-ease);
}

/* fio de luz de 1px no topo — para antes das quinas pra nao vazar do raio */
.ds-modal::before {
  content: "";
  position: absolute;
  inset: 0 var(--ds-space-8) auto var(--ds-space-8);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--ds-line-strong), transparent);
}

/* abre pelo scrim (padrao) ou sozinho, se o modal for o proprio container */
.ds-scrim.is-open > .ds-modal,
.ds-modal.is-open {
  opacity: 1;
  transform: translateY(0) scale(1);
  filter: blur(0px);   /* blur(0), nao none: "none" nao interpola */
}

.ds-modal--sm { width: min(420px, 100%); }
.ds-modal--lg { width: min(900px, 100%); max-height: min(88vh, 900px); }

/* --- cabecalho ---------------------------------------------------------- */
.ds-modal-head {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-4);
  padding: var(--ds-space-6) var(--ds-space-6) var(--ds-space-4);
  border-bottom: 1px solid var(--ds-line-faint);
}

.ds-modal-title {
  margin: 0;
  flex: 1 1 auto;
  font-family: var(--ds-display);
  font-size: var(--ds-fs-xl);
  line-height: var(--ds-lh-snug);
  letter-spacing: var(--ds-track-tight);
  color: var(--ds-text-1);
}

/* micro-label mono acima do titulo — mesma gramatica do numero-heroi */
.ds-modal-kicker {
  display: block;
  margin-bottom: var(--ds-space-2);
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-micro);
  letter-spacing: var(--ds-track-label);
  text-transform: uppercase;
  color: var(--ds-text-3);
}

.ds-modal-sub {
  margin: var(--ds-space-2) 0 0;
  font-family: var(--ds-body);
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-text-2);
}

/* --- corpo --------------------------------------------------------------- */
.ds-modal-body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--ds-space-6);
  font-family: var(--ds-body);
  font-size: var(--ds-fs-base);
  line-height: var(--ds-lh-base);
  color: var(--ds-text-2);

  scrollbar-width: thin;
  scrollbar-color: var(--ds-line-strong) transparent;
}
.ds-modal-body::-webkit-scrollbar { width: 6px; }
.ds-modal-body::-webkit-scrollbar-track { background: transparent; }
.ds-modal-body::-webkit-scrollbar-thumb {
  background: var(--ds-line-strong);
  border-radius: var(--ds-r-pill);
}

/* corpo sem respiro lateral (tabela/lista que sangra ate' a borda) */
.ds-modal-body--flush { padding-left: 0; padding-right: 0; }

/* --- rodape -------------------------------------------------------------- */
.ds-modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--ds-space-3);
  padding: var(--ds-space-4) var(--ds-space-6) var(--ds-space-6);
  border-top: 1px solid var(--ds-line-faint);
  background: var(--ds-surface-2);
  border-radius: 0 0 var(--ds-r-xl) var(--ds-r-xl);
}

/* rodape com texto de apoio de um lado e acoes do outro */
.ds-modal-foot--split { justify-content: space-between; }

/* --- <dialog> nativo ----------------------------------------------------
   Se voce usar <dialog class="ds-modal">, o ::backdrop faz o papel do scrim
   e o browser cuida do foco e do Esc. Custom properties do :root chegam no
   ::backdrop normalmente. */
dialog.ds-modal {
  padding: 0;
  color: var(--ds-text-1);
  overflow: visible;
}
dialog.ds-modal:not([open]) { display: none; }
dialog.ds-modal[open] {
  display: flex;
  opacity: 1;
  transform: translateY(0) scale(1);
  filter: blur(0px);
}
dialog.ds-modal::backdrop {
  background: var(--ds-glass-strong);
  -webkit-backdrop-filter: blur(var(--ds-blur-md)) saturate(120%);
  backdrop-filter: blur(var(--ds-blur-md)) saturate(120%);
}


/* ==========================================================================
   3. DRAWER — a gaveta
   Fixa na borda, cantos internos arredondados (o squircle continua valendo
   mesmo quando a peca encosta na tela). Entrada e' transform puro.
   ========================================================================== */

.ds-drawer {
  position: fixed;
  z-index: var(--ds-z-drawer);

  display: flex;
  flex-direction: column;

  background: var(--ds-surface);
  box-shadow: var(--ds-e4);

  visibility: hidden;
  transition:
    transform var(--ds-dur-3) var(--ds-ease),
    visibility 0s linear var(--ds-dur-3);
}

.ds-drawer.is-open {
  visibility: visible;
  transform: translate(0, 0);
  transition:
    transform var(--ds-dur-3) var(--ds-ease),
    visibility 0s linear 0s;
}

/* fio de luz na borda que encosta no conteudo */
.ds-drawer::before {
  content: "";
  position: absolute;
  pointer-events: none;
}

.ds-drawer--right {
  top: 0;
  right: 0;
  height: 100%;
  width: min(var(--_ds-drawer-w, 420px), 100%);
  transform: translateX(100%);
  border-left: 1px solid var(--ds-line);
  border-radius: var(--ds-r-xl) 0 0 var(--ds-r-xl);
}
.ds-drawer--right::before {
  inset: 0 auto 0 0;
  width: 1px;
  background: linear-gradient(180deg, transparent, var(--ds-line-strong), transparent);
}

.ds-drawer--left {
  top: 0;
  left: 0;
  height: 100%;
  width: min(var(--_ds-drawer-w, 420px), 100%);
  transform: translateX(-100%);
  border-right: 1px solid var(--ds-line);
  border-radius: 0 var(--ds-r-xl) var(--ds-r-xl) 0;
}
.ds-drawer--left::before {
  inset: 0 0 0 auto;
  width: 1px;
  background: linear-gradient(180deg, transparent, var(--ds-line-strong), transparent);
}

.ds-drawer--bottom {
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  max-height: min(86vh, 780px);
  transform: translateY(100%);
  border-top: 1px solid var(--ds-line);
  border-radius: var(--ds-r-2xl) var(--ds-r-2xl) 0 0;
}
.ds-drawer--bottom::before {
  inset: 0 var(--ds-space-9) auto var(--ds-space-9);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--ds-line-strong), transparent);
}

/* pegador da folha inferior */
.ds-drawer--bottom::after {
  content: "";
  position: absolute;
  top: var(--ds-space-3);
  left: 50%;
  transform: translateX(-50%);
  width: var(--ds-space-8);
  height: 4px;
  border-radius: var(--ds-r-pill);
  background: var(--ds-line-strong);
}
.ds-drawer--bottom .ds-drawer-head { padding-top: var(--ds-space-7); }

/* estrutura interna: mesma gramatica do modal */
.ds-drawer-head {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-4);
  padding: var(--ds-space-6);
  border-bottom: 1px solid var(--ds-line-faint);
}
.ds-drawer-title {
  margin: 0;
  flex: 1 1 auto;
  font-family: var(--ds-display);
  font-size: var(--ds-fs-lg);
  line-height: var(--ds-lh-snug);
  letter-spacing: var(--ds-track-tight);
  color: var(--ds-text-1);
}
.ds-drawer-body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--ds-space-6);
  font-family: var(--ds-body);
  font-size: var(--ds-fs-base);
  line-height: var(--ds-lh-base);
  color: var(--ds-text-2);
  scrollbar-width: thin;
  scrollbar-color: var(--ds-line-strong) transparent;
}
.ds-drawer-body::-webkit-scrollbar { width: 6px; }
.ds-drawer-body::-webkit-scrollbar-thumb {
  background: var(--ds-line-strong);
  border-radius: var(--ds-r-pill);
}
.ds-drawer-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--ds-space-3);
  padding: var(--ds-space-4) var(--ds-space-6) var(--ds-space-6);
  border-top: 1px solid var(--ds-line-faint);
  background: var(--ds-surface-2);
}


/* ==========================================================================
   4. POPOVER e MENU — vidro ancorado
   Ficam um degrau abaixo do tooltip: um menu aberto pode cobrir um modal,
   mas nunca cobre uma dica.
   ========================================================================== */

.ds-popover {
  position: absolute;
  z-index: calc(var(--ds-z-tooltip) - 1);

  min-width: 200px;
  max-width: min(360px, 92vw);
  padding: var(--ds-space-4);

  background: var(--ds-glass-strong);
  -webkit-backdrop-filter: blur(var(--ds-blur-md)) saturate(130%);
  backdrop-filter: blur(var(--ds-blur-md)) saturate(130%);
  border: 1px solid var(--ds-line-strong);
  border-radius: var(--ds-r-lg);
  box-shadow: var(--ds-e3);

  font-family: var(--ds-body);
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-text-2);

  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px) scale(0.98);
  transform-origin: top center;
  transition:
    opacity var(--ds-dur-2) var(--ds-ease),
    transform var(--ds-dur-2) var(--ds-ease),
    visibility 0s linear var(--ds-dur-2);
}

.ds-popover.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0) scale(1);
  transition:
    opacity var(--ds-dur-2) var(--ds-ease),
    transform var(--ds-dur-2) var(--ds-ease),
    visibility 0s linear 0s;
}

/* popover ancorado acima do alvo cresce a partir de baixo */
.ds-popover--up {
  transform: translateY(6px) scale(0.98);
  transform-origin: bottom center;
}

.ds-popover-head {
  margin-bottom: var(--ds-space-3);
  padding-bottom: var(--ds-space-3);
  border-bottom: 1px solid var(--ds-line-faint);
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-micro);
  letter-spacing: var(--ds-track-label);
  text-transform: uppercase;
  color: var(--ds-text-3);
}

/* --- menu ---------------------------------------------------------------- */
.ds-menu {
  position: relative;
  z-index: calc(var(--ds-z-tooltip) - 1);

  min-width: 208px;
  margin: 0;
  padding: var(--ds-space-2);
  list-style: none;

  background: var(--ds-glass-strong);
  -webkit-backdrop-filter: blur(var(--ds-blur-md)) saturate(130%);
  backdrop-filter: blur(var(--ds-blur-md)) saturate(130%);
  border: 1px solid var(--ds-line-strong);
  border-radius: var(--ds-r-lg);
  box-shadow: var(--ds-e3);
}

/* menu dentro de popover: o popover ja' e' a superficie, o menu vira so' ritmo */
.ds-popover > .ds-menu {
  min-width: 0;
  padding: 0;
  background: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.ds-menu-label {
  padding: var(--ds-space-3) var(--ds-space-3) var(--ds-space-2);
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-micro);
  letter-spacing: var(--ds-track-label);
  text-transform: uppercase;
  color: var(--ds-text-3);
}

.ds-menu-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  width: 100%;
  padding: var(--ds-space-3) var(--ds-space-3);

  background: none;
  border: 0;
  border-radius: var(--ds-r-sm);
  text-align: left;
  text-decoration: none;
  cursor: pointer;

  font-family: var(--ds-body);
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-snug);
  color: var(--ds-text-2);

  transition:
    background-color var(--ds-dur-1) var(--ds-ease),
    color var(--ds-dur-1) var(--ds-ease),
    box-shadow var(--ds-dur-1) var(--ds-ease);
}

.ds-menu-item:hover {
  background: var(--ds-surface-raised);
  color: var(--ds-text-1);
}

.ds-menu-item:focus-visible {
  outline: none;
  background: var(--ds-surface-raised);
  color: var(--ds-text-1);
  box-shadow: var(--ds-ring-blue);
}

/* selecao = azul (dado neutro), com trilho tramado na esquerda */
.ds-menu-item.is-active {
  color: var(--ds-text-1);
  background: var(--ds-surface-2);
}
.ds-menu-item.is-active::before {
  content: "";
  position: absolute;
  left: 0;
  top: var(--ds-space-2);
  bottom: var(--ds-space-2);
  width: 2px;
  border-radius: var(--ds-r-pill);
  background:
    repeating-linear-gradient(
      180deg,
      var(--ds-blue) 0 2px,
      transparent 2px var(--ds-mesh-size)
    );
}

.ds-menu-item:disabled,
.ds-menu-item.is-disabled {
  color: var(--ds-text-4);
  pointer-events: none;
  cursor: default;
}

/* atalho/valor a direita do item */
.ds-menu-hint {
  margin-left: auto;
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-micro);
  letter-spacing: var(--ds-track-label);
  text-transform: uppercase;
  color: var(--ds-text-4);
}

.ds-menu-sep {
  height: 1px;
  margin: var(--ds-space-2) var(--ds-space-2);
  border: 0;
  background: var(--ds-line);
}


/* ==========================================================================
   5. TOOLTIP — dica curta
   Fica na camada mais alta do sistema. Opaco de proposito: com vidro, a seta
   sobreposta ao corpo criaria uma emenda de alpha visivel.

   O tooltip padrao e' ROSA: e' a dica, e' informacao de ATENCAO/destaque, e o
   rosa e' o que da presenca sem entrar na semantica de lucro (verde) ou erro
   (vermelho). Para a dica discreta de antes existe .ds-tooltip--neutro.
   ========================================================================== */

.ds-tooltip {
  /* CONTRASTE — a conta esta escrita aqui de proposito, pra ninguem
     "consertar" isto pro rosa puro depois e quebrar a acessibilidade:
       var(--ds-atencao) (--ds-pink)      sobre texto branco = 4,01:1  reprova ate' no AA
       var(--ds-atencao) (--ds-pink)      sobre texto preto  = 5,23:1  reprova no AAA
       var(--ds-atencao) (--ds-pink-deep) sobre texto branco = 7,59:1  passa no AAA (7:1)
     Nenhuma combinacao do rosa PURO alcanca 7:1. Por isso o fundo (que carrega
     texto) e' --ds-pink-deep, e o rosa puro fica so' na moldura e no halo, que
     nao carregam texto nenhum. */
  --_ds-tip-fundo: var(--ds-atencao);
  --_ds-tip-linha: color-mix(in srgb, var(--ds-atencao) 62%, var(--ds-atencao));

  position: absolute;
  z-index: var(--ds-z-tooltip);

  /* LARGURA — o conserto do bug de "frase curta em 6 linhas".
     .ds-tooltip e' position:absolute e nao tinha largura propria. O bloco que
     o contem e' o wrapper do gatilho (um botao de ~80px), entao o algoritmo de
     shrink-to-fit resolvia a largura pela palavra mais longa e a frase quebrava
     linha por linha. max-content pede a largura da frase INTEIRA em uma linha;
     o max-width so' entra em acao quando a frase e' realmente longa.
     Se trocar isto por width:auto, o bug volta. */
  width: max-content;
  max-width: min(260px, calc(100vw - var(--ds-space-8)));
  padding: var(--ds-space-2) var(--ds-space-3);
  white-space: normal;
  overflow-wrap: anywhere;
  text-wrap: pretty;   /* quando quebra, quebra equilibrado (progressivo) */

  background: var(--_ds-tip-fundo);
  border: 1px solid var(--_ds-tip-linha);
  border-radius: var(--ds-r-sm);
  box-shadow: var(--ds-e2), 0 12px 32px -20px var(--ds-glow-atencao);

  font-family: var(--ds-body);
  font-size: var(--ds-fs-xs);
  line-height: var(--ds-lh-snug);
  color: var(--ds-white);

  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--ds-dur-1) var(--ds-ease),
    transform var(--ds-dur-1) var(--ds-ease),
    visibility 0s linear var(--ds-dur-1);
}

.ds-tooltip.is-open {
  opacity: 1;
  visibility: visible;
  transform: translate(0, 0);
  transition:
    opacity var(--ds-dur-1) var(--ds-ease),
    transform var(--ds-dur-1) var(--ds-ease),
    visibility 0s linear 0s;
}

/* dado numerico dentro da dica sai em mono */
.ds-tooltip-val {
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-xs);
  letter-spacing: var(--ds-track-tight);
  color: var(--ds-white);
}
.ds-tooltip-label {
  display: block;
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-micro);
  letter-spacing: var(--ds-track-label);
  text-transform: uppercase;
  /* sobre o rosa profundo, --ds-text-3 (38% de branco) sumiria. 76% mantem a
     hierarquia (label mais fraco que o valor) sem cair abaixo do legivel. */
  color: color-mix(in srgb, var(--ds-white) 76%, transparent);
}

/* Dica discreta: o tooltip grafite de antes, pra quando ja' existir rosa por
   perto e uma segunda mancha rosa virar ruido. */
.ds-tooltip--neutro {
  --_ds-tip-fundo: var(--ds-surface-raised);
  --_ds-tip-linha: var(--ds-line-strong);
  color: var(--ds-text-1);
  box-shadow: var(--ds-e2);
}
.ds-tooltip--neutro .ds-tooltip-val { color: var(--ds-text-1); }
.ds-tooltip--neutro .ds-tooltip-label { color: var(--ds-text-3); }

/* seta: quadrado girado, herda o MESMO fundo e a mesma linha do corpo pelas
   variaveis internas — e' o que faz a seta acompanhar a cor nas 4 posicoes
   (e continuar acompanhando em .ds-tooltip--neutro, sem regra extra). */
.ds-tooltip::after {
  content: "";
  position: absolute;
  width: 8px;
  height: 8px;
  background: var(--_ds-tip-fundo);
  border: 1px solid var(--_ds-tip-linha);
}

/* --top = dica ACIMA do alvo, seta apontando pra baixo */
.ds-tooltip--top { transform: translateY(4px); }
.ds-tooltip--top::after {
  left: 50%;
  bottom: -5px;
  transform: translateX(-50%) rotate(45deg);
  border-top: 0;
  border-left: 0;
}

.ds-tooltip--bottom { transform: translateY(-4px); }
.ds-tooltip--bottom::after {
  left: 50%;
  top: -5px;
  transform: translateX(-50%) rotate(45deg);
  border-bottom: 0;
  border-right: 0;
}

.ds-tooltip--left { transform: translateX(4px); }
.ds-tooltip--left::after {
  top: 50%;
  right: -5px;
  transform: translateY(-50%) rotate(45deg);
  border-bottom: 0;
  border-left: 0;
}

.ds-tooltip--right { transform: translateX(-4px); }
.ds-tooltip--right::after {
  top: 50%;
  left: -5px;
  transform: translateY(-50%) rotate(45deg);
  border-top: 0;
  border-right: 0;
}


/* ==========================================================================
   6. TOAST — aviso efemero
   --_ds-toast-cor e' o unico neon da peca: pinta icone, trilho de tempo e o
   halo. Nunca dois acentos no mesmo toast.
   SEMANTICA: --ok usa MENTA, nao verde. Verde e' conversao/lucro, e' veto da
   marca; "salvo com sucesso" nao e' lucro. Pra evento de conversao existe
   .ds-toast--lucro, ai' sim verde.
   ========================================================================== */

.ds-toast-stack {
  position: fixed;
  top: var(--ds-space-6);
  right: var(--ds-space-6);
  z-index: var(--ds-z-toast);

  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
  width: min(380px, calc(100vw - var(--ds-space-8)));

  pointer-events: none;   /* a pilha nao bloqueia o dashboard */
}
.ds-toast-stack > * { pointer-events: auto; }

.ds-toast-stack--left { right: auto; left: var(--ds-space-6); }
.ds-toast-stack--bottom {
  top: auto;
  bottom: var(--ds-space-6);
  flex-direction: column-reverse;
}

.ds-toast {
  --_ds-toast-cor: var(--ds-blue);
  --_ds-toast-tempo: 5s;   /* duracao real do aviso; ver NOTA no topo */

  position: relative;
  overflow: hidden;

  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: var(--ds-space-3);
  padding: var(--ds-space-4);

  background: var(--ds-surface-raised);
  border: 1px solid var(--ds-line);
  border-radius: var(--ds-r-md);
  box-shadow: var(--ds-e3), 0 24px 60px -46px var(--_ds-toast-cor);

  font-family: var(--ds-body);
  animation: ds-toast-entra var(--ds-dur-3) var(--ds-ease) both;
}

/* fio de luz no topo, neutro (o neon do toast e' o trilho de tempo) */
.ds-toast::before {
  content: "";
  position: absolute;
  inset: 0 var(--ds-space-6) auto var(--ds-space-6);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--ds-line-strong), transparent);
}

/* trilho de tempo: escoa da esquerda pra direita e some junto com o toast */
.ds-toast::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  transform-origin: left center;
  background:
    repeating-linear-gradient(
      90deg,
      var(--_ds-toast-cor) 0 2px,
      transparent 2px var(--ds-mesh-size)
    );
  animation: ds-toast-tempo var(--_ds-toast-tempo) linear forwards;
}

/* toast que espera acao do usuario: sem contagem */
.ds-toast--persistente::after { content: none; }

.ds-toast.is-leaving {
  animation: ds-toast-sai var(--ds-dur-2) var(--ds-ease) forwards;
}

.ds-toast-icon {
  width: var(--ds-space-5);
  height: var(--ds-space-5);
  display: grid;
  place-items: center;
  border-radius: var(--ds-r-pill);
  color: var(--_ds-toast-cor);
  background: color-mix(in srgb, var(--_ds-toast-cor) 16%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--_ds-toast-cor) 32%, transparent);
  font-size: var(--ds-fs-xs);
  line-height: 1;
}

.ds-toast-title {
  margin: 0;
  font-family: var(--ds-display);
  font-size: var(--ds-fs-md);
  line-height: var(--ds-lh-snug);
  letter-spacing: var(--ds-track-tight);
  color: var(--ds-text-1);
}

.ds-toast-msg {
  margin: var(--ds-space-1) 0 0;
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-text-2);
}

/* linha de valor: o toast tambem serve pra reportar numero */
.ds-toast-val {
  display: block;
  margin-top: var(--ds-space-2);
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-xs);
  letter-spacing: var(--ds-track-label);
  color: var(--_ds-toast-cor);
}

.ds-toast--info { --_ds-toast-cor: var(--ds-blue); }
.ds-toast--ok   { --_ds-toast-cor: var(--ds-mint); }
.ds-toast--erro {
  --_ds-toast-cor: var(--ds-red);
  border-color: color-mix(in srgb, var(--ds-red) 34%, var(--ds-line));
  box-shadow: var(--ds-e3), 0 24px 70px -44px var(--ds-glow-red);
}
/* conversao/lucro confirmado — o unico verde permitido nesta familia */
.ds-toast--lucro {
  --_ds-toast-cor: var(--ds-green);
  box-shadow: var(--ds-e3), 0 24px 70px -44px var(--ds-glow-green);
}

@keyframes ds-toast-entra {
  from { opacity: 0; transform: translateX(16px) scale(0.97); }
  to   { opacity: 1; transform: translateX(0) scale(1); }
}
@keyframes ds-toast-sai {
  from { opacity: 1; transform: translateX(0) scale(1); max-height: 240px; }
  to   { opacity: 0; transform: translateX(16px) scale(0.97); max-height: 0; }
}
@keyframes ds-toast-tempo {
  from { transform: scaleX(1); }
  to   { transform: scaleX(0); }
}


/* ==========================================================================
   7. SKELETON — o conteudo antes do conteudo
   Fundo tramado (dither), nao cinza chapado: enquanto carrega, a interface ja'
   parece "dado". O brilho que varre e' o UNICO shimmer permitido no sistema.
   ========================================================================== */

.ds-skeleton {
  position: relative;
  overflow: hidden;
  display: block;
  width: 100%;
  border-radius: var(--ds-r-sm);

  background-color: var(--ds-surface-2);
  background-image: radial-gradient(var(--ds-mesh-dot) 0.5px, transparent 0.5px);
  background-size: var(--ds-mesh-size) var(--ds-mesh-size);

  /* nao ha' texto de verdade aqui; some pra leitor de tela via aria-hidden */
  -webkit-user-select: none;
  user-select: none;
}

.ds-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent,
    var(--ds-line-strong),
    transparent
  );
  transform: translateX(-100%);
  animation: ds-skeleton-brilho var(--ds-dur-4) var(--ds-ease-soft) infinite;
}

@keyframes ds-skeleton-brilho {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

.ds-skeleton--text {
  height: var(--ds-fs-xs);
  border-radius: var(--ds-r-pill);
}
/* ultima linha de paragrafo sempre curta, senao nao parece texto */
.ds-skeleton--text:last-child { width: 62%; }

.ds-skeleton--title {
  height: var(--ds-fs-2xl);
  width: 42%;
  border-radius: var(--ds-r-xs);
}

.ds-skeleton--card {
  min-height: calc(var(--ds-space-10) * 2);
  border-radius: var(--ds-r-lg);
}

.ds-skeleton--circle {
  width: var(--ds-space-8);
  height: var(--ds-space-8);
  aspect-ratio: 1;
  border-radius: var(--ds-r-pill);
}

/* pilha de linhas com respiro entre elas */
.ds-skeleton + .ds-skeleton { margin-top: var(--ds-space-3); }


/* ==========================================================================
   8. EMPTY — estado vazio
   Aceita arte de fundo por --_ds-empty-arte (url). A arte entra dessaturada,
   mascarada em degrade e coberta pela trama: nunca vira "banner de stock".
   ========================================================================== */

.ds-empty {
  --_ds-empty-arte: none;

  position: relative;
  overflow: hidden;
  isolation: isolate;

  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--ds-space-3);

  padding: var(--ds-space-9) var(--ds-space-6);
  background: var(--ds-surface);
  border: 1px solid var(--ds-line);
  border-radius: var(--ds-r-xl);
  box-shadow: var(--ds-e2);
}

/* arte opcional, monocromatica, sumindo pras bordas */
.ds-empty::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--_ds-empty-arte);
  background-size: cover;
  background-position: center;
  opacity: 0.24;
  mix-blend-mode: luminosity;
  -webkit-mask-image: radial-gradient(120% 90% at 50% 0%, var(--ds-white), transparent 72%);
  mask-image: radial-gradient(120% 90% at 50% 0%, var(--ds-white), transparent 72%);
}

/* trama por cima da arte: e' o que amarra a peca ao resto do sistema */
.ds-empty::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: radial-gradient(var(--ds-mesh-dot) 0.5px, transparent 0.5px);
  background-size: var(--ds-mesh-size) var(--ds-mesh-size);
  opacity: 0.6;
}

.ds-empty-arte {
  display: grid;
  place-items: center;
  width: var(--ds-space-9);
  height: var(--ds-space-9);
  margin-bottom: var(--ds-space-2);

  border-radius: var(--ds-r-pill);
  color: var(--ds-text-3);
  background: var(--ds-glass);
  -webkit-backdrop-filter: blur(var(--ds-blur-sm));
  backdrop-filter: blur(var(--ds-blur-sm));
  border: 1px solid var(--ds-line-strong);
}
.ds-empty-arte > svg,
.ds-empty-arte > img {
  width: var(--ds-space-6);
  height: var(--ds-space-6);
}

.ds-empty-kicker {
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-micro);
  letter-spacing: var(--ds-track-label);
  text-transform: uppercase;
  color: var(--ds-text-3);
}

.ds-empty-title {
  margin: 0;
  font-family: var(--ds-display);
  font-size: var(--ds-fs-xl);
  line-height: var(--ds-lh-snug);
  letter-spacing: var(--ds-track-tight);
  color: var(--ds-text-1);
}

.ds-empty-text {
  margin: 0;
  max-width: 46ch;
  font-family: var(--ds-body);
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-text-2);
}

/* slot de acao: so' posiciona, quem estiliza botao e' a outra familia */
.ds-empty-acao {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-3);
  margin-top: var(--ds-space-4);
}

/* vazio dentro de card/celula que ja' tem moldura */
.ds-empty--flat {
  background: none;
  border: 0;
  box-shadow: none;
  padding: var(--ds-space-7) var(--ds-space-5);
}
.ds-empty--compact {
  padding: var(--ds-space-6) var(--ds-space-5);
  gap: var(--ds-space-2);
}
.ds-empty--compact .ds-empty-arte {
  width: var(--ds-space-7);
  height: var(--ds-space-7);
}
.ds-empty--compact .ds-empty-title { font-size: var(--ds-fs-lg); }


/* --- 8.1 ARTE CHEIA — a imagem vira a JANELA ------------------------------
   O oposto do slot redondo acima. La' a imagem esta' DENTRO de uma moldura,
   com borda e vidro em volta: e' um icone. Aqui ela SANGRA — ocupa o card
   inteiro, e' a moldura do card que corta ela, e um degrade a funde na
   superficie pra o texto continuar legivel por cima.

   E' o mesmo gesto do .ds-scene (ver dash-shark.scene.css): recorte + camadas
   + a superficie nascendo de dentro da imagem em vez de um veu por cima. O
   olho le "isto continua fora do card" em vez de "puseram uma figurinha aqui".

   FUNCIONA DE DUAS FORMAS, e as duas ao mesmo tempo:
     a) <div class="ds-empty-arte"><img src="..." alt=""></div>  (o HTML de
        hoje continua valendo — a moldura simplesmente cai e a img estoura)
     b) style="--_ds-empty-arte: url(...)"  (sem elemento nenhum no HTML)

   Ordem de pintura, que e' o que faz o texto sobreviver: .ds-empty tem
   isolation:isolate, entao ::before (imagem por variavel), .ds-empty-arte
   (imagem por elemento) e ::after (degrade + trama) ficam todos em z-index:-1,
   pintados nessa ordem, e o conteudo em fluxo fica acima dos tres. */
.ds-empty--arte-cheia {
  /* o vazio de cima e' a imagem; o texto desce pro pe da janela */
  justify-content: flex-end;
  padding-top: calc(var(--ds-space-10) + var(--ds-space-8));
  min-height: calc(var(--ds-space-10) * 4);
}

/* a) imagem por variavel: perde a mascara radial e passa a cobrir tudo */
.ds-empty--arte-cheia::before {
  inset: 0;
  opacity: 0.62;
  background-position: center top;
  -webkit-mask-image: none;
  mask-image: none;
}

/* b) imagem por elemento: a moldura circular SOME e o <img> estoura.
   Nao usamos display:none aqui de proposito — o HTML que ja' existe continua
   funcionando, so' que agora a imagem dele e' o fundo da cena. */
.ds-empty--arte-cheia .ds-empty-arte {
  position: absolute;
  z-index: -1;
  inset: 0 0 auto 0;
  height: 68%;
  width: 100%;
  display: block;
  margin: 0;
  padding: 0;

  background: none;
  border: 0;
  border-radius: 0;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.ds-empty--arte-cheia .ds-empty-arte > img,
.ds-empty--arte-cheia .ds-empty-arte > svg {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  opacity: 0.62;
  /* dessaturada: a paleta do sistema e' a paleta, a foto nao vota */
  filter: grayscale(1) contrast(1.06);
}

/* O DEGRADE que salva a legibilidade. Comeca transparente no topo (a imagem
   respira) e chega em --ds-surface OPACO na altura do texto. Sem a ultima
   parada opaca o texto fica sobre imagem cinza e a leitura depende de qual
   foto o cliente subiu — que e' exatamente o que nao pode.
   A trama continua por cima, agora com o ponto rebaixado por color-mix em vez
   de por opacity, porque a opacity do ::after agora carrega o degrade junto. */
.ds-empty--arte-cheia::after {
  opacity: 1;
  background-image:
    linear-gradient(
      to bottom,
      transparent 0%,
      color-mix(in srgb, var(--ds-surface) 32%, transparent) 30%,
      color-mix(in srgb, var(--ds-surface) 78%, transparent) 50%,
      color-mix(in srgb, var(--ds-surface) 97%, transparent) 66%,
      var(--ds-surface) 78%
    ),
    radial-gradient(
      color-mix(in srgb, var(--ds-mesh-dot) 60%, transparent) 0.5px,
      transparent 0.5px);
  background-size: auto, var(--ds-mesh-size) var(--ds-mesh-size);
}

/* imagem no PE do card: mesma cena, virada. O degrade sobe em vez de descer. */
.ds-empty--arte-cheia.ds-empty--arte-pe {
  justify-content: flex-start;
  padding-top: var(--ds-space-9);
  padding-bottom: calc(var(--ds-space-10) + var(--ds-space-8));
}
.ds-empty--arte-cheia.ds-empty--arte-pe::before { background-position: center bottom; }
.ds-empty--arte-cheia.ds-empty--arte-pe .ds-empty-arte {
  inset: auto 0 0 0;
}
.ds-empty--arte-cheia.ds-empty--arte-pe .ds-empty-arte > img,
.ds-empty--arte-cheia.ds-empty--arte-pe .ds-empty-arte > svg {
  object-position: center bottom;
}
.ds-empty--arte-cheia.ds-empty--arte-pe::after {
  background-image:
    linear-gradient(
      to top,
      transparent 0%,
      color-mix(in srgb, var(--ds-surface) 32%, transparent) 30%,
      color-mix(in srgb, var(--ds-surface) 78%, transparent) 50%,
      color-mix(in srgb, var(--ds-surface) 97%, transparent) 66%,
      var(--ds-surface) 78%
    ),
    radial-gradient(
      color-mix(in srgb, var(--ds-mesh-dot) 60%, transparent) 0.5px,
      transparent 0.5px);
  background-size: auto, var(--ds-mesh-size) var(--ds-mesh-size);
}

/* dentro de .ds-empty--flat o fundo e' o card hospedeiro, nao --ds-surface:
   o degrade precisa terminar na cor de quem hospeda, senao aparece um degrau. */
.ds-empty--arte-cheia.ds-empty--flat::after {
  background-image:
    linear-gradient(
      to bottom,
      transparent 0%,
      color-mix(in srgb, var(--ds-canvas) 34%, transparent) 30%,
      color-mix(in srgb, var(--ds-canvas) 80%, transparent) 50%,
      color-mix(in srgb, var(--ds-canvas) 97%, transparent) 66%,
      var(--ds-canvas) 78%
    ),
    radial-gradient(
      color-mix(in srgb, var(--ds-mesh-dot) 60%, transparent) 0.5px,
      transparent 0.5px);
  background-size: auto, var(--ds-mesh-size) var(--ds-mesh-size);
}


/* ==========================================================================
   9. ERROR — estado de falha
   Aqui vermelho e' correto: falha e' perda. Under-glow vermelho vazando por
   baixo, e' o mesmo gesto do card — so' que com a cor do problema.
   ========================================================================== */

.ds-error {
  position: relative;
  overflow: hidden;
  /* segura a trama do ::after numa camada propria, abaixo do conteudo */
  isolation: isolate;

  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--ds-space-3);

  padding: var(--ds-space-9) var(--ds-space-6);
  background: var(--ds-surface);
  border: 1px solid color-mix(in srgb, var(--ds-red) 26%, var(--ds-line));
  border-radius: var(--ds-r-xl);
  box-shadow: var(--ds-e3), 0 50px 140px -70px var(--ds-glow-red);
}

/* trama vermelha bem baixa: "isto aqui esta' errado" sem virar alarme.
   z-index:-1 poe ela ATRAS do texto e do icone: o diagonal passando por cima
   das letras cansava a leitura justo na tela em que o usuario precisa ler. */
.ds-error::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.5;
  background-image:
    repeating-linear-gradient(
      45deg,
      color-mix(in srgb, var(--ds-red) 12%, transparent) 0 1px,
      transparent 1px calc(var(--ds-mesh-size) * 2)
    );
}

/* O SELO DO ICONE
   Vermelho aqui e' correto e e' o unico lugar decorativo em que ele aparece:
   falha e' perda. O disco e' o mesmo gesto do .ds-toast-icon — tinta de 14%,
   anel de 1px em 30% — pra os dois lerem como a mesma familia.

   Aceita tres formas de icone sem regra extra, porque o alinhamento e' feito
   por grid + place-items e o tamanho por font-size herdado:
     <i class="fa-solid fa-triangle-exclamation"></i>   (Font Awesome, fonte)
     <svg>...</svg>                                     (SVG inline)
     ✕                                                  (glifo de texto)
   Font Awesome NAO e' carregado por este CSS — o projeto que consome e' quem
   inclui a fonte. Aqui so' existe o berco: cor, tamanho, centragem. */
.ds-error-icon {
  display: grid;
  place-items: center;
  width: var(--ds-space-9);
  height: var(--ds-space-9);
  margin-bottom: var(--ds-space-2);
  flex: 0 0 auto;

  border-radius: var(--ds-r-pill);
  color: var(--ds-red);
  background: color-mix(in srgb, var(--ds-red) 14%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--ds-red) 30%, transparent);

  /* o <i> da Font Awesome e' TEXTO: quem dimensiona ele e' font-size, nao
     width/height. Sem isto o icone sai no tamanho do paragrafo, minusculo
     dentro de um disco de 56px. line-height:1 tira o chumbo da linha, que e'
     o que desalinha o glifo meio pixel pra baixo dentro do grid. */
  font-size: var(--ds-fs-xl);
  line-height: 1;
}
.ds-error-icon > i,
.ds-error-icon > span {
  display: block;
  font-size: inherit;
  line-height: 1;
  color: inherit;
}
.ds-error-icon > svg {
  display: block;
  width: var(--ds-space-6);
  height: var(--ds-space-6);
  fill: currentColor;
}

.ds-error-title {
  margin: 0;
  font-family: var(--ds-display);
  font-size: var(--ds-fs-xl);
  line-height: var(--ds-lh-snug);
  letter-spacing: var(--ds-track-tight);
  color: var(--ds-text-1);
}

.ds-error-text {
  margin: 0;
  max-width: 46ch;
  font-family: var(--ds-body);
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-text-2);
}

/* codigo tecnico (HTTP 502, id da requisicao) — mono, discreto, selecionavel */
.ds-error-code {
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-micro);
  letter-spacing: var(--ds-track-label);
  text-transform: uppercase;
  color: var(--ds-text-3);
  -webkit-user-select: all;
  user-select: all;
}

.ds-error-acao {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-3);
  margin-top: var(--ds-space-4);
}

/* falha de um pedaco so' (um widget do dashboard), em linha */
.ds-error--inline {
  flex-direction: row;
  text-align: left;
  align-items: center;
  justify-content: flex-start;
  gap: var(--ds-space-4);
  padding: var(--ds-space-4) var(--ds-space-5);
  border-radius: var(--ds-r-md);
  box-shadow: var(--ds-e1);
}
.ds-error--inline .ds-error-icon {
  width: var(--ds-space-7);
  height: var(--ds-space-7);
  margin-bottom: 0;
  flex: 0 0 auto;
  font-size: var(--ds-fs-sm);   /* disco menor pede glifo menor */
}
.ds-error--inline .ds-error-icon > svg {
  width: var(--ds-space-4);
  height: var(--ds-space-4);
}
.ds-error--inline .ds-error-title { font-size: var(--ds-fs-md); }
.ds-error--inline .ds-error-acao { margin: 0 0 0 auto; }


/* ==========================================================================
   10. LOADING — quatro esperas, quatro caracteres
   Anel de 2px feito com conic-gradient + mask (sem borda falsa de 4 lados).
   Azul: carregar e' estado informativo, nunca verde.

     .ds-loading            anel fino girando   — espera curta, ao lado de texto
     .ds-loading--barra     barra indeterminada — espera de area (topo de tabela)
     .ds-loading--trama     dither acendendo    — a assinatura da marca
     .ds-loading--pulso     anel expandindo     — espera calma, sem urgencia

   A cor sai de uma variavel interna so', entao qualquer loader aceita
   --_ds-loading-cor sem regra nova (ex.: rosa quando a espera e' de destaque).
   ========================================================================== */

.ds-loading {
  --_ds-loading-cor: var(--ds-blue);

  display: inline-block;
  width: var(--ds-space-6);
  height: var(--ds-space-6);
  aspect-ratio: 1;
  border-radius: var(--ds-r-pill);
  vertical-align: middle;

  background: conic-gradient(
    from 0deg,
    transparent 0deg 200deg,
    var(--_ds-loading-cor) 340deg
  );
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), var(--ds-white) 0);
  mask: radial-gradient(farthest-side, transparent calc(100% - 2px), var(--ds-white) 0);

  animation: ds-loading-gira var(--ds-dur-4) linear infinite;
}

@keyframes ds-loading-gira {
  to { transform: rotate(1turn); }
}

.ds-loading--sm { width: var(--ds-space-4); height: var(--ds-space-4); }
.ds-loading--lg { width: var(--ds-space-8); height: var(--ds-space-8); }

/* --- barra indeterminada -------------------------------------------------
   Trilho tramado + cursor de luz que atravessa. Serve pro topo de tabela,
   pro rodape de modal, pra qualquer espera sem porcentagem. */
.ds-loading--barra {
  position: relative;
  overflow: hidden;
  display: block;
  width: 100%;
  height: 2px;
  aspect-ratio: auto;
  border-radius: var(--ds-r-pill);

  background-image:
    repeating-linear-gradient(
      90deg,
      var(--ds-mesh-dot) 0 1px,
      transparent 1px var(--ds-mesh-size)
    );
  -webkit-mask: none;
  mask: none;
  animation: none;
}

.ds-loading--barra::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 38%;
  border-radius: var(--ds-r-pill);
  background: linear-gradient(90deg, transparent, var(--_ds-loading-cor), transparent);
  animation: ds-loading-corre var(--ds-dur-4) var(--ds-ease-soft) infinite;
}

@keyframes ds-loading-corre {
  from { transform: translateX(-100%); }
  to   { transform: translateX(320%); }
}

/* --- TRAMA: pontos de dither acendendo em sequencia ----------------------
   O loader da casa. A trama e' a assinatura do sistema (skeleton, scrim,
   estado vazio), e aqui ela vira o proprio movimento: uma grade de pontos
   apagados que acende da esquerda pra direita, como dado chegando.

   COMO FUNCIONA (e por que nao e' caro): o elemento inteiro e' MASCARADO pela
   grade de pontos, entao so' os pontos existem na tela. O fundo do elemento e'
   o ponto apagado; o ::after e' uma faixa de luz que atravessa por baixo dessa
   mascara. O que anima e' um transform do ::after — composto na GPU, sem
   repintura de layout. Animar mask-position ou background-position aqui daria
   o mesmo desenho e repintaria a cada quadro. */
.ds-loading--trama {
  position: relative;
  overflow: hidden;
  display: inline-block;
  width: var(--ds-space-9);
  height: var(--ds-space-4);
  aspect-ratio: auto;
  border-radius: 0;
  vertical-align: middle;

  /* o ponto "apagado": a cor existe, so' que fraca */
  background: color-mix(in srgb, var(--_ds-loading-cor) 26%, transparent);

  /* a grade de pontos, no mesmo passo da trama do resto do sistema */
  -webkit-mask-image: radial-gradient(
    circle at center,
    var(--ds-white) 1.1px,
    transparent 1.35px);
  mask-image: radial-gradient(
    circle at center,
    var(--ds-white) 1.1px,
    transparent 1.35px);
  -webkit-mask-size: calc(var(--ds-mesh-size) * 2) calc(var(--ds-mesh-size) * 2);
  mask-size: calc(var(--ds-mesh-size) * 2) calc(var(--ds-mesh-size) * 2);
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;

  animation: none;   /* quem gira e' o anel; este nao gira */
}

.ds-loading--trama::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--_ds-loading-cor) 42%,
    var(--_ds-loading-cor) 58%,
    transparent 100%
  );
  transform: translateX(-100%);
  animation: ds-loading-trama var(--ds-dur-4) var(--ds-ease-soft) infinite;
}

@keyframes ds-loading-trama {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

.ds-loading--trama.ds-loading--sm { width: var(--ds-space-7); height: var(--ds-space-3); }
.ds-loading--trama.ds-loading--lg { width: var(--ds-space-10); height: var(--ds-space-5); }

/* --- PULSO: anel que nasce e se expande -----------------------------------
   Espera longa e sem urgencia (sincronizando conta, processando lote). Dois
   aneis defasados em meio ciclo: sempre ha' um saindo enquanto outro nasce, e
   e' isso que faz o movimento parecer continuo em vez de piscar.
   ATENCAO: usa ::before E ::after do proprio .ds-loading. Nao combine com
   --barra nem com --trama, que tambem usam ::after — os blocos nao se anulam,
   eles se fundem pela cascata. */
.ds-loading--pulso {
  position: relative;
  background: none;
  -webkit-mask: none;
  mask: none;
  animation: none;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--_ds-loading-cor) 26%, transparent);
}

.ds-loading--pulso::before,
.ds-loading--pulso::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--ds-r-pill);
  border: 1px solid var(--_ds-loading-cor);
  animation: ds-loading-pulso var(--ds-dur-4) var(--ds-ease-soft) infinite;
}
/* meio ciclo de defasagem — duracao vem do token, entao zera junto com ele */
.ds-loading--pulso::after { animation-delay: calc(var(--ds-dur-4) / 2); }

@keyframes ds-loading-pulso {
  from { transform: scale(0.35); opacity: 0.85; }
  to   { transform: scale(1);    opacity: 0; }
}

/* legenda mono ao lado do spinner */
.ds-loading-label {
  display: inline-block;
  margin-left: var(--ds-space-3);
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-micro);
  letter-spacing: var(--ds-track-label);
  text-transform: uppercase;
  color: var(--ds-text-3);
  vertical-align: middle;
}


/* ==========================================================================
   11. BANNER — faixa de aviso no topo do conteudo
   Trilho SOLIDO na esquerda carrega a cor; o texto ganha marca-texto.
   --alerta usa ROSA, nao vermelho: vermelho e' perda/erro, ponto.
   Nao existe ambar/amarelo neste sistema (ver tokens).

   DUAS COISAS QUE PARECEM UMA SO' E NAO SAO:
     --_ds-banner-cor    o rosa PURO. Trilho, borda, icone, halo — tudo que e'
                         linha e luz, e nao carrega texto por cima.
     --_ds-banner-fill   o rosa PROFUNDO. Fundo do marca-texto, o unico lugar
                         onde a cor fica ATRAS de letra.
   CONTRASTE, com os numeros, pra ninguem unificar as duas depois:
     var(--ds-atencao) (--ds-pink)      + texto branco = 4,01:1  reprova ate' no AA (4,5)
     var(--ds-atencao) (--ds-pink)      + texto preto  = 5,23:1  reprova no AAA (7,0)
     var(--ds-atencao) (--ds-pink-deep) + texto branco = 7,59:1  passa no AAA
   O rosa puro NAO pode virar fundo de texto. Se alguem trocar --_ds-banner-fill
   por var(--ds-atencao) "pra ficar mais vivo", o banner cai de 7,59:1 pra 4,01:1.

   O tinte lavado que existia no fundo da faixa saiu: com o trilho solido e o
   marca-texto, ele so' sujava o preto sem carregar informacao nenhuma.
   ========================================================================== */

.ds-banner {
  --_ds-banner-cor:  var(--ds-blue);
  --_ds-banner-fill: var(--ds-blue);

  position: relative;
  overflow: hidden;

  display: flex;
  align-items: center;
  gap: var(--ds-space-4);
  padding: var(--ds-space-4) var(--ds-space-5);

  background: var(--ds-surface-2);
  border: 1px solid var(--ds-line);
  border-color: color-mix(in srgb, var(--_ds-banner-cor) 22%, var(--ds-line));
  border-radius: var(--ds-r-md);

  font-family: var(--ds-body);
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-text-2);
}

/* Trilho de cor a esquerda, SOLIDO.
   Era tramado (repeating-linear-gradient de 2px em 3px), e a trama num filete
   de 3px nao le como textura — le como cor apagada, porque metade do trilho e'
   preto. A trama continua no sistema onde ela tem area pra se ler: skeleton,
   scrim, estado vazio, trilho de progresso. Aqui ela custava a presenca da
   marca e nao devolvia nada. */
.ds-banner::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--_ds-banner-cor);
}

/* --- MARCA-TEXTO ---------------------------------------------------------
   Marca-texto de verdade: e' um INLINE com box-decoration-break: clone, entao
   o fill acompanha cada linha do texto como caneta grifa — nao e' um retangulo
   do bloco inteiro. Sem o clone, uma frase de duas linhas ganha um unico bloco
   torto que vaza pro fim da primeira linha.
   O padding lateral tem contrapeso em margin negativa pra letra grifada
   continuar alinhada com o resto do paragrafo. */
.ds-banner-mark {
  display: inline;
  padding: 0.16em 0.42em;
  margin-inline: -0.06em;
  background: var(--_ds-banner-fill);
  color: var(--ds-white);
  border-radius: var(--ds-r-xs);
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* Faixa com o titulo ja' grifado, sem precisar de span extra no HTML.
   O titulo e' bloco e cabe em uma linha, entao width:fit-content produz
   exatamente o mesmo desenho do clone — para TEXTO CORRIDO de varias linhas
   use .ds-banner-mark, que e' o inline. */
.ds-banner--marcado .ds-banner-title {
  width: -moz-fit-content;
  width: fit-content;
  padding: 0.16em 0.42em;
  margin-bottom: var(--ds-space-1);
  background: var(--_ds-banner-fill);
  color: var(--ds-white);
  border-radius: var(--ds-r-xs);
}

.ds-banner-icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: var(--ds-space-5);
  height: var(--ds-space-5);
  color: var(--_ds-banner-cor);
}

.ds-banner-text { flex: 1 1 auto; margin: 0; }

.ds-banner-title {
  display: block;
  font-family: var(--ds-display);
  font-size: var(--ds-fs-base);
  letter-spacing: var(--ds-track-tight);
  color: var(--ds-text-1);
}

.ds-banner-acao {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
}

.ds-banner--info {
  --_ds-banner-cor: var(--ds-blue);
  /* azul puro com branco da' 3,6:1; escurecido pela superficie chega no faixa
     de AAA junto com o branco. Mesma logica do rosa, mesmo remedio. */
  --_ds-banner-fill: color-mix(in srgb, var(--ds-blue) 62%, var(--ds-black));
}
/* ATENCAO/novidade = rosa. Trilho no rosa PURO, grifo no rosa PROFUNDO. */
.ds-banner--alerta {
  --_ds-banner-cor:  var(--ds-atencao);
  --_ds-banner-fill: var(--ds-atencao);
}
.ds-banner--erro {
  --_ds-banner-cor: var(--ds-red);
  --_ds-banner-fill: color-mix(in srgb, var(--ds-red) 58%, var(--ds-black));
  box-shadow: 0 24px 70px -56px var(--ds-glow-red);
}

/* faixa colada no topo da area de conteudo */
.ds-banner--sticky {
  position: sticky;
  top: 0;
  z-index: var(--ds-z-sticky);
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  -webkit-backdrop-filter: blur(var(--ds-blur-sm));
  backdrop-filter: blur(var(--ds-blur-sm));
}


/* ==========================================================================
   12. SPLASH e TRANSICOES DE PAGINA
   A primeira coisa que o usuario ve, e a costura entre uma tela e a proxima.
   ----------------------------------------------------------------------------
   O splash aplica o conceito de JANELA (ver dash-shark.scene.css): preto,
   trama, e UM neon nascendo do horizonte de dentro da tela — nao um veu
   uniforme por cima. E ele sai como a cena se afastando: fade + scale.

   REDE DE SEGURANCA — a regra que nao pode ser removida.
   Um splash e' a unica peca deste arquivo capaz de deixar o produto
   INUTILIZAVEL sozinha: se o JS que tira ele nunca rodar (erro de script,
   bundle que nao carregou, rede que caiu no meio), o usuario fica olhando uma
   tela preta pra sempre. Por isso as duas pecas desta secao se auto-dispensam
   por CSS depois de um limite (--_ds-splash-limite / --_ds-page-limite),
   independentemente do JS.

   Esses dois valores sao a segunda excecao documentada a "toda duracao vem de
   var(--ds-dur-*)" — a primeira e' o trilho de tempo do toast. Motivo igual:
   nao e' ritmo de animacao, e' um prazo. Se virasse --ds-dur-3 (400ms), o
   splash sumiria antes da pagina carregar; e ele NAO pode zerar em
   prefers-reduced-motion, senao o splash nem chega a aparecer.
   ========================================================================== */

.ds-splash {
  --_ds-splash-cor: var(--ds-blue);
  --_ds-splash-limite: 6s;   /* prazo da rede de seguranca; ver nota acima */

  position: fixed;
  inset: 0;
  z-index: calc(var(--ds-z-tooltip) + 1);   /* acima de tudo, ate' da dica */
  overflow: hidden;
  isolation: isolate;

  display: grid;
  place-items: center;
  padding: var(--ds-space-6);

  background: var(--ds-canvas);

  opacity: 1;
  transform: scale(1);
  transition:
    opacity var(--ds-dur-3) var(--ds-ease),
    transform var(--ds-dur-3) var(--ds-ease),
    visibility 0s linear var(--ds-dur-3);

  /* a rede de seguranca: sem 'both', nada e' aplicado durante o atraso, entao
     o .is-done do JS continua sendo o caminho normal e esta animacao so'
     acontece se o JS falhar */
  animation: ds-splash-sai var(--ds-dur-3) var(--ds-ease) var(--_ds-splash-limite) forwards;
}

/* a luz do horizonte, nascendo DENTRO da tela (o gesto do .ds-scene-sky) */
.ds-splash::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(
      150% 54% at 50% 108%,
      color-mix(in srgb, var(--_ds-splash-cor) 26%, transparent) 0%,
      color-mix(in srgb, var(--_ds-splash-cor) 10%, transparent) 30%,
      color-mix(in srgb, var(--_ds-splash-cor) 3%, transparent) 56%,
      transparent 80%
    );
}

/* a trama por cima da luz: e' o que amarra a tela de carregamento ao resto */
.ds-splash::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.7;
  background-image: radial-gradient(var(--ds-mesh-dot) 0.5px, transparent 0.5px);
  background-size: var(--ds-mesh-size) var(--ds-mesh-size);
}

/* o estado que o JS liga quando a aplicacao esta' pronta */
.ds-splash.is-done {
  animation: none;   /* o JS chegou antes do prazo; a rede nao precisa disparar */
  opacity: 0;
  transform: scale(1.03);
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--ds-dur-3) var(--ds-ease),
    transform var(--ds-dur-3) var(--ds-ease),
    visibility 0s linear var(--ds-dur-3);
}

@keyframes ds-splash-sai {
  to {
    opacity: 0;
    transform: scale(1.03);
    visibility: hidden;
    pointer-events: none;
  }
}

/* conteudo do splash, acima das duas camadas decorativas */
.ds-splash-corpo {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--ds-space-5);
  max-width: 32ch;
}

/* slot da marca (logo em svg/img). O halo e' o mesmo under-glow da casa:
   spread negativo, a luz vaza por baixo em vez de circundar. */
.ds-splash-marca {
  display: grid;
  place-items: center;
  filter: drop-shadow(0 18px 40px color-mix(in srgb, var(--_ds-splash-cor) 34%, transparent));
}
.ds-splash-marca > img,
.ds-splash-marca > svg {
  display: block;
  width: auto;
  max-width: min(220px, 56vw);
  height: auto;
}

.ds-splash-label {
  font-family: var(--ds-mono);
  font-size: var(--ds-fs-micro);
  letter-spacing: var(--ds-track-label);
  text-transform: uppercase;
  color: var(--ds-text-3);
}

/* barra de espera do splash: reaproveita o desenho de .ds-loading--barra
   sem herdar dele, porque aqui ela tem largura de tela e nao de componente */
.ds-splash-barra {
  position: relative;
  overflow: hidden;
  width: min(240px, 62vw);
  height: 2px;
  border-radius: var(--ds-r-pill);
  background-image:
    repeating-linear-gradient(
      90deg,
      var(--ds-mesh-dot) 0 1px,
      transparent 1px var(--ds-mesh-size)
    );
}
.ds-splash-barra::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 38%;
  border-radius: var(--ds-r-pill);
  background: linear-gradient(90deg, transparent, var(--_ds-splash-cor), transparent);
  animation: ds-loading-corre var(--ds-dur-4) var(--ds-ease-soft) infinite;
}

/* splash de destaque (primeiro acesso, lancamento): o neon vira rosa.
   Rosa PURO aqui e' seguro: ele e' luz e halo, nao fundo de texto nenhum. */
.ds-splash--energia { --_ds-splash-cor: var(--ds-atencao); }
/* splash de conversao confirmada (pos-pagamento) — o verde permitido */
.ds-splash--lucro   { --_ds-splash-cor: var(--ds-green); }


/* --- TRANSICAO DE CONTEUDO / ROTA ----------------------------------------
   A troca de tela nao e' um corte seco: o que sai recua e desfoca, o que entra
   chega focando. Mesmo vocabulario do modal (y + scale + blur), so' que mais
   curto — transicao de rota que se faz notar cansa em cinco cliques.

   .ds-page-enter  poe no conteudo NOVO. Seguro por natureza: o repouso e' o
                   conteudo visivel, a animacao so' o traz.
   .ds-page-leave  poe no conteudo VELHO, e so' quando voce for mesmo remover
                   ou substituir o no. Como ele termina invisivel, tem a mesma
                   rede de seguranca do splash: passado --_ds-page-limite o
                   conteudo REAPARECE sozinho. Melhor uma transicao feia que
                   uma tela em branco. */

.ds-page-enter {
  animation: ds-page-entra var(--ds-dur-3) var(--ds-ease) both;
}

.ds-page-leave {
  --_ds-page-limite: 4s;   /* prazo da rede de seguranca; ver nota da secao */

  animation:
    ds-page-sai var(--ds-dur-2) var(--ds-ease) forwards,
    ds-page-resgata 0s linear var(--_ds-page-limite) forwards;
  pointer-events: none;   /* nao aceita clique enquanto esta saindo */
}

@keyframes ds-page-entra {
  from { opacity: 0; transform: translateY(10px) scale(0.995); filter: blur(4px); }
  to   { opacity: 1; transform: translateY(0) scale(1);        filter: blur(0px); }
}

@keyframes ds-page-sai {
  from { opacity: 1; transform: translateY(0) scale(1);         filter: blur(0px); }
  to   { opacity: 0; transform: translateY(-8px) scale(0.995);  filter: blur(4px); }
}

/* a rede: se o no' nao foi removido no prazo, ele volta a existir */
@keyframes ds-page-resgata {
  to { opacity: 1; transform: none; filter: none; }
}

/* variante lateral, pra navegacao entre abas irmas (esquerda/direita) */
.ds-page-enter--dir { animation-name: ds-page-entra-dir; }
.ds-page-leave--dir { animation-name: ds-page-sai-dir, ds-page-resgata; }

@keyframes ds-page-entra-dir {
  from { opacity: 0; transform: translateX(18px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes ds-page-sai-dir {
  from { opacity: 1; transform: translateX(0); }
  to   { opacity: 0; transform: translateX(-18px); }
}


/* ==========================================================================
   13. FECHAR — o botao de dispensa compartilhado
   Pill de vidro. Vive dentro de modal/drawer/toast/banner; nao e' o botao do
   sistema, e' o "x" da propria peca.
   ========================================================================== */

.ds-modal-close,
.ds-drawer-close,
.ds-toast-close,
.ds-banner-close {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: var(--ds-space-7);
  height: var(--ds-space-7);
  padding: 0;

  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;

  background: var(--ds-glass);
  -webkit-backdrop-filter: blur(var(--ds-blur-sm));
  backdrop-filter: blur(var(--ds-blur-sm));
  border: 1px solid var(--ds-line);
  border-radius: var(--ds-r-pill);
  color: var(--ds-text-3);
  font-size: var(--ds-fs-sm);
  line-height: 1;

  transition:
    color var(--ds-dur-1) var(--ds-ease),
    border-color var(--ds-dur-1) var(--ds-ease),
    background-color var(--ds-dur-1) var(--ds-ease),
    box-shadow var(--ds-dur-1) var(--ds-ease);
}

.ds-modal-close:hover,
.ds-drawer-close:hover,
.ds-toast-close:hover,
.ds-banner-close:hover {
  color: var(--ds-text-1);
  border-color: var(--ds-line-strong);
  background: var(--ds-surface-raised);
}

.ds-modal-close:focus-visible,
.ds-drawer-close:focus-visible,
.ds-toast-close:focus-visible,
.ds-banner-close:focus-visible {
  outline: none;
  color: var(--ds-text-1);
  box-shadow: var(--ds-ring-blue);
}

.ds-modal-close:disabled,
.ds-drawer-close:disabled,
.ds-toast-close:disabled,
.ds-banner-close:disabled {
  color: var(--ds-text-4);
  cursor: default;
  pointer-events: none;
}


/* ==========================================================================
   14. RESPONSIVO
   767px e' o mesmo corte dos tokens (onde os blur zeram). Manter em sincronia.
   ========================================================================== */

@media (max-width: 767px) {
  /* modal vira folha inferior: mais alcance de polegar, menos viagem de olho */
  .ds-scrim { padding: 0; align-items: flex-end; }

  .ds-modal,
  .ds-modal--sm,
  .ds-modal--lg {
    width: 100%;
    max-height: 88vh;
    border-radius: var(--ds-r-2xl) var(--ds-r-2xl) 0 0;
    border-bottom: 0;
    transform: translateY(24px) scale(1);
  }
  .ds-modal-foot { border-radius: 0; }

  /* gaveta lateral ocupa a tela toda, sem quinas internas */
  .ds-drawer--right,
  .ds-drawer--left {
    width: 100%;
    border-radius: 0;
  }

  .ds-toast-stack {
    top: var(--ds-space-4);
    right: var(--ds-space-4);
    left: var(--ds-space-4);
    width: auto;
  }
  .ds-toast-stack--bottom {
    top: auto;
    bottom: var(--ds-space-4);
  }

  .ds-popover,
  .ds-menu { max-width: calc(100vw - var(--ds-space-8)); }

  /* a dica nunca pode ficar mais larga que a tela — com width:max-content ela
     tentaria caber a frase inteira em uma linha e vazaria pela direita */
  .ds-tooltip { max-width: calc(100vw - var(--ds-space-8)); }

  .ds-empty,
  .ds-error { padding: var(--ds-space-7) var(--ds-space-5); }

  /* a arte cheia perde area util em tela estreita: menos altura de imagem,
     menos padding morto, pra o texto nao ser empurrado pra fora do card */
  .ds-empty--arte-cheia {
    padding-top: calc(var(--ds-space-9) + var(--ds-space-6));
    min-height: calc(var(--ds-space-10) * 3);
  }
  .ds-empty--arte-cheia .ds-empty-arte { height: 56%; }
  .ds-empty--arte-cheia.ds-empty--arte-pe {
    padding-bottom: calc(var(--ds-space-9) + var(--ds-space-6));
  }

  .ds-splash { padding: var(--ds-space-5); }

  /* faixa em coluna: acao nao espreme o texto */
  .ds-banner {
    flex-wrap: wrap;
    row-gap: var(--ds-space-3);
  }
  .ds-banner-acao { margin-left: calc(var(--ds-space-5) + var(--ds-space-2)); }

  .ds-error--inline { flex-wrap: wrap; }
  .ds-error--inline .ds-error-acao { margin-left: 0; }
}


/* ==========================================================================
   15. MOVIMENTO REDUZIDO
   As duracoes ja' zeram nos tokens. O que precisa de trava extra aqui sao as
   animacoes INFINITAS: com duracao 0 elas viram loop de iteracoes de tamanho
   zero, o que alguns browsers tratam mal. Entao aqui a gente desliga de vez e
   deixa um estado estatico legivel no lugar.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .ds-skeleton::after {
    animation: none;
    opacity: 0.35;
    transform: none;
    background: linear-gradient(90deg, transparent, var(--ds-line), transparent);
  }

  .ds-loading {
    animation: none;
    background: conic-gradient(from 0deg, var(--_ds-loading-cor) 0deg 360deg);
    opacity: 0.5;
  }

  .ds-loading--barra::after {
    animation: none;
    width: 100%;
    background: var(--_ds-loading-cor);
    opacity: 0.5;
  }

  .ds-splash-barra::after {
    animation: none;
    width: 100%;
    background: var(--_ds-splash-cor);
    opacity: 0.5;
  }

  /* trama: os pontos param acesos, no lugar de acender em sequencia. Continua
     lendo como "carregando" porque o desenho ja' e' o dither da marca. */
  .ds-loading--trama::after {
    animation: none;
    transform: none;
    background: var(--_ds-loading-cor);
    opacity: 0.6;
  }

  /* pulso: fica um anel parado, sem nascer nem morrer */
  .ds-loading--pulso::before {
    animation: none;
    transform: none;
    opacity: 0.55;
  }
  .ds-loading--pulso::after { animation: none; display: none; }

  /* o trilho de tempo do toast permanece: ele e' informacao (quanto falta),
     nao decoracao. Mas fica linear e sem entrada/saida animada. */
  .ds-toast { animation: none; }
  .ds-toast.is-leaving { animation: none; opacity: 0; }

  /* SPLASH: a saida vira corte seco, MAS a rede de seguranca continua de pe'.
     Repare que --_ds-splash-limite nao esta' aqui: se ele zerasse junto com as
     duracoes, o splash sumiria antes de aparecer, e quem pediu menos movimento
     acabaria vendo um flash preto — o oposto do pedido. */
  .ds-splash {
    transition: visibility 0s linear var(--ds-dur-3);
    animation: ds-splash-sai 0s linear var(--_ds-splash-limite) forwards;
  }
  .ds-splash.is-done { transform: none; }

  /* TRANSICAO DE ROTA: so' opacidade, sem deslocamento nem desfoque. A rede de
     resgate do --leave permanece pelo mesmo motivo do splash. */
  .ds-page-enter,
  .ds-page-enter--dir { animation: none; }
  .ds-page-leave,
  .ds-page-leave--dir {
    animation: ds-page-resgata 0s linear var(--_ds-page-limite) forwards;
    opacity: 0;
  }
}
