/* ==========================================================================
   CONPSI · Design System · COMPONENTES
   Camada 2 de 2. Depende de tokens.css — nenhum valor cru aqui.
   Regras de movimento: só transform e opacity; nunca `transition: all`;
   nunca `ease-in`; hover com motion sempre atrás de @media (hover: hover).
   ========================================================================== */

/* ==========================================================================
   VIDRO E O MODELO DE CAMADAS
   Esta é a ideia que organiza a biblioteca inteira. Uma regra só decide se
   algo é de vidro ou sólido:

        VIDRO é o que você OPERA.   SÓLIDO é o que você LÊ.

   Quatro camadas, de trás para frente:

   0 · CAMPO      o fundo com o véu da marca (--bg-wash). Não é decoração:
                  é o que as camadas de vidro refratam. Sem ele, todo o
                  efeito some.
   1 · CONTEÚDO   sólido, opaco. Card, tabela, formulário, gráfico — tudo
                  que a pessoa lê e de onde tira um número. Nunca de vidro:
                  desfoque atrás de dado é ruído em cima do que ela veio ler.
   2 · CROMO      vidro, flutuando sobre o conteúdo. Navegação, trilho, barra
                  de ferramentas, dock de ações. É o que a pessoa opera para
                  chegar ao conteúdo — e o que deve sumir de vista enquanto
                  ela lê.
   3 · SOBREPOSIÇÃO  vidro denso. Menu, tooltip, modal, drawer, toast, paleta.
                  Interrompe: exige mais opacidade porque carrega texto
                  próprio que precisa vencer o fundo.

   Três coisas juntas fazem parecer vidro, e nenhuma sozinha basta:
   o blur com saturate (reacende a cor que o blur lava), o fio de luz na borda
   de cima, e a sombra de contato embaixo.
   ========================================================================== */
.ds-glass {
  position: relative;
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
          backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow), inset 0 1px 0 0 oklch(1 0 0 / 0.45),
              0 0 0 1px var(--glass-edge);
}
/* O brilho de cima é um pseudo-elemento para não entrar no background e
   atrapalhar o backdrop-filter. */
.ds-glass::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit; background: var(--glass-shine);
  opacity: 0.9;
}
.ds-glass > * { position: relative; }
.ds-glass-strong { background: var(--glass-bg-strong); }
.ds-glass-sm { -webkit-backdrop-filter: var(--glass-blur-sm); backdrop-filter: var(--glass-blur-sm); }

/* Vidro de contorno: quase todo transparente, definido pela borda e pelo
   realce. Só funciona sobre fundo com cor — sobre branco chapado some.
   Use em controle que deve desaparecer quando não está sendo usado. */
.ds-glass-outline {
  background: var(--glass-bg-thin);
  border-color: oklch(1 0 0 / 0.55);
  box-shadow: inset 0 1px 0 0 oklch(1 0 0 / 0.55), 0 0 0 1px var(--glass-edge);
}

/* Sem suporte a backdrop-filter, vira superfície opaca. Nunca translúcida
   sem blur: seria texto por cima de conteúdo, ilegível. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-glass { background: var(--surface-raised); }
  .ds-glass::before { display: none; }
}

/* Barra fixa de topo — o caso mais útil de vidro numa tela de produto */
.ds-appbar {
  position: sticky; top: 0; z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-6);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur-sm);
          backdrop-filter: var(--glass-blur-sm);
  border-bottom: 1px solid var(--border);
}

/* ==========================================================================
   TIPOGRAFIA
   ========================================================================== */
.ds-display  { font: var(--text-display); letter-spacing: var(--tracking-tight); }
.ds-h1       { font: var(--text-h1);      letter-spacing: var(--tracking-tight); }
.ds-h2       { font: var(--text-h2);      letter-spacing: var(--tracking-tight); }
.ds-h3       { font: var(--text-h3);      letter-spacing: -0.01em; }
.ds-body-lg  { font: var(--text-body-lg); }
.ds-body     { font: var(--text-body); }
.ds-body-sm  { font: var(--text-body-sm); }
.ds-caption  { font: var(--text-caption); color: var(--text-muted); }
.ds-label    { font: var(--text-label); }
.ds-overline { font: var(--text-overline); letter-spacing: var(--tracking-wide);
               text-transform: uppercase; color: var(--text-muted); }
.ds-code     { font: var(--text-code); }
.ds-muted    { color: var(--text-muted); }
.ds-num      { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.ds-link     { color: var(--text-link); font-weight: 500;
               text-decoration: underline; text-underline-offset: 2px;
               text-decoration-color: transparent;
               transition: text-decoration-color var(--dur-fast) var(--ease-hover); }
.ds-link:hover { text-decoration-color: currentColor; }

/* ==========================================================================
   ÍCONE
   ========================================================================== */
.ds-icon    { width: var(--icon-md); height: var(--icon-md); flex-shrink: 0;
              stroke: currentColor; fill: none; stroke-width: var(--icon-stroke);
              stroke-linecap: round; stroke-linejoin: round; }
.ds-icon-xs { width: var(--icon-xs); height: var(--icon-xs); }
.ds-icon-sm { width: var(--icon-sm); height: var(--icon-sm); }
.ds-icon-lg { width: var(--icon-lg); height: var(--icon-lg); }
.ds-icon-xl { width: var(--icon-xl); height: var(--icon-xl); }

/* ==========================================================================
   BOTÃO
   Variantes: primary · secondary · ghost · destructive · icon
   Estados:   padrão · hover · foco · ativo · desabilitado · carregando
   ========================================================================== */
.ds-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-2);
  font: 600 14px/1 var(--font-sans); letter-spacing: var(--tracking-normal);
  padding: 10px 16px;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  white-space: nowrap;
  position: relative;
  transition: background-color var(--dur-fast) var(--ease-hover),
              border-color     var(--dur-fast) var(--ease-hover),
              color            var(--dur-fast) var(--ease-hover),
              box-shadow       var(--dur-fast) var(--ease-hover),
              transform        var(--dur-spring) var(--spring-soft);
}
.ds-btn > .ds-icon { width: var(--icon-md); height: var(--icon-md); }
/* Tempo assimétrico: afunda rápido e linear enquanto o dedo está descendo,
   volta com mola quando solta. É o que separa um botão que responde de um
   botão que só pisca. */
.ds-btn:active:not(:disabled) {
  transform: scale(0.97);
  transition-duration: 90ms;
  transition-timing-function: var(--ease-out);
}
.ds-btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* Luz vinda de cima: borda de cima acesa e sombra leve, no tom do próprio botão.
   O botão colorido já se destaca pela cor, então a sombra não precisa ser escura. */
.ds-btn-primary   { background: var(--action-solid); color: var(--action-fg);
                    box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.22), 0 1px 3px oklch(0.53 0.10 189 / 0.16); }
.ds-btn-primary:hover:not(:disabled)   { background: var(--action-solid-hover); }
.ds-btn-primary:active:not(:disabled)  { background: var(--action-solid-active); }
.ds-btn-secondary { background: var(--surface); color: var(--text);
                    border-color: var(--border-strong); box-shadow: var(--shadow-xs); }
.ds-btn-secondary:hover:not(:disabled) { background: var(--surface-hover); border-color: var(--border-hover); }
.ds-btn-ghost     { background: transparent; color: var(--text-muted); }
.ds-btn-ghost:hover:not(:disabled)     { background: var(--surface-hover); color: var(--text); }
.ds-btn-soft      { background: var(--action-soft); color: var(--action-soft-fg); }
.ds-btn-soft:hover:not(:disabled)      { background: var(--action-soft-hover); }
.ds-btn-destructive { background: var(--danger); color: var(--danger-fg); box-shadow: var(--shadow-xs); }
.ds-btn-destructive:hover:not(:disabled) { background: var(--danger-hover); }

/* Tela de toque: alvo de 44 px. No mouse, o botão continua compacto. */
@media (pointer: coarse) { .ds-btn { min-height: 44px; } }
.ds-btn-sm  { padding: 7px 12px; font-size: 13px; border-radius: var(--radius-sm); gap: 6px; }
.ds-btn-lg  { padding: 13px 22px; font-size: 15px; }
.ds-btn-icon{ padding: 10px; }
.ds-btn-icon.ds-btn-sm { padding: 7px; }
.ds-btn-block { width: 100%; }

/* Carregando: o rótulo fica invisível mas ocupa o espaço, então o botão
   não muda de largura no meio do clique. */
.ds-btn[data-loading="true"] { pointer-events: none; color: transparent; }
.ds-btn[data-loading="true"] > * { opacity: 0; }
.ds-btn[data-loading="true"]::after {
  content: ''; position: absolute; width: 15px; height: 15px;
  border: 2px solid currentColor; border-top-color: transparent;
  border-radius: var(--radius-full);
  color: var(--action-fg);
  animation: ds-spin 600ms linear infinite;
}
.ds-btn-secondary[data-loading="true"]::after,
.ds-btn-ghost[data-loading="true"]::after,
.ds-btn-soft[data-loading="true"]::after { color: var(--text-muted); }
@keyframes ds-spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   CAMPO DE FORMULÁRIO
   ========================================================================== */
.ds-field { display: flex; flex-direction: column; gap: 6px; }
.ds-field > label { font: var(--text-label); }
.ds-field > label .req { color: var(--danger); margin-left: 2px; }
.ds-field .ds-hint  { font: var(--text-caption); color: var(--text-muted); }
.ds-field .ds-error {
  font: var(--text-caption); color: var(--danger-soft-fg);
  display: flex; align-items: center; gap: 5px;
  animation: ds-shift-in 180ms var(--ease-out);
}
@keyframes ds-shift-in { from { opacity: 0; transform: translateY(-3px); } }

.ds-input, .ds-textarea, .ds-select {
  width: 100%;
  font: var(--text-body); color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  padding: 10px 13px;
  transition: border-color var(--dur-fast) var(--ease-hover),
              box-shadow   var(--dur-fast) var(--ease-hover),
              background-color var(--dur-fast) var(--ease-hover);
}
.ds-input::placeholder, .ds-textarea::placeholder { color: var(--text-subtle); }
.ds-input:hover:not(:disabled), .ds-textarea:hover:not(:disabled),
.ds-select:hover:not(:disabled) { border-color: var(--border-hover); }
.ds-input:focus, .ds-textarea:focus, .ds-select:focus {
  outline: none; border-color: var(--ring); box-shadow: var(--ring-shadow);
}
.ds-input:disabled, .ds-textarea:disabled, .ds-select:disabled {
  background: var(--surface-sunken); color: var(--text-subtle); cursor: not-allowed;
}
.ds-input[aria-invalid="true"], .ds-textarea[aria-invalid="true"] { border-color: var(--danger); }
.ds-input[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px var(--danger-soft); }
.ds-textarea { resize: vertical; min-height: 84px; line-height: 1.55; }

/* Campo com ícone / affix */
.ds-input-wrap { position: relative; display: flex; align-items: center; }
.ds-input-wrap > .ds-icon {
  position: absolute; left: 12px; color: var(--text-subtle); pointer-events: none;
  transition: color var(--dur-fast) var(--ease-hover);
}
.ds-input-wrap:focus-within > .ds-icon { color: var(--action); }
.ds-input-wrap > .ds-input { padding-left: 38px; }
.ds-input-wrap > .ds-affix {
  position: absolute; right: 6px; display: flex; align-items: center; gap: 2px;
}
.ds-input-wrap:has(> .ds-affix) > .ds-input { padding-right: 42px; }

/* Busca: o botão de limpar só aparece quando há texto */
.ds-search .ds-clear { opacity: 0; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-hover); }
.ds-search[data-filled="true"] .ds-clear { opacity: 1; pointer-events: auto; }

/* Contador de caracteres */
.ds-counter { font: var(--text-caption); color: var(--text-muted);
  font-family: var(--font-mono); align-self: flex-end;
  transition: color var(--dur-fast) var(--ease-hover); }
.ds-counter[data-over="true"] { color: var(--danger-soft-fg); }

/* ==========================================================================
   SELECT nativo com seta própria
   ========================================================================== */
.ds-select {
  appearance: none; padding-right: 36px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237b8494' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; background-size: 16px;
}

/* ==========================================================================
   CHECKBOX · RADIO · SWITCH · SLIDER
   ========================================================================== */
.ds-check {
  display: inline-flex; align-items: center; gap: 10px;
  font: var(--text-body); cursor: pointer;
}
.ds-check:has(input:disabled) { opacity: 0.5; cursor: not-allowed; }
.ds-check input[type="checkbox"], .ds-check input[type="radio"] {
  appearance: none; width: 18px; height: 18px; flex-shrink: 0; position: relative;
  border: 1.5px solid var(--border-strong); background: var(--surface); cursor: inherit;
  transition: background-color var(--dur-fast) var(--ease-hover),
              border-color     var(--dur-fast) var(--ease-hover),
              transform        var(--dur-press) var(--ease-out);
}
.ds-check input[type="checkbox"] { border-radius: var(--radius-xs); }
.ds-check input[type="radio"]    { border-radius: var(--radius-full); }
.ds-check input:active:not(:disabled) { transform: scale(0.92); }
.ds-check input:checked { background: var(--action); border-color: var(--action); }
.ds-check input[type="checkbox"]:checked::after {
  content: ''; position: absolute; left: 5px; top: 1.5px;
  width: 4px; height: 9px;
  border: solid var(--action-fg); border-width: 0 2px 2px 0;
  transform: rotate(45deg); transform-origin: center;
  /* A marca é DESENHADA, não aparece pronta: o clip revela de baixo para cima,
     que no eixo rotacionado é a ordem em que a mão faria o traço. */
  animation: ds-check-draw 220ms var(--ease-out);
}
@keyframes ds-check-draw {
  from { clip-path: inset(100% 0 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
/* A caixa dá um pulo curto ao marcar — só ao marcar, não ao desmarcar. */
.ds-check input[type="checkbox"]:checked, .ds-check input[type="radio"]:checked {
  animation: ds-box-pop var(--dur-spring) var(--spring-bounce);
}
@keyframes ds-box-pop { from { transform: scale(0.82); } to { transform: scale(1); } }
.ds-check input[type="checkbox"]:indeterminate {
  background: var(--action); border-color: var(--action);
}
.ds-check input[type="checkbox"]:indeterminate::after {
  content: ''; position: absolute; left: 3px; top: 7px;
  width: 9px; height: 2px; border-radius: var(--radius-full); background: var(--action-fg);
}
.ds-check input[type="radio"]:checked::after {
  content: ''; position: absolute; inset: 4px;
  border-radius: var(--radius-full); background: var(--action-fg);
  animation: ds-dot-in var(--dur-spring) var(--spring-bounce);
}
/* Parte de 0.3, não de 0: nada no mundo real aparece do nada. */
@keyframes ds-dot-in { from { transform: scale(0.3); opacity: 0; } }

/* Switch */
.ds-switch { position: relative; display: inline-block; width: 40px; height: 22px; flex-shrink: 0; }
.ds-switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; z-index: 1; }
.ds-switch .track {
  position: absolute; inset: 0; border-radius: var(--radius-full);
  background: var(--border-strong);
  transition: background-color var(--dur-base) var(--ease-out);
}
.ds-switch .track::before {
  content: ''; position: absolute; left: 3px; top: 3px; width: 16px; height: 16px;
  border-radius: var(--radius-full); background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.25);
  /* O polegar tem massa: assenta com mola em vez de parar seco no fim. */
  transition: transform var(--dur-spring) var(--spring-soft),
              width     var(--dur-base)   var(--ease-out);
}
.ds-switch input:checked + .track { background: var(--action); }
.ds-switch input:checked + .track::before { transform: translateX(18px); }
/* O polegar estica no aperto e volta — dá peso ao gesto. */
.ds-switch input:active:not(:disabled) + .track::before { width: 20px; }
.ds-switch input:checked:active:not(:disabled) + .track::before { transform: translateX(14px); }
.ds-switch input:disabled { cursor: not-allowed; }
.ds-switch input:disabled + .track { opacity: 0.45; }

/* Slider */
.ds-slider { width: 100%; appearance: none; background: transparent; cursor: pointer; }
.ds-slider::-webkit-slider-runnable-track {
  height: 6px; border-radius: var(--radius-full);
  background: linear-gradient(to right, var(--action) var(--ds-fill, 50%), var(--border-strong) var(--ds-fill, 50%));
}
.ds-slider::-moz-range-track { height: 6px; border-radius: var(--radius-full); background: var(--border-strong); }
.ds-slider::-moz-range-progress { height: 6px; border-radius: var(--radius-full); background: var(--action); }
.ds-slider::-webkit-slider-thumb {
  appearance: none; width: 18px; height: 18px; margin-top: -6px;
  border-radius: var(--radius-full); background: var(--surface);
  border: 2px solid var(--action); box-shadow: var(--shadow-sm);
  transition: transform var(--dur-press) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-hover);
}
.ds-slider::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: var(--radius-full);
  background: var(--surface); border: 2px solid var(--action); box-shadow: var(--shadow-sm);
}
.ds-slider:active::-webkit-slider-thumb { transform: scale(1.15); box-shadow: var(--ring-shadow); }
.ds-slider:disabled { opacity: 0.45; cursor: not-allowed; }

/* ==========================================================================
   AVATAR
   ========================================================================== */
.ds-avatar {
  display: inline-grid; place-items: center; flex-shrink: 0; position: relative;
  width: 36px; height: 36px; border-radius: var(--radius-full);
  background: var(--action-soft); color: var(--action-soft-fg);
  font: 600 13px/1 var(--font-sans);
  overflow: hidden; user-select: none;
}
.ds-avatar img { width: 100%; height: 100%; object-fit: cover; }
.ds-avatar-xs { width: 24px; height: 24px; font-size: 10px; }
.ds-avatar-sm { width: 28px; height: 28px; font-size: 11px; }
.ds-avatar-lg { width: 44px; height: 44px; font-size: 15px; }
.ds-avatar-xl { width: 64px; height: 64px; font-size: 20px; }
.ds-avatar-square { border-radius: var(--radius-md); }

/* Presença */
.ds-avatar[data-status]::after {
  content: ''; position: absolute; right: -1px; bottom: -1px;
  width: 30%; height: 30%; border-radius: var(--radius-full);
  border: 2px solid var(--surface);
}
.ds-avatar[data-status="online"]::after  { background: var(--success); }
.ds-avatar[data-status="ausente"]::after { background: var(--warning); }
.ds-avatar[data-status="offline"]::after { background: var(--text-subtle); }

/* Grupo: o hover abre um pouco o leque, sem reflow (só transform) */
.ds-avatar-group { display: inline-flex; }
.ds-avatar-group > * {
  margin-left: -10px;
  box-shadow: 0 0 0 2px var(--surface);
  transition: transform var(--dur-base) var(--ease-out);
}
.ds-avatar-group > *:first-child { margin-left: 0; }
@media (hover: hover) and (pointer: fine) {
  .ds-avatar-group:hover > * { transform: translateX(4px); }
  .ds-avatar-group:hover > *:first-child { transform: translateX(0); }
  .ds-avatar-group > *:hover { transform: translateY(-3px) translateX(4px); z-index: 1; }
}
.ds-avatar-more { background: var(--neutral-soft); color: var(--neutral-soft-fg); }

/* ==========================================================================
   BADGE · TAG · CHIP
   ========================================================================== */
.ds-badge {
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 12px/1.3 var(--font-sans);
  padding: 4px 10px; border-radius: var(--radius-full);
  white-space: nowrap; border: 1px solid transparent;
}
.ds-badge .dot { width: 6px; height: 6px; border-radius: var(--radius-full); background: currentColor; flex-shrink: 0; }
.ds-badge-neutral { background: var(--neutral-soft); color: var(--neutral-soft-fg); }
.ds-badge-brand   { background: var(--action-soft);  color: var(--action-soft-fg); }
.ds-badge-success { background: var(--success-soft);  color: var(--success-soft-fg); }
.ds-badge-warning { background: var(--warning-soft);  color: var(--warning-soft-fg); }
.ds-badge-danger  { background: var(--danger-soft);   color: var(--danger-soft-fg); }
.ds-badge-info    { background: var(--info-soft);     color: var(--info-soft-fg); }
.ds-badge-accent  { background: var(--accent-soft);   color: var(--accent-soft-fg); }
.ds-badge-outline { background: transparent; border-color: var(--border-strong); color: var(--text-muted); }
.ds-badge-sq      { border-radius: var(--radius-xs); }

/* Ponto pulsante para estado vivo — 2s, discreto */
.ds-badge .dot-live { position: relative; }
.ds-badge .dot-live::after {
  content: ''; position: absolute; inset: 0; border-radius: var(--radius-full);
  background: currentColor; animation: ds-pulse 2s var(--ease-out) infinite;
}
@keyframes ds-pulse {
  0%   { transform: scale(1);   opacity: 0.6; }
  70%  { transform: scale(2.6); opacity: 0; }
  100% { transform: scale(2.6); opacity: 0; }
}

/* Chip clicável / removível */
.ds-chip {
  display: inline-flex; align-items: center; gap: 7px;
  font: 500 13px/1 var(--font-sans);
  padding: 7px 13px; border-radius: var(--radius-full);
  background: var(--surface); color: var(--text-muted);
  border: 1px solid var(--border-strong);
  transition: background-color var(--dur-fast) var(--ease-hover),
              border-color     var(--dur-fast) var(--ease-hover),
              color            var(--dur-fast) var(--ease-hover),
              transform        var(--dur-press) var(--ease-out);
}
.ds-chip:hover  { border-color: var(--action); color: var(--action-soft-fg); }
.ds-chip:active { transform: scale(0.96); }
.ds-chip[aria-pressed="true"], .ds-chip.is-active {
  background: var(--action); border-color: var(--action); color: var(--action-fg); font-weight: 600;
  animation: ds-chip-pop var(--dur-spring) var(--spring-bounce);
}
@keyframes ds-chip-pop { from { transform: scale(0.94); } to { transform: scale(1); } }
.ds-chip .x { display: grid; place-items: center; width: 16px; height: 16px;
  border-radius: var(--radius-full); opacity: 0.6;
  transition: opacity var(--dur-fast) var(--ease-hover), background-color var(--dur-fast) var(--ease-hover); }
.ds-chip .x:hover { opacity: 1; background: rgba(0,0,0,.08); }

/* ==========================================================================
   CARD
   ========================================================================== */
.ds-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
}
.ds-card-pad  { padding: var(--space-5); }
.ds-card-head { padding: var(--space-5) var(--space-5) 0; display: flex;
                justify-content: space-between; align-items: flex-start; gap: var(--space-4); }
.ds-card-body { padding: var(--space-4) var(--space-5) var(--space-5); }
.ds-card-foot { padding: var(--space-4) var(--space-5); background: var(--surface-sunken);
                border-top: 1px solid var(--border); display: flex; justify-content: flex-end;
                gap: var(--space-3); border-radius: 0 0 var(--radius-lg) var(--radius-lg); }
@media (hover: hover) and (pointer: fine) {
  .ds-card-interactive {
    transition: transform var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out),
                border-color var(--dur-base) var(--ease-hover);
  }
  .ds-card-interactive:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
  .ds-card-interactive:active { transform: translateY(0) scale(0.995); }
}

/* ==========================================================================
   TOOLTIP
   125ms, nasce do gatilho. Depois que um abre, os vizinhos abrem na hora.
   ========================================================================== */
.ds-tip { position: relative; display: inline-flex; }
.ds-tip > .ds-tip-content {
  position: absolute; bottom: calc(100% + 8px); left: 50%;
  transform-origin: bottom center;
  transform: translateX(-50%) scale(0.97);
  opacity: 0; pointer-events: none;
  background: oklch(0.207 0.017 252 / 0.88); color: var(--gray-0);
  -webkit-backdrop-filter: var(--glass-blur-sm); backdrop-filter: var(--glass-blur-sm);
  font: 500 12.5px/1.4 var(--font-sans);
  padding: 6px 10px; border-radius: var(--radius-sm);
  white-space: nowrap; box-shadow: var(--shadow-md); z-index: var(--z-tooltip);
  transition: opacity   125ms var(--ease-out),
              transform 125ms var(--ease-out);
}
.ds-tip:hover > .ds-tip-content,
.ds-tip:focus-within > .ds-tip-content { opacity: 1; transform: translateX(-50%) scale(1); }
.ds-tip[data-side="bottom"] > .ds-tip-content {
  bottom: auto; top: calc(100% + 8px); transform-origin: top center;
}
.ds-tip-group:has(.ds-tip-content[style*="opacity: 1"]) .ds-tip-content { transition-duration: 0ms; }

/* ==========================================================================
   DROPDOWN / MENU
   Escala para fora do gatilho — não do nada.
   ========================================================================== */
.ds-dropdown { position: relative; display: inline-block; }
.ds-menu {
  position: absolute; top: calc(100% + 6px); left: 0; min-width: 210px;
  transform-origin: top left; transform: scale(0.95); opacity: 0; pointer-events: none;
  background: var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--glass-shadow), 0 0 0 1px var(--glass-edge),
              inset 0 1px 0 0 oklch(1 0 0 / 0.45);
  padding: 6px; z-index: var(--z-dropdown);
  transition: opacity   var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-menu { background: var(--surface-raised); }
}
.ds-menu[data-align="right"] { left: auto; right: 0; transform-origin: top right; }
.ds-dropdown.is-open > .ds-menu { opacity: 1; transform: scale(1); pointer-events: auto; }

/* Os itens entram em cascata curta atrás do painel. 22ms entre um e outro:
   o conjunto todo fecha em menos de 200ms, então o menu nunca fica
   "carregando" — dá só a impressão de profundidade.
   Decorativo: os itens já são clicáveis desde o primeiro quadro. */
.ds-menu > .ds-menu-item, .ds-menu > .ds-menu-label {
  opacity: 0; transform: translateY(-5px);
  transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out);
}
.ds-dropdown.is-open > .ds-menu > .ds-menu-item,
.ds-dropdown.is-open > .ds-menu > .ds-menu-label { opacity: 1; transform: none; }
.ds-dropdown.is-open > .ds-menu > :nth-child(1) { transition-delay: 20ms; }
.ds-dropdown.is-open > .ds-menu > :nth-child(2) { transition-delay: 42ms; }
.ds-dropdown.is-open > .ds-menu > :nth-child(3) { transition-delay: 64ms; }
.ds-dropdown.is-open > .ds-menu > :nth-child(4) { transition-delay: 82ms; }
.ds-dropdown.is-open > .ds-menu > :nth-child(5) { transition-delay: 98ms; }
.ds-dropdown.is-open > .ds-menu > :nth-child(n+6) { transition-delay: 112ms; }
/* Ao fechar, todos somem juntos: cascata na saída faria o menu "vazar". */
.ds-dropdown:not(.is-open) > .ds-menu > * { transition-delay: 0ms; transition-duration: 90ms; }
@media (prefers-reduced-motion: reduce) {
  .ds-menu > .ds-menu-item, .ds-menu > .ds-menu-label { opacity: 1; transform: none; }
}
.ds-menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  font: var(--text-body-sm); font-weight: 500; text-align: left; color: var(--text);
  padding: 8px 10px; border-radius: var(--radius-sm);
  transition: background-color var(--dur-fast) var(--ease-hover), color var(--dur-fast) var(--ease-hover);
}
.ds-menu-item:hover, .ds-menu-item:focus-visible { background: var(--surface-hover); outline: none; }
.ds-menu-item[aria-checked="true"] { color: var(--action-soft-fg); font-weight: 600; }
.ds-menu-item[data-variant="danger"] { color: var(--danger-soft-fg); }
.ds-menu-item[data-variant="danger"]:hover { background: var(--danger-soft); }
.ds-menu-item:disabled { opacity: 0.45; pointer-events: none; }
.ds-menu-item .kbd { margin-left: auto; }
.ds-menu-sep   { height: 1px; background: var(--border); margin: 5px -5px; }
.ds-menu-label { font: var(--text-overline); letter-spacing: var(--tracking-wide);
                 text-transform: uppercase; color: var(--text-subtle); padding: 8px 10px 4px; }

.ds-kbd {
  font: 500 11px/1 var(--font-mono); color: var(--text-muted);
  background: var(--surface-sunken); border: 1px solid var(--border-strong);
  border-bottom-width: 2px; border-radius: var(--radius-xs);
  padding: 3px 5px; min-width: 18px; text-align: center; display: inline-block;
}

/* ==========================================================================
   ACCORDION
   height é a exceção tolerada — não existe equivalente em transform.
   Por isso fica curto: 200ms.
   ========================================================================== */
.ds-accordion { border: 1px solid var(--border); border-radius: var(--radius-lg);
                background: var(--surface); overflow: hidden; }
.ds-acc-item + .ds-acc-item { border-top: 1px solid var(--border); }
.ds-acc-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  width: 100%; text-align: left; padding: 16px 18px;
  font: var(--text-h3); letter-spacing: -0.01em; color: var(--text);
  transition: background-color var(--dur-fast) var(--ease-hover);
}
.ds-acc-trigger:hover { background: var(--surface-hover); }
.ds-acc-trigger > .ds-icon { color: var(--text-muted);
  transition: transform var(--dur-base) var(--ease-out), color var(--dur-fast) var(--ease-hover); }
.ds-acc-item.is-open > .ds-acc-trigger > .ds-icon { transform: rotate(180deg); color: var(--action); }
.ds-acc-panel {
  height: 0; overflow: hidden; opacity: 0;
  transition: height  var(--dur-base) var(--ease-out),
              opacity var(--dur-base) var(--ease-out);
}
.ds-acc-item.is-open > .ds-acc-panel { opacity: 1; }
/* O conteúdo sobe um pouco enquanto a altura abre. Sem isso o texto parece
   colado no topo do painel e a abertura lê como um corte, não um movimento. */
.ds-acc-panel > div {
  padding: 0 18px 18px; font: var(--text-body); color: var(--text-muted);
  transform: translateY(-6px);
  transition: transform var(--dur-base) var(--ease-out);
}
.ds-acc-item.is-open > .ds-acc-panel > div { transform: translateY(0); }

/* ==========================================================================
   ALERTA
   ========================================================================== */
.ds-alert {
  display: flex; gap: var(--space-3); align-items: flex-start;
  padding: 13px 16px; border-radius: var(--radius-md);
  font: var(--text-body-sm); border: 1px solid transparent;
}
.ds-alert > .ds-icon { margin-top: 1px; }
.ds-alert .ds-alert-body { flex: 1; min-width: 0; }
.ds-alert strong { font-weight: 600; display: block; margin-bottom: 2px; }
.ds-alert-info    { background: var(--info-soft);    color: var(--info-soft-fg); }
.ds-alert-success { background: var(--success-soft); color: var(--success-soft-fg); }
.ds-alert-warning { background: var(--warning-soft); color: var(--warning-soft-fg); }
.ds-alert-danger  { background: var(--danger-soft);  color: var(--danger-soft-fg); }
.ds-alert-neutral { background: var(--neutral-soft); color: var(--neutral-soft-fg); }
/* Sem largura para os dois, a ação desce para a própria linha em vez de
   espremer o texto numa coluna de três palavras. */
@media (max-width: 620px) {
  .ds-alert { flex-wrap: wrap; }
  .ds-alert > .ds-btn { flex-basis: 100%; margin-top: var(--space-2); }
}

/* ==========================================================================
   TOAST
   Transição, nunca keyframes: o usuário dispara dois em um segundo e a
   transição retoma do valor atual em vez de reiniciar do zero.
   ========================================================================== */
.ds-toast-host {
  position: fixed; bottom: var(--space-6); right: var(--space-6);
  z-index: var(--z-toast); display: flex; flex-direction: column-reverse;
  gap: 10px; pointer-events: none;
}
/* Quando empilha, os toasts ocupam o mesmo lugar e o transform separa. */
.ds-toast-host[data-stacked="true"] { display: grid; }
.ds-toast-host[data-stacked="true"] > .ds-toast { grid-area: 1 / 1; }
.ds-toast {
  display: flex; align-items: flex-start; gap: 11px; pointer-events: auto;
  min-width: 300px; max-width: 400px;
  background: var(--glass-bg-strong); color: var(--text);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
  box-shadow: var(--glass-shadow), 0 0 0 1px var(--glass-edge),
              inset 0 1px 0 0 oklch(1 0 0 / 0.45);
  padding: 13px 15px;
  font: var(--text-body-sm);
  touch-action: pan-y;
  /* Variáveis do empilhamento, escritas pelo JS. O transform é UM só, montado
     a partir delas — assim o arrasto, a pilha e a entrada não brigam por
     quem controla a propriedade. */
  --t-y: 16px; --t-scale: 0.96; --t-drag: 0px; --t-op: 0;
  opacity: var(--t-op);
  transform: translateY(calc(var(--t-y) + var(--t-drag))) scale(var(--t-scale));
  transition: opacity   var(--dur-toast) var(--ease-hover),
              transform var(--dur-toast) var(--ease-hover);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-toast { background: var(--surface-raised); }
}
.ds-toast.is-in { --t-y: 0px; --t-scale: 1; --t-op: 1; }
/* Sai pelo mesmo caminho por onde entrou. */
.ds-toast.is-out { --t-y: 16px; --t-scale: 0.96; --t-op: 0; }
/* Empilhados: os de trás recuam e encolhem, como no Sonner. Ninguém lê três
   toasts ao mesmo tempo — a pilha diz "tem mais" sem disputar a atenção. */
.ds-toast[data-depth="1"] { --t-y: -8px;  --t-scale: 0.95; --t-op: 0.72; }
.ds-toast[data-depth="2"] { --t-y: -15px; --t-scale: 0.90; --t-op: 0.42; }
.ds-toast[data-depth="3"] { --t-y: -21px; --t-scale: 0.86; --t-op: 0; }
/* A pilha abre quando o mouse entra: aí sim dá para ler todos. */
.ds-toast-host:hover .ds-toast[data-depth] { --t-y: 0px; --t-scale: 1; --t-op: 1; }
.ds-toast.is-dragging { transition: none; cursor: grabbing; }
.ds-toast.is-settling { transition: transform var(--dur-spring) var(--spring-soft), opacity 150ms var(--ease-out); }
.ds-toast .ds-toast-title { font-weight: 600; }
.ds-toast .ds-toast-desc  { color: var(--text-muted); margin-top: 2px; }
.ds-toast[data-variant="success"] > .ds-icon { color: var(--success); }
.ds-toast[data-variant="danger"]  > .ds-icon { color: var(--danger); }
.ds-toast[data-variant="warning"] > .ds-icon { color: var(--warning); }
.ds-toast[data-variant="info"]    > .ds-icon { color: var(--info); }
@media (max-width: 520px) {
  .ds-toast-host { left: var(--space-4); right: var(--space-4); bottom: var(--space-4); }
  .ds-toast { min-width: 0; max-width: none; }
}

/* ==========================================================================
   MODAL
   É o único popover que fica centralizado: não está ancorado a gatilho nenhum.
   ========================================================================== */
.ds-modal-overlay {
  position: fixed; inset: 0; z-index: var(--z-modal);
  background: var(--overlay);
  -webkit-backdrop-filter: blur(0px); backdrop-filter: blur(0px);
  display: grid; place-items: center; padding: var(--space-6);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease-out),
              backdrop-filter var(--dur-slow) var(--ease-out),
              -webkit-backdrop-filter var(--dur-slow) var(--ease-out);
}
/* O véu embaça junto com o painel entrando, não antes: os dois leem como
   um movimento só, e o fundo "afunda" enquanto o modal sobe. */
.ds-modal-overlay.is-open {
  -webkit-backdrop-filter: var(--overlay-blur); backdrop-filter: var(--overlay-blur);
}
.ds-modal-overlay.is-open { opacity: 1; pointer-events: auto; }
.ds-modal {
  width: 100%; max-width: 520px; max-height: calc(100vh - 96px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--glass-shadow), 0 0 0 1px var(--glass-edge),
              inset 0 1px 0 0 oklch(1 0 0 / 0.50);
  transform-origin: center; transform: scale(0.96); opacity: 0;
  filter: blur(3px);
  /* O desfoque de entrada é curto e pequeno (3px). Ele mascara o instante em
     que o painel ainda não tem forma definida — o olho lê uma coisa só
     ganhando foco, em vez de um retângulo surgindo. Acima de ~20px vira
     caro, especialmente no Safari. */
  transition: opacity   var(--dur-slow) var(--ease-out),
              transform var(--dur-spring) var(--spring-soft),
              filter    180ms var(--ease-out);
}
.ds-modal-overlay.is-open .ds-modal { transform: scale(1); opacity: 1; filter: blur(0); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-modal { background: var(--surface-raised); }
}
.ds-modal-head { padding: var(--space-5) var(--space-5) 0; display: flex;
                 justify-content: space-between; align-items: flex-start; gap: var(--space-4); }
.ds-modal-body { padding: var(--space-3) var(--space-5) var(--space-5); overflow-y: auto; }
.ds-modal-foot { padding: var(--space-4) var(--space-5); background: var(--glass-veil);
                 border-top: 1px solid var(--border); display: flex;
                 justify-content: flex-end; gap: var(--space-3); }
@media (max-width: 520px) {
  .ds-modal-overlay { padding: var(--space-3); align-items: flex-end; }
  .ds-modal { max-width: none; }
  .ds-modal-foot { flex-direction: column-reverse; }
  .ds-modal-foot > .ds-btn { width: 100%; }
}

/* ==========================================================================
   DRAWER
   Curva de drawer iOS, 420ms. Sai pelo mesmo lado.
   ========================================================================== */
.ds-drawer-overlay {
  position: fixed; inset: 0; z-index: var(--z-drawer);
  background: var(--overlay); opacity: 0; pointer-events: none;
  -webkit-backdrop-filter: var(--overlay-blur); backdrop-filter: var(--overlay-blur);
  transition: opacity var(--dur-drawer) var(--ease-drawer);
}
.ds-drawer-overlay.is-open { opacity: 1; pointer-events: auto; }
.ds-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: var(--z-drawer);
  width: min(420px, 100vw); display: flex; flex-direction: column;
  background: var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border-left: 1px solid var(--glass-border);
  border-radius: var(--radius-xl) 0 0 var(--radius-xl);
  box-shadow: var(--glass-shadow), 0 0 0 1px var(--glass-edge);
  transform: translateX(100%);
  transition: transform var(--dur-drawer) var(--ease-drawer);
  touch-action: pan-y;
}
.ds-drawer.is-open { transform: translateX(0); }
/* Enquanto o dedo está no painel, ele segue o dedo sem transição nenhuma:
   qualquer duração aqui vira atraso entre o gesto e a resposta. */
.ds-drawer.is-dragging { transition: none; user-select: none; }
/* Ao soltar, assenta com mola — a mola carrega a velocidade do gesto. */
.ds-drawer.is-settling { transition: transform var(--dur-spring-lg) var(--spring-soft); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-drawer { background: var(--surface); }
}
/* Alça: some do caminho no desktop, aparece na folha do celular */
.ds-drawer-grip { display: none; }
.ds-drawer-head { padding: var(--space-5); border-bottom: 1px solid var(--border);
                  display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-4); }
.ds-drawer-body { flex: 1; overflow-y: auto; padding: var(--space-5); }
.ds-drawer-foot { padding: var(--space-4) var(--space-5); border-top: 1px solid var(--border);
                  background: var(--glass-veil); display: flex; gap: var(--space-3); }
/* No celular vira folha de baixo — o polegar alcança. */
@media (max-width: 640px) {
  .ds-drawer {
    top: auto; left: 0; right: 0; width: auto; height: min(88vh, 720px);
    border-left: 0; border-top: 1px solid var(--border);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    transform: translateY(100%);
  }
  .ds-drawer.is-open { transform: translateY(0); }
  .ds-drawer-head { padding-top: var(--space-6); }
  .ds-drawer-head::before {
    content: ''; position: absolute; top: 9px; left: 50%; transform: translateX(-50%);
    width: 36px; height: 4px; border-radius: var(--radius-full); background: var(--border-strong);
    transition: width var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-hover);
  }
  .ds-drawer.is-dragging .ds-drawer-head::before { width: 48px; background: var(--action); }
}

/* ==========================================================================
   TABS
   O indicador desliza entre as abas em vez de piscar de uma para outra.
   ========================================================================== */
.ds-tabs { position: relative; display: inline-flex; gap: 2px;
           background: var(--surface-sunken); border: 1px solid var(--border);
           padding: 3px; border-radius: var(--radius-md);
           max-width: 100%; overflow-x: auto; scrollbar-width: none; }
/* Em tela estreita a faixa rola em vez de estourar a página.
   A barra some: a lista já indica com o corte que há mais abas. */
.ds-tabs::-webkit-scrollbar, .ds-tabs-line::-webkit-scrollbar { display: none; }
.ds-tabs-line { scrollbar-width: none; }
.ds-tabs-thumb {
  position: absolute; top: 3px; height: calc(100% - 6px);
  background: var(--surface); border-radius: var(--radius-sm); box-shadow: var(--shadow-sm);
  transition: transform var(--dur-base) var(--ease-in-out),
              width     var(--dur-base) var(--ease-in-out);
  pointer-events: none;
}
/* O peso é 600 em TODA aba, ativa ou não. Isso é o que permite a camada
   recortada abaixo: as duas listas têm métrica idêntica, então o recorte
   cai exatamente sobre o texto original, sem meio pixel de desencontro. */
.ds-tab {
  position: relative; z-index: 1;
  font: 600 13px/1 var(--font-sans); color: var(--text-muted);
  padding: 8px 15px; border-radius: var(--radius-sm); white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-hover);
}
.ds-tab:hover { color: var(--text); }
.ds-tab[aria-selected="true"] { color: var(--text); }
.ds-tab .count { font-family: var(--font-mono); font-size: 11px; color: var(--text-subtle); margin-left: 5px; }
.ds-tab[aria-selected="true"] .count { color: var(--action-soft-fg); }

/* ---- Indicador por recorte ----
   Cronometrar a cor de cada aba separadamente nunca fecha: uma sempre chega
   antes da outra. Em vez disso, existe uma CÓPIA da lista inteira já pintada
   como ativa, e o que anima é o recorte que revela só a aba da vez.
   O fundo e o texto mudam em sincronia perfeita porque são um elemento só
   sendo revelado, não duas cores sendo interpoladas. */
.ds-tabs-clip {
  position: absolute; top: 3px; left: 3px; height: calc(100% - 6px);
  pointer-events: none; overflow: hidden; z-index: 2;
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--dur-base) var(--ease-in-out);
}
.ds-tabs-clip-row { display: flex; gap: 2px; height: 100%; align-items: stretch; }
.ds-tabs-clip .ds-tab { color: var(--text); background: transparent; }
.ds-tabs-clip .ds-tab .count { color: var(--action-soft-fg); }
.ds-tabs-line .ds-tabs-clip { top: 0; left: 0; height: 100%; }
.ds-tabs-line .ds-tabs-clip-row { gap: var(--space-5); }
@media (prefers-reduced-motion: reduce) { .ds-tabs-clip { transition-duration: 100ms; } }

/* Tabs sublinhadas, para navegação de página */
.ds-tabs-line { position: relative; display: flex; gap: var(--space-5);
                border-bottom: 1px solid var(--border); overflow-x: auto; }
.ds-tabs-line .ds-tab { padding: 11px 0; border-radius: 0; }
.ds-tabs-line .ds-tabs-thumb {
  top: auto; bottom: -1px; height: 2px; border-radius: var(--radius-full) var(--radius-full) 0 0;
  background: var(--action); box-shadow: none;
}

/* ==========================================================================
   BREADCRUMBS
   ========================================================================== */
.ds-crumbs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
             font: var(--text-body-sm); color: var(--text-muted); }
.ds-crumbs a { padding: 2px 4px; border-radius: var(--radius-xs);
               transition: color var(--dur-fast) var(--ease-hover),
                           background-color var(--dur-fast) var(--ease-hover); }
.ds-crumbs a:hover { color: var(--text); background: var(--surface-hover); }
.ds-crumbs .sep { color: var(--text-subtle); }
.ds-crumbs [aria-current="page"] { color: var(--text); font-weight: 600; }

/* ==========================================================================
   PAGINAÇÃO
   ========================================================================== */
.ds-pagination { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.ds-page {
  min-width: 34px; height: 34px; padding: 0 8px;
  display: inline-grid; place-items: center;
  font: 500 13px/1 var(--font-sans); color: var(--text-muted);
  border: 1px solid transparent; border-radius: var(--radius-sm);
  transition: background-color var(--dur-fast) var(--ease-hover),
              color var(--dur-fast) var(--ease-hover),
              border-color var(--dur-fast) var(--ease-hover),
              transform var(--dur-press) var(--ease-out);
}
.ds-page:hover:not(:disabled):not([aria-current]) { background: var(--surface-hover); color: var(--text); }
.ds-page:active:not(:disabled) { transform: scale(0.94); }
.ds-page[aria-current="page"] { background: var(--action); color: var(--action-fg); font-weight: 600; }
.ds-page:disabled { opacity: 0.35; cursor: not-allowed; }
.ds-page-gap { color: var(--text-subtle); padding: 0 4px; }

/* ==========================================================================
   TABELA
   ========================================================================== */
.ds-table-wrap { background: var(--surface); border: 1px solid var(--border);
                 border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-xs);
                 max-width: 100%; min-width: 0; }
/* A tabela rola na horizontal dentro do card em vez de espremer as colunas. */
.ds-table-scroll { overflow-x: auto; }
.ds-table { width: 100%; border-collapse: collapse; font: var(--text-body); }
.ds-table thead th {
  text-align: left; font: var(--text-overline); letter-spacing: var(--tracking-wide);
  text-transform: uppercase; color: var(--text-muted);
  padding: 11px 16px; background: var(--surface-sunken);
  border-bottom: 1px solid var(--border); white-space: nowrap;
}
.ds-table tbody td { padding: 13px 16px; border-bottom: 1px solid var(--border); }
.ds-table tbody tr:last-child td { border-bottom: 0; }
.ds-table tbody tr { transition: background-color var(--dur-fast) var(--ease-hover); }
.ds-table tbody tr:hover { background: var(--surface-hover); }
.ds-table tbody tr[aria-selected="true"] { background: var(--action-soft); }
.ds-table .cell-main { font-weight: 600; }
.ds-table .cell-sub  { font: var(--text-caption); color: var(--text-muted); }
.ds-table .cell-num  { font-family: var(--font-mono); font-size: 13px; font-variant-numeric: tabular-nums;
                       text-align: right; white-space: nowrap; }
.ds-table th.cell-num { text-align: right; }
/* As ações da linha só aparecem no hover — em toque, ficam sempre visíveis. */
.ds-table .row-actions { display: flex; gap: 2px; justify-content: flex-end; }
@media (hover: hover) and (pointer: fine) {
  .ds-table .row-actions { opacity: 0; transition: opacity var(--dur-fast) var(--ease-hover); }
  .ds-table tbody tr:hover .row-actions,
  .ds-table .row-actions:focus-within { opacity: 1; }
}

/* ---- Cabeçalho ordenável ----
   A seta só aparece na coluna ativa. Mostrar seta em toda coluna enche o
   cabeçalho de tinta que não é dado — e ainda deixa ambíguo qual está
   valendo. No hover, a coluna inativa mostra a seta esmaecida, que é a
   dica de que dá para clicar. */
.ds-table th[data-sort] { padding: 0; }
.ds-table th[data-sort] > button {
  display: flex; align-items: center; gap: 5px; width: 100%;
  padding: 11px 16px; font: inherit; color: inherit; text-align: inherit;
  letter-spacing: inherit; text-transform: inherit;
  transition: color var(--dur-fast) var(--ease-hover),
              background-color var(--dur-fast) var(--ease-hover);
}
.ds-table th[data-sort].cell-num > button { justify-content: flex-end; }
.ds-table th[data-sort] > button:hover { color: var(--text); background: var(--surface-hover); }
.ds-table th[data-sort] .seta {
  width: 12px; height: 12px; flex-shrink: 0; opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-hover),
              transform var(--dur-base) var(--ease-out);
}
.ds-table th[data-sort] > button:hover .seta { opacity: 0.4; }
.ds-table th[aria-sort="ascending"] > button,
.ds-table th[aria-sort="descending"] > button { color: var(--action-soft-fg); }
.ds-table th[aria-sort="ascending"] .seta,
.ds-table th[aria-sort="descending"] .seta { opacity: 1; }
.ds-table th[aria-sort="descending"] .seta { transform: rotate(180deg); }

/* A linha some e volta quando a tabela é reordenada ou filtrada. Sem isso
   as linhas trocam de lugar de um quadro para o outro e o olho perde
   qual era qual. */
.ds-table tbody tr { animation: ds-linha-in 220ms var(--ease-out) backwards; }
@keyframes ds-linha-in { from { opacity: 0; transform: translateY(-4px); } }
@media (prefers-reduced-motion: reduce) {
  .ds-table tbody tr { animation: none; }
}

/* ==========================================================================
   PROGRESSO E MEDIDOR
   ========================================================================== */
.ds-progress { height: 8px; background: var(--surface-active);
               border-radius: var(--radius-full); overflow: hidden; }
.ds-progress > .fill {
  height: 100%; border-radius: var(--radius-full); background: var(--action);
  transition: width 600ms var(--ease-out), background-color var(--dur-base) var(--ease-hover);
}
.ds-progress[data-state="warning"] > .fill { background: var(--warning); }
.ds-progress[data-state="danger"]  > .fill { background: var(--danger); }
.ds-progress-sm { height: 5px; }
/* Indeterminado: CSS animation, roda fora da thread principal e não perde
   quadros justamente quando a página está ocupada carregando. */
.ds-progress[data-indeterminate] > .fill { width: 35%; animation: ds-indet 1.4s var(--ease-in-out) infinite; }
@keyframes ds-indet {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(340%); }
}

.ds-meter { display: flex; flex-direction: column; gap: 8px; }
.ds-meter-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3); }
.ds-meter-value { font: 700 19px/1.2 var(--font-sans); font-variant-numeric: tabular-nums;
                  letter-spacing: var(--tracking-tight); }

/* ==========================================================================
   ÁREA DE UPLOAD
   ========================================================================== */
.ds-drop {
  border: 1.5px dashed var(--border-strong); border-radius: var(--radius-lg);
  background: var(--surface); padding: var(--space-8) var(--space-6); text-align: center;
  transition: border-color var(--dur-fast) var(--ease-hover),
              background-color var(--dur-fast) var(--ease-hover),
              transform var(--dur-base) var(--ease-out);
}
.ds-drop:hover { border-color: var(--border-hover); }
.ds-drop.is-over { border-color: var(--action); background: var(--action-soft); transform: scale(1.01); }
.ds-drop .ds-drop-icon {
  width: 48px; height: 48px; margin: 0 auto var(--space-3);
  display: grid; place-items: center; border-radius: var(--radius-md);
  background: var(--action-soft); color: var(--action-soft-fg);
  transition: transform var(--dur-base) var(--ease-out);
}
.ds-drop.is-over .ds-drop-icon { transform: translateY(-4px); }

.ds-file {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 13px; border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--surface);
  animation: ds-file-in 240ms var(--ease-out);
}
@keyframes ds-file-in { from { opacity: 0; transform: translateY(-6px); } }
.ds-file .ds-file-icon { width: 34px; height: 34px; flex-shrink: 0; display: grid; place-items: center;
                         border-radius: var(--radius-md); background: var(--neutral-soft); color: var(--neutral-soft-fg); }
.ds-file .ds-file-info { flex: 1; min-width: 0; }
.ds-file .ds-file-name { font: 500 13.5px/1.35 var(--font-sans);
                         overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-file .ds-file-meta { font: var(--text-caption); color: var(--text-muted); font-family: var(--font-mono); }
.ds-file[data-state="error"] { border-color: var(--danger); background: var(--danger-soft); }
.ds-file[data-state="error"] .ds-file-icon { background: var(--danger); color: var(--danger-fg); }
.ds-file[data-state="error"] .ds-file-meta { color: var(--danger-soft-fg); }
.ds-file[data-state="done"] .ds-file-icon { background: var(--success-soft); color: var(--success-soft-fg); }

/* ==========================================================================
   COMMAND PALETTE
   Abre por atalho de teclado: sem animação de entrada. Uma pessoa abre isto
   dezenas de vezes por dia — qualquer transição vira atrito.
   ========================================================================== */
.ds-cmdk-overlay {
  position: fixed; inset: 0; z-index: var(--z-modal);
  background: var(--overlay); backdrop-filter: blur(3px);
  display: none; align-items: flex-start; justify-content: center;
  padding: 12vh var(--space-4) var(--space-4);
}
.ds-cmdk-overlay.is-open { display: flex; }
.ds-cmdk {
  width: 100%; max-width: 560px; overflow: hidden;
  background: var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--glass-shadow), 0 0 0 1px var(--glass-edge),
              inset 0 1px 0 0 oklch(1 0 0 / 0.50);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-cmdk { background: var(--surface-raised); }
}
.ds-cmdk-input {
  display: flex; align-items: center; gap: 11px;
  padding: 14px 16px; border-bottom: 1px solid var(--border);
}
.ds-cmdk-input > .ds-icon { color: var(--text-subtle); }
.ds-cmdk-input input { flex: 1; border: 0; outline: none; font: 400 15px/1.4 var(--font-sans); }
.ds-cmdk-input input::placeholder { color: var(--text-subtle); }
.ds-cmdk-list { max-height: 340px; overflow-y: auto; padding: 6px; }
.ds-cmdk-group { font: var(--text-overline); letter-spacing: var(--tracking-wide);
                 text-transform: uppercase; color: var(--text-subtle); padding: 10px 10px 5px; }
.ds-cmdk-item {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  font: var(--text-body-sm); font-weight: 500; color: var(--text);
  padding: 9px 10px; border-radius: var(--radius-sm);
}
.ds-cmdk-item > .ds-icon { color: var(--text-muted); }
.ds-cmdk-item[data-active="true"] { background: var(--action-soft); color: var(--action-soft-fg); }
.ds-cmdk-item[data-active="true"] > .ds-icon { color: currentColor; }
.ds-cmdk-item .ds-kbd { margin-left: auto; }
.ds-cmdk-foot { display: flex; align-items: center; gap: var(--space-4);
                padding: 9px 14px; border-top: 1px solid var(--border);
                background: var(--glass-veil); font: var(--text-caption); color: var(--text-muted); }

/* ==========================================================================
   ESTADO VAZIO · LIMITE · SKELETON
   ========================================================================== */
.ds-empty { border: 1.5px dashed var(--border-strong); border-radius: var(--radius-lg);
            background: var(--surface); padding: var(--space-12) var(--space-8); text-align: center; }
.ds-empty .ds-empty-icon {
  width: 52px; height: 52px; margin: 0 auto var(--space-4);
  display: grid; place-items: center; border-radius: var(--radius-md);
  background: var(--action-soft); color: var(--action-soft-fg);
}
.ds-empty h4 { font: 700 16px/1.3 var(--font-sans); letter-spacing: var(--tracking-tight); }
.ds-empty p  { font: var(--text-body-sm); color: var(--text-muted);
               margin: 6px auto var(--space-5); max-width: 340px; }

.ds-limit {
  display: flex; align-items: flex-start; gap: var(--space-4);
  padding: var(--space-5); border-radius: var(--radius-lg);
  background: var(--warning-soft); border: 1px solid transparent; color: var(--warning-soft-fg);
}
.ds-limit .ds-limit-icon { width: 40px; height: 40px; flex-shrink: 0; display: grid; place-items: center;
                           border-radius: var(--radius-md); background: var(--warning); color: var(--gray-900); }
.ds-limit h4 { font: 700 14.5px/1.35 var(--font-sans); }
.ds-limit p  { font: var(--text-body-sm); opacity: 0.85; margin-top: 3px; }

/* Skeleton: brilho linear e lento. Não é decoração — é sinal de espera. */
.ds-skel {
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg,
    var(--surface-active) 25%, var(--surface-hover) 37%, var(--surface-active) 63%);
  background-size: 400% 100%;
  animation: ds-shimmer 1.4s linear infinite;
}
@keyframes ds-shimmer { from { background-position: 100% 50%; } to { background-position: 0 50%; } }
.ds-skel-text   { height: 12px; }
.ds-skel-title  { height: 18px; }
.ds-skel-circle { border-radius: var(--radius-full); }
@media (prefers-reduced-motion: reduce) {
  .ds-skel { animation: none; background: var(--surface-active); }
}

/* ==========================================================================
   KPI
   ========================================================================== */
.ds-kpi { padding: 18px 20px; }
.ds-kpi .ds-overline { margin-bottom: 8px; }
.ds-kpi-value { font: 700 27px/1.15 var(--font-sans); letter-spacing: var(--tracking-tight);
                font-variant-numeric: tabular-nums; }
.ds-kpi-delta { display: inline-flex; align-items: center; gap: 4px;
                font: 600 12px/1.3 var(--font-sans); margin-top: 6px; }
.ds-kpi-delta[data-dir="up"]   { color: var(--success-soft-fg); }
.ds-kpi-delta[data-dir="down"] { color: var(--danger-soft-fg); }
.ds-kpi-delta[data-dir="flat"] { color: var(--text-muted); }

/* ==========================================================================
   UTILITÁRIOS DE LAYOUT (mínimos — a biblioteca não é um framework de grid)
   ========================================================================== */
.ds-row   { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.ds-col   { display: flex; flex-direction: column; gap: var(--space-3); }
.ds-stack { display: flex; flex-direction: column; gap: var(--space-2); }
.ds-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
.ds-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); }
.ds-grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-4); }
.ds-spacer { flex: 1; }
@media (max-width: 900px) { .ds-grid-3, .ds-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .ds-grid-2, .ds-grid-3, .ds-grid-4 { grid-template-columns: 1fr; } }

/* Entrada escalonada de grupo. Decorativa: nunca bloqueia o clique. */
.ds-stagger > * { opacity: 0; animation: ds-rise 300ms var(--ease-out) forwards; }
.ds-stagger > *:nth-child(1) { animation-delay: 0ms; }
.ds-stagger > *:nth-child(2) { animation-delay: 45ms; }
.ds-stagger > *:nth-child(3) { animation-delay: 90ms; }
.ds-stagger > *:nth-child(4) { animation-delay: 135ms; }
.ds-stagger > *:nth-child(5) { animation-delay: 180ms; }
.ds-stagger > *:nth-child(6) { animation-delay: 225ms; }
@keyframes ds-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .ds-stagger > * { opacity: 1; animation: none; }
}


/* ==========================================================================
   SEGURAR PARA CONFIRMAR
   Para ação destrutiva onde um clique é fácil demais de disparar sem querer.
   Tempo assimétrico e proposital: 2s lineares enquanto a pessoa decide,
   200ms para voltar quando solta. `linear` está certo aqui — o preenchimento
   é uma barra de progresso, e progresso não deve ter aceleração.
   ========================================================================== */
.ds-btn-hold { position: relative; overflow: hidden; isolation: isolate; }
.ds-btn-hold > .ds-hold-fill {
  position: absolute; inset: 0; z-index: -1;
  background: var(--danger-hover);
  clip-path: inset(0 100% 0 0);
  transition: clip-path 200ms var(--ease-out);
}
.ds-btn-hold:active:not(:disabled) > .ds-hold-fill {
  clip-path: inset(0 0 0 0);
  transition: clip-path 2s linear;
}
.ds-btn-hold[data-held="true"] { animation: ds-hold-done 420ms var(--spring-bounce); }
@keyframes ds-hold-done { from { transform: scale(1.04); } to { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
  .ds-btn-hold:active:not(:disabled) > .ds-hold-fill { transition-duration: 2s; }
}

/* ==========================================================================
   TROCA DE CONTEÚDO (skeleton → dado)
   Sem o desfoque o olho lê dois objetos trocando de lugar. Com ele, lê um
   objeto ganhando foco. Mantenha baixo: desfoque pesado é caro no Safari.
   ========================================================================== */
.ds-swap { transition: opacity 220ms var(--ease-out), filter 220ms var(--ease-out); }
.ds-swap[data-swapping="true"] { opacity: 0; filter: blur(4px); }

/* Número que mudou de valor: sobe e assenta. É indicação de estado, não
   enfeite — só dispare quando o valor mudou de verdade. */
.ds-num-change { animation: ds-num-in 380ms var(--ease-out); }
@keyframes ds-num-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .ds-num-change { animation: none; }
}


/* ==========================================================================
   NAVEGAÇÃO FLUTUANTE EM PÍLULA
   Barra de vidro que flutua sobre o conteúdo, com polegar deslizante.
   O polegar é um elemento só que se move — o mesmo princípio das tabs:
   o que anima é a posição, não a cor de cada item.
   ========================================================================== */
.ds-navbar {
  --ds-thumb-inset: 6px;
  position: relative; display: inline-flex; align-items: center; gap: 2px;
  padding: var(--ds-thumb-inset); border-radius: var(--radius-full);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow), inset 0 1px 0 0 oklch(1 0 0 / 0.55);
  max-width: 100%; overflow-x: auto; scrollbar-width: none;
}
.ds-navbar::-webkit-scrollbar { display: none; }
.ds-navbar-thumb {
  /* `left` explícito, não posição estática. Sem ele o polegar herdava o
     ponto onde CAIRIA no fluxo — e num contêiner com `justify-content:
     center` isso é o meio, não a borda. Funcionava por acaso na navbar. */
  position: absolute; left: var(--ds-thumb-inset, 6px);
  top: var(--ds-thumb-inset, 6px);
  height: calc(100% - var(--ds-thumb-inset, 6px) * 2);
  border-radius: var(--radius-full);
  background: oklch(1 0 0 / 0.72);
  box-shadow: 0 2px 8px -2px oklch(0.207 0.017 252 / 0.20),
              inset 0 1px 0 0 oklch(1 0 0 / 0.9);
  pointer-events: none;
  transition: transform var(--dur-spring) var(--spring-soft),
              width     var(--dur-spring) var(--spring-soft);
}
.ds-navitem {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 9px 18px; border-radius: var(--radius-full);
  font: 600 11.5px/1 var(--font-sans); color: var(--text-muted);
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-hover),
              transform var(--dur-spring) var(--spring-soft);
}
.ds-navitem > .ds-icon { width: var(--icon-lg); height: var(--icon-lg); stroke-width: 1.6; }
.ds-navitem:hover { color: var(--text); }
.ds-navitem[aria-current="page"], .ds-navitem[aria-selected="true"] { color: var(--action-soft-fg); }
.ds-navitem:active { transform: scale(0.94); transition-duration: 90ms;
                     transition-timing-function: var(--ease-out); }
.ds-navbar-inline .ds-navitem { flex-direction: row; gap: 8px; font-size: 13px; padding: 9px 16px; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-navbar { background: var(--surface); }
}

/* ==========================================================================
   BOTÃO COM AÇÃO ANEXA
   Pílula com um alvo circular grudado na ponta: o rótulo diz o que acontece,
   o círculo é onde a mão mira. Vem das referências e resolve um caso real —
   "Nova cobrança" com um atalho de "duplicar a última" ao lado.
   ========================================================================== */
.ds-btn-pair {
  display: inline-flex; align-items: stretch; gap: 2px;
  border-radius: var(--radius-full); isolation: isolate;
}
.ds-btn-pair > .ds-btn:first-child {
  border-radius: var(--radius-full) var(--radius-xs) var(--radius-xs) var(--radius-full);
  padding-left: 20px;
}
.ds-btn-pair > .ds-btn:last-child {
  border-radius: var(--radius-xs) var(--radius-full) var(--radius-full) var(--radius-xs);
  padding-left: 13px; padding-right: 15px;
}
.ds-btn-pair > .ds-btn:only-child { border-radius: var(--radius-full); }
/* O par se aperta junto, mas só a metade tocada afunda: a outra acompanha
   de leve. Sem isso o par lê como um botão só e o alvo some. */
.ds-btn-pair > .ds-btn:active:not(:disabled) { transform: scale(0.96); }
.ds-btn-pair:has(> .ds-btn:active) > .ds-btn:not(:active) { transform: scale(0.99); }

/* Pílula: a forma padrão das referências para controle de ação */
.ds-btn-pill { border-radius: var(--radius-full); padding-left: 20px; padding-right: 20px; }


/* ==========================================================================
   CAMADA 2 · CROMO FLUTUANTE
   Mesma matéria da navegação flutuante, aplicada aos outros lugares onde a
   pessoa OPERA em vez de ler. Todos compartilham a mesma cápsula de vidro,
   o mesmo raio de pílula e a mesma sombra de contato — é isso que faz a tela
   inteira parecer um sistema só, e não peças parecidas.
   ========================================================================== */

/* A cápsula. Tudo nesta camada herda daqui. */
.ds-float {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 7px; border-radius: var(--radius-full);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow), inset 0 1px 0 0 oklch(1 0 0 / 0.55);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-float { background: var(--surface); }
}

/* ---- Trilho vertical: a navegação lateral, flutuando ---- */
.ds-rail {
  position: sticky; top: var(--space-4);
  display: inline-flex; flex-direction: column; align-items: stretch; gap: 2px;
  padding: 7px; border-radius: var(--radius-xl);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow), inset 0 1px 0 0 oklch(1 0 0 / 0.55);
}
.ds-rail .ds-navitem { padding: 11px 14px; border-radius: var(--radius-lg); }
.ds-rail { --ds-thumb-inset: 7px; }
.ds-rail .ds-navbar-thumb {
  left: var(--ds-thumb-inset); top: var(--ds-thumb-inset);
  width: calc(100% - var(--ds-thumb-inset) * 2); height: auto;
  border-radius: var(--radius-lg);
  transition: transform var(--dur-spring) var(--spring-soft),
              height    var(--dur-spring) var(--spring-soft);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-rail { background: var(--surface); }
}

/* ---- Barra de ferramentas: filtros e ações, numa linha só acima do conteúdo.
   Nunca dentro de um card de gráfico, nunca uma por gráfico — se um gráfico
   precisa do próprio filtro, é outra tela. ---- */
.ds-toolbar {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  padding: 7px 9px; border-radius: var(--radius-full);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur-sm); backdrop-filter: var(--glass-blur-sm);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow), inset 0 1px 0 0 oklch(1 0 0 / 0.50);
}
.ds-toolbar-sep { width: 1px; align-self: stretch; margin: 2px 4px;
                  background: var(--border-strong); opacity: 0.6; }
.ds-toolbar > .ds-input-wrap > .ds-input {
  background: transparent; border-color: transparent; padding-top: 7px; padding-bottom: 7px;
}
.ds-toolbar > .ds-input-wrap > .ds-input:focus {
  background: var(--surface); border-color: var(--ring);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-toolbar { background: var(--surface); }
}

/* ---- Dock: cápsula fixa no rodapé. Aparece quando há seleção (ações em
   massa) e, no celular, é a navegação principal — é onde o polegar chega.
   Entra de baixo com a mesma mola do resto. ---- */
.ds-dock {
  position: fixed; left: 50%; bottom: var(--space-5); z-index: var(--z-dropdown);
  display: flex; align-items: center; gap: var(--space-2);
  padding: 8px 10px; border-radius: var(--radius-full);
  background: var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow), inset 0 1px 0 0 oklch(1 0 0 / 0.55);
  transform: translate(-50%, 120%);
  transition: transform var(--dur-spring) var(--spring-soft),
              opacity var(--dur-base) var(--ease-out);
  opacity: 0; pointer-events: none;
}
.ds-dock.is-in { transform: translate(-50%, 0); opacity: 1; pointer-events: auto; }
.ds-dock[data-ds-navbar] { --ds-thumb-inset: 8px; padding: 8px; }
.ds-dock .ds-dock-count {
  font: 600 13px/1 var(--font-sans); color: var(--text);
  padding: 0 var(--space-2) 0 var(--space-3); white-space: nowrap;
}
.ds-dock .ds-dock-count b { font-family: var(--font-mono); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ds-dock { background: var(--surface); }
}
@media (max-width: 560px) {
  .ds-dock { left: var(--space-3); right: var(--space-3); transform: translateY(120%);
             justify-content: center; overflow-x: auto; scrollbar-width: none; }
  .ds-dock::-webkit-scrollbar { display: none; }
  .ds-dock.is-in { transform: translateY(0); }
  /* Sem largura para rótulo, as ações viram só ícone — o ícone é o alvo e o
     rótulo já estava redundante com ele. O alvo de toque não encolhe. */
  .ds-dock .ds-btn { font-size: 0; padding-left: 11px; padding-right: 11px; }
  .ds-dock .ds-btn > .ds-icon { width: var(--icon-lg); height: var(--icon-lg); }
  .ds-dock .ds-dock-count { font-size: 12.5px; padding-left: var(--space-2); }
}

/* ---- Cromo DENTRO de cromo ----
   Quando uma peça da camada 2 entra em outra — a navegação dentro da barra de
   topo, um grupo de abas dentro da barra de ferramentas — ela PERDE a cápsula.
   Vidro sobre vidro empilha dois desfoques e dois fios de luz: fica turvo e a
   peça de dentro parece descolada. Só a de fora carrega a matéria; a de dentro
   contribui apenas com o comportamento (o polegar que desliza).
   É regra de descendência, não modificador: assim ninguém precisa lembrar. */
.ds-appbar-float .ds-navbar, .ds-toolbar .ds-navbar,
.ds-appbar .ds-navbar, .ds-dock .ds-navbar,
.ds-appbar-float .ds-tabs, .ds-toolbar .ds-tabs {
  --ds-thumb-inset: 0px;
  background: none; border: 0; box-shadow: none; padding: 0;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
/* O polegar continua sendo a única coisa opaca ali dentro. */
.ds-appbar-float .ds-navbar-thumb, .ds-toolbar .ds-navbar-thumb {
  background: oklch(1 0 0 / 0.85);
  box-shadow: 0 1px 4px -1px oklch(0.207 0.017 252 / 0.18),
              inset 0 1px 0 0 oklch(1 0 0 / 0.9);
}

/* ---- Barra de topo flutuante: a variante destacada da .ds-appbar ---- */
.ds-appbar-float {
  position: sticky; top: var(--space-4); z-index: var(--z-sticky);
  margin: 0 auto; width: fit-content; max-width: 100%;
  display: flex; align-items: center; gap: var(--space-2);
  padding: 7px 9px; border-radius: var(--radius-full);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow), inset 0 1px 0 0 oklch(1 0 0 / 0.55);
  border-bottom: 1px solid var(--glass-border);
}

/* ---- Superfície de campo: onde o vidro precisa de algo para refratar.
   Use em tela de entrada, estado vazio ilustrado e cabeçalho de herói. ---- */
.ds-field-bg { background: var(--bg-field); border-radius: var(--radius-2xl); }

/* ---- Superfície invertida ----
   Escura por FUNÇÃO, não por tema — a biblioteca continua só clara.
   Entrada, faixa de marca, cabeçalho de documento. Dentro dela o texto e o
   logo viram brancos; é o único lugar onde conpsi-lockup-branco entra. */
.ds-field-deep {
  background: var(--bg-field-deep);
  color: var(--text-on-inverse);
  border-radius: var(--radius-2xl);
}
.ds-field-deep .ds-caption,
.ds-field-deep .ds-muted { color: oklch(1 0 0 / 0.72); }
.ds-field-deep .ds-overline { color: oklch(1 0 0 / 0.62); }


/* ==========================================================================
   MARCA
   O símbolo oficial em vez de uma letra dentro de um quadrado.
   ========================================================================== */
.ds-brand { display: inline-flex; align-items: center; gap: 9px; flex-shrink: 0; }
.ds-brand-mark { height: 30px; width: auto; display: block; }
.ds-brand-mark-sm { height: 24px; }
.ds-brand-mark-lg { height: 44px; }
.ds-brand-name { font: 700 15px/1.2 var(--font-sans); letter-spacing: var(--tracking-tight); }
.ds-brand-name .sub { display: block; font: 500 10px/1.2 var(--font-sans);
                      letter-spacing: 0.02em; color: var(--text-muted); margin-top: 2px; }
/* ---- Qual arquivo usar onde ----
   conpsi-lockup-branco.*
                    o mesmo lockup em branco. Só para superfície invertida:
                    sobre --action dá 3,06:1 (passa, mas no limite) e sobre
                    --surface-inverse dá 7,88:1 (confortável). Sobre claro
                    reprova — 1,15:1.
   conpsi-lockup.*  lockup 3:1, teal monocromático, SEM assinatura.
                    É o arquivo da interface: barra, cabeçalho, e-mail, e
                    também entrada a 96px+. Funciona sobre branco, sobre o
                    campo e sobre a barra de vidro.
                    Foi escolhido comparando lado a lado com uma versão de
                    vidro mais marcada: esta lê melhor porque é UNIFORME.
                    Contraste bruto favorecia a outra — uniformidade venceu.
   conpsi-logo.*    logo completo com a assinatura "Contabilidade para
                    Psicólogos". A assinatura tem 7,6% da altura — abaixo de
                    105px de altura ela não lê. Só para documento e impresso.
   conpsi-simbolo.* só a fita, para favicon e avatar quadrado. */
.ds-logo { height: 38px; width: auto; display: block; }
.ds-logo-lg { height: 64px; }


/* ==========================================================================
   AVISO DE PÁGINA
   A terceira família de mensagem, que faltava:

     .ds-alert  faixa colorida, presa a uma seção ou a um formulário.
     .ds-toast  efêmero, confirma uma ação que acabou de acontecer.
     .ds-notice ← PERSISTE e convive com os cards da página.

   Por isso ele é um CARD, não uma faixa: mesma superfície, mesma borda,
   mesmo raio e mesma sombra do resto do conteúdo. A cor do estado aparece
   só no fio da esquerda e no chip do ícone — banhar o card inteiro de
   amarelo é o que fazia a peça parecer de outra plataforma.
   ========================================================================== */
.ds-notice {
  position: relative;
  display: flex; align-items: flex-start; gap: var(--space-4);
  background: var(--surface);
  /* O estado é UM FIO FINO em volta do card inteiro. Contornar tudo destaca
     mais do que uma barra num canto só, e não custa nada de peso: o miolo
     continua branco como os outros cards. */
  border: 1px solid var(--ds-notice-border, var(--border));
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  padding: var(--space-4) var(--space-5);
}
.ds-notice-icon {
  width: 34px; height: 34px; flex-shrink: 0; display: grid; place-items: center;
  border-radius: var(--radius-md);
  background: var(--ds-notice-soft, var(--info-soft));
  color: var(--ds-notice-ink, var(--info-soft-fg));
}
.ds-notice-body { flex: 1; min-width: 0; }
.ds-notice-body h4 { font: 600 14.5px/1.35 var(--font-sans); letter-spacing: -0.005em; }
.ds-notice-body p  { font: var(--text-body-sm); color: var(--text-muted); margin-top: 3px; }
.ds-notice-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }

/* Cada estado empresta a própria cor ao fio. Os quatro foram medidos contra
   o card branco: info 4,79:1 · sucesso 4,95:1 · erro 4,76:1 · atenção 3,30:1.
   Atenção usa --warning-line, e não --warning, porque o âmbar de
   preenchimento daria 1,98:1 — fio invisível. */
.ds-notice[data-status="info"]    { --ds-notice-border: var(--info);
  --ds-notice-soft: var(--info-soft);    --ds-notice-ink: var(--info-soft-fg); }
.ds-notice[data-status="success"] { --ds-notice-border: var(--success);
  --ds-notice-soft: var(--success-soft); --ds-notice-ink: var(--success-soft-fg); }
.ds-notice[data-status="warning"] { --ds-notice-border: var(--warning-line);
  --ds-notice-soft: var(--warning-soft); --ds-notice-ink: var(--warning-soft-fg); }
.ds-notice[data-status="danger"]  { --ds-notice-border: var(--danger);
  --ds-notice-soft: var(--danger-soft);  --ds-notice-ink: var(--danger-soft-fg); }

/* Dispensa com a mesma saída do toast: encolhe e some por onde entrou. */
.ds-notice.is-out {
  opacity: 0; transform: scale(0.98);
  transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out);
}
@media (max-width: 720px) {
  .ds-notice { flex-wrap: wrap; }
  .ds-notice-actions { flex-basis: 100%; margin-top: var(--space-2); }
  .ds-notice-actions > .ds-btn { flex: 1; }
}

/* ==========================================================================
   ALVO DE TOQUE
   Um link de texto tem a altura da LINHA — 20px. A ponta do dedo tem cerca
   de 9mm. O mínimo de 24×24 é regra (WCAG 2.5.8), e 44×44 é o que de fato
   evita erro de toque.

   O pseudo-elemento estende a área tocável SEM mexer no layout: nada se
   desloca, nada cresce na tela, e o alvo passa a caber no dedo.
   Só em ponteiro grosso — no mouse, 20px já é alvo de sobra e uma área
   invisível maior roubaria o clique dos vizinhos.
   ========================================================================== */
@media (pointer: coarse) {
  .ds-link,
  .ds-check,
  .ds-crumbs a,
  .ds-toolbar .ds-chip { position: relative; }

  .ds-link::after,
  .ds-check::after,
  .ds-crumbs a::after {
    content: ''; position: absolute; inset: -12px -6px; border-radius: inherit;
  }
  /* Botão só de ícone cresce de verdade: ele não tem rótulo por perto que
     o pseudo-elemento pudesse cobrir. */
  .ds-btn-icon.ds-btn-sm { padding: 12px; }
  .ds-btn-icon { padding: 13px; }
  /* A caixa do checkbox e do radio acompanham o dedo. */
  .ds-check input[type="checkbox"],
  .ds-check input[type="radio"] { width: 22px; height: 22px; }
  .ds-check input[type="checkbox"]:checked::after { left: 7px; top: 3px; }
}

/* ==========================================================================
   AJUDA EM BALAO ("?")
   Explicacao que nao precisa estar sempre na tela. O botao fica ao lado do titulo;
   o balao abre ao clicar (toque e teclado funcionam, passar o mouse nao serve no celular).
   Texto que diz o ESTADO ou a consequencia ("libera depois de validar o mes") fica visivel;
   texto que explica uma regra ou o porque vai aqui dentro.
   ========================================================================== */
.ds-ajuda { position: relative; display: inline-flex; vertical-align: middle; }
.ds-ajuda-btn { display: inline-grid; place-items: center; width: 22px; height: 22px; padding: 0;
  border: 0; border-radius: var(--radius-full); background: transparent; color: var(--text-subtle);
  cursor: pointer; transition: color var(--dur-fast) var(--ease-hover), background-color var(--dur-fast) var(--ease-hover); }
.ds-ajuda-btn:hover, .ds-ajuda.is-open > .ds-ajuda-btn { color: var(--text); background: var(--surface-hover); }
.ds-ajuda-btn .ds-icon { width: 16px; height: 16px; }
/* position: fixed e coordenadas do JS: o cartao corta o que passa da borda (overflow: hidden) e o balao nao pode ser cortado. */
.ds-ajuda-balao { position: fixed; top: 0; left: 0; z-index: var(--z-tooltip);
  width: max-content; max-width: min(340px, calc(100vw - 32px)); padding: var(--space-3) var(--space-4);
  background: var(--surface-raised, var(--surface)); border: 1px solid var(--border-hover);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-md, 0 8px 24px oklch(0.2 0.02 252 / 0.14));
  color: var(--text); font: var(--text-body-sm); font-weight: 400; letter-spacing: normal; text-transform: none;
  text-align: left; white-space: normal;
  opacity: 0; transform: translateY(-4px); pointer-events: none; visibility: hidden;
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out), visibility 0s linear var(--dur-base); }
.ds-ajuda.is-open > .ds-ajuda-balao { opacity: 1; transform: none; pointer-events: auto; visibility: visible;
  transition-delay: 0s; }
.ds-ajuda-balao p { margin: 0; }
.ds-ajuda-balao p + p { margin-top: var(--space-2); }
@media (prefers-reduced-motion: reduce) { .ds-ajuda-balao { transition: none; transform: none; } }
