# HibouPay Design System v0.4.0

> Papel para decidir. Vidro para navegar.

A fonte de cor, espaço, vidro e comportamento do checkout, do backoffice e dos produtos que vierem depois. Cada peça tem uma razão escrita.

## Princípios

1. **Papel e vidro.** Papel guarda dado e decisão. Vidro só no que navega por cima: barras, busca, compositor, toast.
2. **A coruja é o agente.** Nenhum ícone genérico de IA. A coruja pisca quando vigia e ganha órbita quando trabalha.
3. **Mono é máquina.** Índices, IDs, rótulos e estados em mono. O que a pessoa lê fica em Inter; o que decide, em Sora.
4. **Raios concêntricos.** Raio interno = externo − padding. Cartão 16 com 4 de respiro abriga controle 12.
5. **Uma margem só.** 16 · 24 · 40 por largura. Nenhuma tela inventa o próprio padding lateral.
6. **Luz, não faixa.** Tom aparece como ponto e brilho. Nada de barra colorida no topo do cartão.

## Cor

### Marca

- **Black** `#000000` (0 · 0 · 0) — Robustez. Fim da escala corporativa, fundo da superfície escura.
- **Graffiti** `#2E3440` (46 · 52 · 64) — Credibilidade. Tinta de texto, sidebar, dock e toda sombra.
- **Vivid Blue** `#0061F1` (0 · 97 · 241) — Confiança e tecnologia. Ação principal, foco e luz.
- **White** `#FFFFFF` (255 · 255 · 255) — Transparência. Papel onde o dado mora.

**Escala de tom** (Tom corporativo → Tom popular): Cada tela escolhe onde pousa. Relatório e dinheiro puxam para o corporativo; convite e ação puxam para o popular.

### Neutros e estado

- **canvas** (`color.bg`) — Fundo da aplicação. Nunca recebe dado direto; o dado mora no papel.
- **paper** (`color.card`) — Cartão, tabela, formulário, diálogo.
- **wash** (`color.wash`) — Skeleton e áreas rebaixadas.
- **muted** (`color.muted`) — Texto de apoio, rótulo de máquina, ícone inativo.
- **success** (`color.success`) — Concluído, aplicado, liquidado. Texto usa success-ink.
- **warning** (`color.warning`) — Aguardando decisão, variação ruim. Texto usa warning-ink.
- **danger** (`color.danger`) — Falha, ação destrutiva. Texto usa danger-ink.
- **secondary** (`color.secondary`) — Rascunho, experimento. Nunca como ação principal.

## Vidro

Mesmo fundo, mesmo desenho, três níveis de transparência. Escolha um e ele vale para barra, busca, popover, toast, sidebar e dock no produto inteiro.

- **Base** (`base`, branco 56→40% · blur 32 · sat 200%) — Lê a cor de trás, não a forma. Mais sóbrio, mais corporativo.
- **Mediano** (`medio`, branco 40→24% · blur 24 · sat 220%) — A forma de trás aparece borrada. Brilho na quina superior.
- **Intenso** (`intenso`, branco 20→8% · blur 14 · sat 250%) — Quase líquido: vê-se o que está atrás. Borda e brilho seguram a leitura.

Onde usar:
- Barra de topo e cabeçalho móvel
- Busca global e paleta de comandos
- Popover de filtro e menu
- Toast
- Sidebar e dock (vidro noturno)

Nunca:
- Tabela, formulário ou qualquer dado que precise ser lido com atenção
- Diálogo de decisão (é papel sobre scrim)
- Sobre foto sem scrim
- Empilhado sobre outro vidro

## Tipografia

- **Display** — Sora 600 · clamp(32→44px) · −0.035em. Título de página e herói. Um por tela. (`text-hb-display`)
- **Título** — Sora 600 · 16 · −0.015em. Cabeçalho de cartão, diálogo e seção. (`font-sora text-hb-title font-semibold`)
- **Subtítulo** — Sora 600 · 14. Linha de apoio sob o título. O código já usava; agora tem nome. (`font-sora text-hb-body font-semibold`)
- **Métrica** — Sora tabular · 26/30. Número grande de cartão. 30 quando é o herói da área. (`font-sora text-hb-metric tabular-nums`)
- **Número** — Sora tabular · −0.02em. Valor, contagem, dinheiro. Alinha em coluna. (`hb-num`)
- **Corpo** — Inter 15 · 1.55. Texto corrido, descrição, ajuda. (`text-hb-lead leading-relaxed`)
- **Máquina** — JetBrains Mono 12. ID, código, hash, modelo, custo. (`font-mono text-hb-small`)
- **Rótulo** — JetBrains Mono 500 · 11 · +0.12em · caixa alta. Cabeçalho de tabela, rótulo de campo, índice de seção. (`hb-label`)

## Movimento

- **instant** `120ms` — Fecha a pálpebra. Hover, troca de cor.
- **strike** `150ms` — O corpo do controle viaja antes da luz. Thumb do Switch.
- **quick** `200ms` — Botão, chip, toast, drawer.
- **calm** `280ms` — Abre a pálpebra. Diálogo que assenta. Aba que desliza.
- **slow** `420ms` — Bloco que entra na tela, tile ativo da navegação.
- **pupil** `80ms` — Atraso da luz. Entra depois que o corpo chegou.
- **ease** `cubic-bezier(0.16, 1, 0.3, 1)` — Curva padrão: sai rápido, pousa devagar.
- **spring** `cubic-bezier(0.34, 1.36, 0.64, 1)` — Só para confirmação alegre: passa um pouco e volta.

## Coruja

- **parado** — Sem estado: a marca como assinatura, sem movimento.
- **idle** — Vigiando. Pisca a cada 6 s. Use onde o agente está disponível mas não age.
- **working** — Trabalhando. Ganha órbita azul. Use enquanto o agente lê, busca ou escreve.

## Componentes

### Button · Ação · `stable`

Ação. Uma altura por tamanho, compartilhada com input e chip. Toque afunda 2%. No foco, o anel de 2px chega agora e o halo azul entra depois.

`import { Button, buttonVariants, buttonSizes } from "@hiboupay/react";`

**Quando usar**
- Disparar uma ação na tela atual: salvar, confirmar, abrir diálogo.
- Confirmar ou cancelar dentro de diálogo e gate.
- Ação de ícone em barra densa (size icon / icon-sm) com aria-label.

**Quando não usar**
- Navegar para outra página: use link.
- Alternar entre vistas: use Tabs.
- Filtrar lista: use FilterChip.

**Anatomia:** Contêiner com altura de controle (28 · 32 · 40 · 48); Ícone opcional à esquerda ou direita (lucide, tamanho automático); Rótulo com verbo; Spinner sobreposto quando loading (a largura não muda).

**Variantes**
- `primary` — Vivid Blue em degradê. Uma por área. A ação que a pessoa veio fazer.
- `night` — Graffiti. Ação forte que não é a principal, ou principal sobre fundo azul.
- `outline` — Papel com anel. Ação alternativa ao lado da primária.
- `secondary` — Violeta suave. Rascunho, experimento.
- `ghost` — Sem fundo. Cancelar, ação terciária.
- `glass` — Sobre vidro ou canvas com aurora.
- `success` — Concluir de forma positiva (liquidar, aprovar).
- `danger` — Destrutiva. Quase sempre abre ConfirmGate.
- `link` — Saída discreta, sem caixa. Voltar, cancelar navegação.

**Estados:** default, hover, active (scale 0.98), focus-visible (anel 2px + offset 2px), loading (aria-busy, largura mantida), disabled (opacidade 55%).

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `variant` | `"primary" \| "night" \| "secondary" \| "outline" \| "ghost" \| "glass" \| "link" \| "danger" \| "success"` | `"primary"` | Peso visual e intenção. |
| `size` | `"xs" \| "sm" \| "md" \| "lg" \| "icon" \| "icon-sm"` | `"md"` | xs 28 · sm 32 · md 40 · lg 48. icon é quadrado de 40; icon-sm de 32. |
| `loading` | `boolean` | `false` | Troca o conteúdo por spinner mantendo a largura. Também desabilita e liga aria-busy. |
| `type` | `"button" \| "submit" \| "reset"` | `"button"` | Padrão button para não submeter formulário por acidente. |
| `...props` | `ButtonHTMLAttributes` | — | Qualquer atributo nativo de <button>. ref é encaminhado. |

**Tokens:** `color.primary`, `color.primaryLight`, `color.primaryDeep`, `color.secondaryInk`, `color.graffiti`, `color.graffitiDeep`, `color.graffitiLight`, `type.small`, `type.compact`, `type.body`, `type.lead`, `control.xs`, `control.sm`, `control.md`, `control.lg`, `radius.sm`, `radius.md`, `radius.lg`, `motion.ease`, `motion.quick`, `motion.pupil`.

**Teclado**
- Tab: Move o foco para o botão.
- Enter / Espaço: Ativa.

**Leitor de tela**
- Anunciado como botão com o rótulo.
- size icon / icon-sm exige aria-label.
- loading anuncia ocupado (aria-busy).

**Fazer**
- Uma primária por área de decisão.
- Rótulo com verbo: "Confirmar e assinar", "Estornar R$ 1.200".
- Mesma altura do input ao lado.

**Evitar**
- Duas primárias lado a lado.
- Botão para navegar.
- Rótulo genérico ("OK", "Clique aqui").

**Conteúdo**
- Verbo no infinitivo ou imperativo curto, até 3 palavras.
- Inclua o objeto quando houver dinheiro: "Pagar R$ 90".

### Card · Superfície · `stable`

Superfície. Quatro materiais: papel guarda dado e decisão, rebaixado abriga prévia, vidro só no que flutua, noite é a superfície da marca.

`import { Card, CardHeader, CardFooter, SectionTitle, Kicker } from "@hiboupay/react";`

**Quando usar**
- Agrupar dado relacionado com título e ações.
- Item clicável em grade (interactive).
- Bloco de métrica, formulário ou prévia.

**Quando não usar**
- Envolver a página inteira.
- Card dentro de card com o mesmo material.
- Vidro sobre dado que precisa de leitura atenta.

**Anatomia:** Contêiner raio 16 com material; CardHeader: índice mono, rótulo (kicker), título Sora, descrição, ações; Corpo livre; CardFooter: divisor e ações à direita.

**Variantes**
- `paper · e1` — O padrão. Dado, formulário, decisão.
- `sunken · e0` — Trilho de segmento, área de teste, prévia.
- `glass · e2` — Só no que flutua. Combine com hb-lens para a borda de luz.
- `night · e2` — Graffiti. Cabeçalho do agente, destaque de marca.

**Estados:** default, interactive: fica no lugar, sombra e2, borda de luz nasce depois.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `material` | `"paper" \| "sunken" \| "glass" \| "night"` | `"paper"` | Material da superfície. |
| `padding` | `"none" \| "sm" \| "md" \| "lg"` | `"md"` | Cresce com a tela. O raio interno de quem está dentro é 16 − padding. |
| `interactive` | `boolean` | `false` | Acende a borda de luz no hover, sem levantar o cartão. |
| `hover` | `boolean` | — | Obsoleto. Use interactive. |
| `CardHeader.index` | `string \| number` | — | Número vira dois dígitos (1 → 01). |
| `CardHeader.kicker` | `ReactNode` | — | Rótulo de máquina acima do título. |
| `CardHeader.title` | `ReactNode` | — | Título em Sora 16. |
| `CardHeader.description` | `ReactNode` | — | Texto de apoio. |
| `CardHeader.actions` | `ReactNode` | — | Botões. Quebram para baixo no celular. |
| `CardHeader.divider` | `boolean` | — | Linha abaixo do cabeçalho. |

**Tokens:** `color.card`, `radius.lg`, `elevation.0`, `elevation.1`, `elevation.2`, `motion.ease`.

**Teclado**
- —: Card não é focável. Se for clicável, envolva um link ou botão real.

**Leitor de tela**
- CardHeader.title é h3; ajuste a hierarquia da página em volta.
- Não coloque role=button no Card; use link ou botão dentro.

**Fazer**
- Um assunto por cartão.
- Ações no cabeçalho ou no rodapé, não nos dois.
- Raio concêntrico para o que está dentro.

**Evitar**
- Faixa colorida no topo para indicar estado.
- Sombra preta.
- Card com padding lg dentro de card.

**Conteúdo**
- Título diz o assunto, não a ação.
- Kicker em mono, curto: "paper · e1", "Etapa 2".

### Badge · Estado · `stable`

Etiqueta de estado com raio 6, nunca pílula. O ponto diz que o estado está vivo. O app mapeia o domínio para um tom do Core.

`import { Badge, badgeTones } from "@hiboupay/react";`

**Quando usar**
- Estado de um item: aplicado, aguardando, falhou.
- Código curto (P0, L2, v1) com code.
- Categoria discreta em tabela ou cartão.

**Quando não usar**
- Ação clicável: use Button.
- Filtro: use FilterChip.
- Texto longo.

**Anatomia:** Contêiner 20px, raio 6, anel interno; Ponto opcional em currentColor; Rótulo 11px (ou mono 11 com code).

**Variantes**
- `default` — Neutro.
- `muted` — Expirado, inativo.
- `primary` — Em andamento, agente.
- `secondary` — Rascunho.
- `success` — Concluído, aplicado.
- `warning` — Aguardando decisão.
- `danger` — Falhou.
- `night` — Nível, código de destaque.

**Estados:** estático, com ponto (estado vivo).

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `tone` | `"default" \| "muted" \| "primary" \| "secondary" \| "success" \| "warning" \| "danger" \| "night"` | `"default"` | Tom do Core. |
| `dot` | `boolean` | `false` | Ponto que indica estado vivo. |
| `code` | `boolean` | `false` | Mono em caixa, para códigos curtos (P0, L2, v1). |
| `...props` | `HTMLAttributes<HTMLSpanElement>` | — | Atributos de <span>. |

**Tokens:** `radius.xs`, `color.successInk`, `color.warningInk`, `color.dangerInk`, `color.primaryDeep`.

**Teclado**
- —: Não é interativo.

**Leitor de tela**
- Lido como texto. Se o estado for só cor, o rótulo precisa dizer o estado.
- O ponto é aria-hidden.

**Fazer**
- Rótulo de uma ou duas palavras, minúsculas.
- Mapear domínio → tom no app.

**Evitar**
- Criar tom novo no Core para um domínio.
- Pílula totalmente arredondada.
- Badge clicável.

**Conteúdo**
- Minúsculas, particípio ou substantivo: "aplicado", "rascunho".

### Input · Entrada · `stable`

Papel com anel fino. Foco vira azul com halo de 4px. Erro diz o que fazer, com ícone e aria.

`import { Input, Label, Field, fieldFrame } from "@hiboupay/react";`

**Quando usar**
- Texto curto de uma linha: nome, e-mail, valor, busca.
- Busca em barra com leading de ícone.

**Quando não usar**
- Texto longo: Textarea.
- Escolha de lista fechada: Select ou FilterChip.

**Anatomia:** Label (hb-label) opcional acima; Campo com anel interno; leading / trailing opcionais; Mensagem: dica ou erro com ícone.

**Variantes**
- `sm · 32` — Barras densas.
- `md · 40` — Padrão.
- `lg · 48` — Formulário de destaque, celular.

**Estados:** default, hover (anel mais escuro), focus (anel azul + halo 4px), error (anel e halo danger, aria-invalid), disabled.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `label` | `string` | — | Rótulo visível, ligado por htmlFor. |
| `hint` | `string` | — | Dica abaixo, ligada por aria-describedby. |
| `error` | `string` | — | Mensagem de erro. Liga aria-invalid e substitui a dica. |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 32 · 40 · 48. Acompanhe o botão ao lado. |
| `leading` | `ReactNode` | — | Ícone à esquerda (decorativo). |
| `trailing` | `ReactNode` | — | Ícone ou ação à direita. |
| `...props` | `InputHTMLAttributes` | — | Atributos nativos. ref é encaminhado; id é gerado se ausente. |

**Tokens:** `control.sm`, `control.md`, `control.lg`, `radius.md`, `color.primary`, `color.danger`, `color.dangerInk`, `motion.quick`.

**Teclado**
- Tab: Foca o campo.

**Leitor de tela**
- Label associado por id.
- Erro e dica anunciados por aria-describedby.
- aria-invalid quando há erro.

**Fazer**
- Rótulo sempre visível.
- Erro com instrução e exemplo.

**Evitar**
- Placeholder como rótulo.
- Erro só em cor.

**Conteúdo**
- Rótulo substantivo curto: "E-mail do decisor".
- Placeholder é exemplo: "nome@empresa.com".

### Select · Entrada · `stable`

Campo nativo. Mesmo anel e a mesma altura do Input, com a seta Graffiti do campo. O menu aberto é o do sistema.

`import { Select } from "@hiboupay/react";`

**Quando usar**
- Escolher um valor de lista curta dentro de formulário.
- Quando o picker nativo do celular é desejável.

**Quando não usar**
- Filtro em barra: FilterChip.
- Mais de ~15 opções com busca: FilterChip searchable.

**Anatomia:** Label; Campo com seta à direita; Mensagem: dica ou erro.

**Estados:** default, hover, focus, error, disabled.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `label` | `string` | — | Rótulo visível. |
| `hint` | `string` | — | Dica abaixo. |
| `error` | `string` | — | Erro; liga aria-invalid. |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 32 · 40 · 48. |
| `children` * | `<option> \| <optgroup>` | — | Opções nativas. |

**Tokens:** `control.md`, `radius.md`, `color.muted`.

**Teclado**
- Espaço / Alt+↓: Abre as opções.
- ↑ ↓: Troca a opção.

**Leitor de tela**
- Combobox nativo, anunciado pelo sistema.

**Fazer**
- Opção padrão sensata ou placeholder desabilitado.

**Evitar**
- Select para duas opções: use Tabs segment.

**Conteúdo**
- Opções em ordem lógica (fluxo), não alfabética, quando houver fluxo.

### Textarea · Entrada · `stable`

Campo de várias linhas. Mesmo anel do Input. Mínimo de 96px, cresce na vertical.

`import { Textarea } from "@hiboupay/react";`

**Quando usar**
- Motivo, nota, descrição.
- Motivo obrigatório do ConfirmGate.

**Quando não usar**
- Uma linha: Input.

**Anatomia:** Label; Área de texto; Mensagem.

**Estados:** default, hover, focus, error, disabled.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `label` | `string` | — | Rótulo visível. |
| `hint` | `string` | — | Dica abaixo. |
| `error` | `string` | — | Erro; liga aria-invalid. |
| `...props` | `TextareaHTMLAttributes` | — | Atributos nativos. |

**Tokens:** `radius.md`, `color.primary`.

**Teclado**
- Tab: Foca. Enter quebra linha.

**Leitor de tela**
- Igual ao Input.

**Fazer**
- Dica dizendo para onde o texto vai: "Vai para o histórico."

**Evitar**
- Limite de caracteres escondido.

**Conteúdo**
- Placeholder com exemplos reais separados por ponto e vírgula.

### Tabs · Entrada · `stable`

Segmento: trilho rebaixado com um papel que desliza até a aba ativa. Linha: sublinhado azul para conteúdo longo. Base Radix.

`import { Tabs, TabsList, TabsTrigger, TabsContent } from "@hiboupay/react";`

**Quando usar**
- Alternar vistas do mesmo conjunto (Todos, Aguardando, Decididos).
- Seções de um detalhe (line).

**Quando não usar**
- Navegação entre páginas.
- Mais de 6 abas.

**Anatomia:** TabsList (trilho); Indicador deslizante (papel ou linha); TabsTrigger com contagem opcional; TabsContent (entra só por opacidade).

**Variantes**
- `segment` — Padrão. Filtro de vista compacto.
- `line` — Abas de conteúdo longo, largura total.

**Estados:** inativa, hover, ativa, focus-visible.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `Tabs.defaultValue / value` | `string` | — | Aba inicial (não controlado) ou atual (controlado). |
| `Tabs.onValueChange` | `(value: string) => void` | — | Troca de aba. |
| `TabsList.variant` | `"segment" \| "line"` | `"segment"` | Desenho do trilho. |
| `TabsTrigger.value` * | `string` | — | Identificador da aba. |
| `TabsTrigger.count` | `number` | — | Contagem em Sora tabular ao lado do rótulo. |

**Tokens:** `radius.md`, `radius.sm`, `color.primary`, `motion.ease`.

**Teclado**
- ← →: Move entre abas.
- Home / End: Primeira / última.
- Tab: Sai para o conteúdo.

**Leitor de tela**
- tablist / tab / tabpanel com aria-selected (Radix).

**Fazer**
- Rótulos curtos e paralelos.
- count para o que pede atenção.

**Evitar**
- Aba que dispara ação.

**Conteúdo**
- Substantivo ou particípio no plural: "Decididos".

### FilterChip · Entrada · `beta`

Chip de filtro: rótulo + valor, popover de vidro. Ativo vira papel com anel azul e ganha X para limpar.

`import { FilterChip, ChipOption } from "@hiboupay/react";`

**Quando usar**
- Barra de filtros sobre lista ou quadro.
- Seleção múltipla (padrão) ou única (single).

**Quando não usar**
- Campo de formulário: Select.
- Alternar vista: Tabs.

**Anatomia:** Gatilho: rótulo, valor resumido, seta; Botão limpar (quando ativo); Popover de vidro com lente; Busca opcional; Opções com caixa ou rádio, ponto de acento e dica mono; Limpar seleção.

**Estados:** inativo, hover, aberto, ativo (anel azul), vazio na busca.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `label` * | `string` | — | Dimensão filtrada. |
| `options` * | `ChipOption[]` | — | { value, label, hint?, accent? }. |
| `value` * | `string[]` | — | Valores selecionados. |
| `onChange` * | `(next: string[]) => void` | — | Nova seleção. |
| `single` | `boolean` | `false` | Uma opção por vez (rádio). |
| `searchable` | `boolean` | `false` | Busca no topo do popover. |
| `renderOption` | `(option, selected) => ReactNode` | — | Conteúdo customizado da opção. |
| `emptyLabel` | `string` | `"Nada encontrado"` | Texto quando a busca não acha. |
| `clearLabel` | `string` | `"Limpar seleção"` | Rótulo do limpar no rodapé. |

**Tokens:** `control.sm`, `radius.lg`, `color.primary`, `color.primaryDeep`, `glass.base.blur`, `motion.calm`.

**Teclado**
- Enter / Espaço: Abre o popover.
- ↑ / ↓: Percorre as opções.
- Home / End: Primeira e última opção.
- Tab: Percorre as opções.
- Esc: Fecha e devolve o foco.

**Leitor de tela**
- Grupo de opções. Múltipla: role=checkbox com aria-checked. Única: role=radio com aria-checked.
- Limpar tem aria-label "Limpar {label}".

**Fazer**
- Uma dimensão por chip.
- searchable acima de 8 opções.

**Evitar**
- Cor de estado no chip.
- Chip que some quando não há resultado.

**Conteúdo**
- Rótulo substantivo singular: "Status", "Período".

### Modal · Diálogo · `stable`

Papel sobre scrim de Graffiti. No celular vira folha que sobe; no desktop pousa e a borda de luz acende depois. O rodapé é a faixa da decisão; o corpo rola.

`import { Modal } from "@hiboupay/react";`

**Quando usar**
- Tarefa curta que precisa de foco: atribuir, editar um campo, revisar.
- Detalhe que não merece página.

**Quando não usar**
- Confirmação de ação irreversível: ConfirmGate.
- Fluxo longo com várias etapas: página.

**Anatomia:** Scrim (hb-scrim); Alça (só no celular); Título Sora 18 e descrição; Fechar (X); Corpo com rolagem; Rodapé com ações.

**Variantes**
- `sm · 420` — Gate, confirmação.
- `md · 520` — Padrão.
- `lg · 720` — Formulário com duas colunas.
- `xl · 960` — Revisão com tabela.

**Estados:** fechado, abrindo (folha no celular, assenta no desktop), aberto.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `open` * | `boolean` | — | Controlado. |
| `onOpenChange` * | `(open: boolean) => void` | — | Fechar por X, Esc ou clique no scrim. |
| `title` * | `ReactNode` | — | Título acessível. |
| `description` | `ReactNode` | — | Descrição acessível. |
| `footer` | `ReactNode` | — | Ações. Empilham no celular com a principal por baixo. |
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` | Largura máxima no desktop. |
| `children` | `ReactNode` | — | Corpo. |

**Tokens:** `radius.xl`, `elevation.3`, `motion.ease`.

**Teclado**
- Esc: Fecha.
- Tab: Circula dentro do diálogo (foco preso).

**Leitor de tela**
- role=dialog com aria-labelledby (title) e aria-describedby (description).
- Foco volta ao gatilho ao fechar.

**Fazer**
- Título diz a tarefa.
- Ação principal repete o verbo do título.

**Evitar**
- Modal sobre modal.
- Modal para aviso que não pede decisão.

**Conteúdo**
- Título: verbo + objeto ("Atribuir responsável").

### ConfirmGate · Diálogo · `stable`

Gate para ações irreversíveis: dinheiro, permissão, exclusão, execução de agente. Pode exigir motivo e palavra digitada.

`import { ConfirmGate } from "@hiboupay/react";`

**Quando usar**
- Qualquer ação que não volta.
- Ação cujo motivo precisa ir para auditoria.

**Quando não usar**
- Ação desfazível: execute e ofereça desfazer.

**Anatomia:** Ícone de escudo com tom; Título e consequência; Conteúdo extra opcional; Motivo (Textarea) opcional; Palavra de confirmação (Input mono) opcional; Cancelar (ghost) + Confirmar (tom).

**Variantes**
- `danger` — Destrutiva (padrão).
- `warning` — Arriscada, mas não destrutiva.
- `primary` — Execução que precisa de consentimento.
- `success` — Conclusão positiva irreversível.

**Estados:** bloqueado (motivo ou palavra faltando), liberado, loading.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `open / onOpenChange` * | `boolean / (open) => void` | — | Controlado. |
| `title` * | `ReactNode` | — | O que vai acontecer. |
| `description` | `ReactNode` | — | Consequência. |
| `tone` | `"danger" \| "primary" \| "success" \| "warning"` | `"danger"` | Cor do ícone e do botão. |
| `confirmLabel` | `string` | `"Confirmar"` | Repita o verbo da ação. |
| `cancelLabel` | `string` | `"Cancelar"` | Rótulo do cancelar. |
| `requireReason` | `boolean` | `false` | Exige motivo de ao menos 3 caracteres. |
| `reasonLabel` | `string` | `"Motivo (obrigatório)"` | Rótulo do motivo. |
| `reasonPlaceholder` | `string` | — | Exemplo de motivo. |
| `requireTypedConfirm` | `string` | — | Palavra exata a digitar (ex.: EXCLUIR). |
| `onConfirm` * | `({ reason?: string }) => void \| Promise<void>` | — | Recebe o motivo. |
| `loading` | `boolean` | — | Botão de confirmar em loading. |

**Tokens:** `color.danger`, `color.dangerInk`, `color.warningInk`, `radius.xl`.

**Teclado**
- Esc: Cancela.
- Tab: Motivo → Cancelar → Confirmar.

**Leitor de tela**
- Herdado do Modal.
- Botão desabilitado até cumprir as exigências.

**Fazer**
- Diga a consequência em uma frase.
- Motivo quando a ação entra no histórico.

**Evitar**
- "Tem certeza?" sem consequência.
- Confirmar com rótulo genérico.

**Conteúdo**
- Título: "Estornar pagamento". Descrição: "O valor volta ao cliente em até 2 dias. Não dá para desfazer."

### DataTable · Dados · `stable`

Tabela de trabalho em papel. Linha de 48 (36 no denso), cabeçalho de máquina que ordena, número à direita em Sora tabular. O link da linha é o alvo do teclado.

`import { DataTable, Column } from "@hiboupay/react";`

**Quando usar**
- Lista de registros com colunas comparáveis.
- Dado financeiro que precisa alinhar.

**Quando não usar**
- Poucos itens ricos: cartões.
- Layout de página.

**Anatomia:** Contêiner papel e1 com rolagem horizontal; Cabeçalho hb-label, opaco, que gruda quando pedido; Botão de ordenação com seta Graffiti; Linhas com divisor 5%; Link ou botão na primeira célula quando a linha abre; Célula numérica hb-num à direita; Vazio com EmptyState compacto; Carregando com esqueleto na altura da linha.

**Variantes**
- `padrão · 48` — Leitura confortável.
- `dense · 36` — Muitas linhas, painel operacional.

**Estados:** vazio, carregando, com linhas, linha hover, coluna ordenada.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `columns` * | `Column<T>[]` | — | { key, header, cell(row), align?, width?, sortable?, sortValue?, className? }. |
| `rows` * | `T[]` | — | Dados. |
| `rowKey` * | `(row: T) => string` | — | Chave estável. |
| `onRowClick` | `(row: T) => void` | — | Ação de mouse. O teclado usa um botão na primeira célula. |
| `rowHref` | `(row: T) => string` | — | Link real na primeira célula. O ::after cobre a linha. |
| `sort` | `SortState \| null` | — | Ordem controlada. Sem isto, a tabela guarda. |
| `onSortChange` | `(sort: SortState) => void` | — | Nova ordem ao clicar no cabeçalho. |
| `loading` | `boolean` | — | Corpo vira esqueleto. aria-busy na tabela. |
| `stickyHeader` | `boolean` | — | Cabeçalho gruda no topo do scroll. |
| `empty` | `ReactNode \| string` | `"Nada por aqui."` | Conteúdo quando não há linhas. |
| `dense` | `boolean` | `false` | Linha de 36px. |
| `caption` | `string` | — | Legenda acessível (sr-only). |

**Tokens:** `radius.lg`, `elevation.1`, `font.mono`, `font.heading`, `type.body`, `type.caption`.

**Teclado**
- Enter / Espaço: Ordena o cabeçalho.
- Tab: Foca o link ou o botão da linha.
- Enter: Abre a linha.

**Leitor de tela**
- Tabela semântica com th scope=col.
- Use caption para nomear a tabela.

**Fazer**
- Números à direita.
- ID em mono.
- Célula vazia fica vazia.

**Evitar**
- Zebra.
- Centralizar números.

**Conteúdo**
- Cabeçalho curto, substantivo.

### Avatar · Dados · `beta`

Iniciais em Sora sobre uma cor de token: Vivid Blue, Graffiti, success-ink, warning-ink ou danger-ink. A cor vem do e-mail, então a mesma pessoa tem sempre a mesma cor. Ninguém atribuído é tracejado.

`import { Avatar, AvatarPlaceholder, avatarPalette, avatarHue, initials } from "@hiboupay/react";`

**Quando usar**
- Pessoa responsável em lista, cartão, conta.

**Quando não usar**
- Empresa ou marca: use logo.

**Anatomia:** Círculo na cor do token; Iniciais Sora; Brilho interno.

**Variantes**
- `xs · 20` — Tabela densa.
- `sm · 24` — Padrão em lista.
- `md · 32` — Menu de conta.
- `lg · 40` — Perfil.

**Estados:** com pessoa, AvatarPlaceholder (ninguém).

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `name` | `string \| null` | — | Nome para as iniciais. |
| `email` | `string \| null` | — | Chave da cor; fallback das iniciais. |
| `size` | `"xs" \| "sm" \| "md" \| "lg"` | `"sm"` | 20 · 24 · 32 · 40. |
| `title` | `string` | — | Tooltip; padrão é o nome ou e-mail. |
| `AvatarPlaceholder.title` | `string` | `"Ninguém atribuído"` | Explica a ausência. |

**Tokens:** `font.heading`, `color.primary`, `color.graffiti`, `color.successInk`, `color.warningInk`, `color.dangerInk`, `color.muted`.

**Teclado**
- —: Não é interativo.

**Leitor de tela**
- Avatar expõe o nome por title; ponha o nome em texto ao lado quando for essencial.
- AvatarPlaceholder é role=img com aria-label.

**Fazer**
- Mesmo tamanho numa lista.

**Evitar**
- Inventar pessoa quando não há.
- Cor manual.

**Conteúdo**
- title do placeholder diz o que fazer: "Sem responsável — atribua alguém".

### MetricWidget · Dados · `beta`

Métrica: rótulo de máquina, número em Sora tabular, contexto embaixo. O tom é luz, não faixa. Bar mostra proporção.

`import { MetricWidget, Bar } from "@hiboupay/react";`

**Quando usar**
- Número-chave de painel com contexto.
- Progresso contra meta (Bar no footer).

**Quando não usar**
- Série temporal: gráfico.
- Mais de 4 por linha.

**Anatomia:** Ponto do tom + rótulo hb-label; Valor 26–30 hb-num; Delta mono; Sub; Rodapé opcional (Bar).

**Estados:** sem tom, com tom, delta ok, delta aviso.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `label` * | `string` | — | O que é medido. |
| `value` * | `ReactNode` | — | Valor formatado pelo app. |
| `sub` | `ReactNode` | — | Contexto. |
| `delta` | `{ label: string; ok: boolean } \| null` | — | Variação. ok=false pinta aviso, não perigo. |
| `tone` | `"primary" \| "secondary" \| "success" \| "warning" \| "danger"` | — | Luz do cartão. |
| `footer` | `ReactNode` | — | Área abaixo com divisor. |
| `Bar.value / Bar.max` * | `number` | — | Proporção (limitada a 100%). |
| `Bar.tone` | `MetricTone \| "muted"` | `"primary"` | Primário usa degradê Graffiti → Vivid Blue. |
| `Bar.label` | `ReactNode` | — | Linha acima da barra. |

**Tokens:** `font.heading`, `radius.xs`, `color.successInk`, `color.warningInk`.

**Teclado**
- —: Não é interativo.

**Leitor de tela**
- Bar é role=meter com aria-valuenow em %.
- Leia rótulo e valor juntos; evite abreviações sem contexto.

**Fazer**
- Valor com unidade.
- Delta curto: "+4 pp".

**Evitar**
- Delta ruim em danger.
- Faixa colorida no topo.

**Conteúdo**
- Rótulo curto, substantivo: "Liquidado", "Custo".

### EmptyState · Estado · `stable`

Vazio: a coruja vigia até ter o que mostrar. Sem ilustração genérica. Sempre que puder, um próximo passo. É o StatusView no tom neutro.

`import { EmptyState } from "@hiboupay/react";`

**Quando usar**
- Lista ou área sem dados.
- Resultado de filtro vazio.
- Primeiro uso.

**Quando não usar**
- Erro recuperável numa área: ErrorBanner.
- Esperando, aprovado ou recusado: StatusView com o tom certo.

**Anatomia:** Halo azul radial; Tile de papel com a coruja (ou ícone); Título Sora 15; Descrição; Ação.

**Variantes**
- `padrão` — Área principal.
- `compact` — Dentro de tabela ou cartão.

**Estados:** estático.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `title` * | `string` | — | O que está vazio. |
| `description` | `string` | — | Por quê, ou o que fazer. |
| `action` | `ReactNode` | — | Próximo passo. |
| `icon` | `ReactNode` | — | Troca a coruja por um ícone. |
| `compact` | `boolean` | `false` | Menos respiro. |

**Tokens:** `elevation.1`, `font.heading`, `color.muted`.

**Teclado**
- —: Só a ação é focável.

**Leitor de tela**
- role=status: anunciado quando aparece.

**Fazer**
- Diga o próximo passo.

**Evitar**
- "Nenhum registro encontrado." sem saída.

**Conteúdo**
- Título: "Nada por aqui ainda." Descrição: o que traz dado para cá.

### ErrorBanner · Estado · `stable`

Erro recuperável com a saída já montada: causa em linguagem humana e "Tentar de novo". É o Notice de perigo com o botão pronto.

`import { ErrorBanner } from "@hiboupay/react";`

**Quando usar**
- Falha ao carregar ou salvar uma área.

**Quando não usar**
- Erro de campo: use error no Input.
- Página inteira quebrada: StatusView danger.
- Aviso que não é falha: Notice.

**Anatomia:** Ícone de alerta em tile danger; Mensagem; Tentar de novo (opcional).

**Estados:** sem ação, com ação.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `message` * | `string` | — | Causa, na língua da pessoa. |
| `onRetry` | `() => void` | — | Mostra o botão de tentar de novo. |
| `retryLabel` | `string` | `"Tentar de novo"` | Rótulo do botão. |

**Tokens:** `color.danger`, `color.dangerInk`, `radius.lg`.

**Teclado**
- Tab: Foca Tentar de novo.

**Leitor de tela**
- role=alert: anunciado na hora.

**Fazer**
- Diga a causa.

**Evitar**
- Código técnico sem tradução.

**Conteúdo**
- "A chave foi recusada pelo provedor."

### Spinner · Estado · `stable`

Arco curto em currentColor, para dentro de um botão ou ao lado de um texto.

`import { Spinner } from "@hiboupay/react";`

**Quando usar**
- Ação curta dentro de botão (automático com loading).
- Indicador pequeno ao lado de texto.

**Quando não usar**
- Carregando lista: Skeleton.
- Agente trabalhando: HibouMark working.
- Espera de tela: StatusView working.

**Anatomia:** Trilho 20% de opacidade; Arco de 90°.

**Estados:** girando.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `label` | `string` | — | Nome acessível quando está sozinho (vira role=status). |
| `className` | `string` | — | Tamanho e cor (text-*). |

**Tokens:** `color.primary`.

**Teclado**
- —: Não é interativo.

**Leitor de tela**
- Sem label: aria-hidden.
- Com label: role=status.

**Fazer**
- label quando sozinho.

**Evitar**
- Spinner de página inteira.

**Conteúdo**
- label: "Carregando pagamentos".

### Skeleton · Estado · `beta`

A forma da peça que ainda não chegou. Raio, altura e etiqueta iguais aos da peça real. Sempre aria-hidden; quem carrega anuncia.

`import { Skeleton, SkeletonCard, SkeletonRows, SkeletonMetric } from "@hiboupay/react";`

**Quando usar**
- Lista, tabela ou cartão carregando.

**Quando não usar**
- Ação curta: Button loading.

**Anatomia:** Espelha a peça que anuncia: linha 48 da tabela, cartão 16, etiqueta raio 6; Bloco wash com shimmer; Nunca pílula.

**Variantes**
- `Skeleton` — Bloco livre (dê altura e largura).
- `SkeletonCard` — Cartão com título Sora 16 e etiqueta raio 6.
- `SkeletonRows` — Linhas de 48 (36 no denso), a altura da tabela.
- `SkeletonMetric` — Métrica com barra.

**Estados:** shimmer (desliga com reduced-motion).

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `className` | `string` | — | Tamanho e raio do bloco. |
| `SkeletonRows.rows` | `number` | `6` | Quantidade de linhas. |

**Tokens:** `color.wash`, `motion.ease`, `radius.xs`, `radius.lg`, `type.title`, `type.body`.

**Teclado**
- —: Não é interativo.

**Leitor de tela**
- aria-hidden. Marque o contêiner com aria-busy ou use Spinner com label.

**Fazer**
- Mesma forma do conteúdo final.

**Evitar**
- Skeleton genérico que não lembra o conteúdo.

**Conteúdo**
- —

### HibouMark · Marca · `stable`

O símbolo oficial, com o traço exato do imagotipo. Ocioso ele pisca a cada 6 s; trabalhando ganha órbita. É o ícone do agente.

`import { HibouMark, OWL, OWL_STOPS } from "@hiboupay/icons";`

**Quando usar**
- Representar o agente.
- Vazio (EmptyState).
- Marca compacta no trilho da navegação.

**Quando não usar**
- Decoração repetida.
- Ícone genérico de IA.

**Anatomia:** Contêiner; SVG da coruja (degradê ou currentColor); Órbita (working).

**Variantes**
- `tone brand` — Degradê oficial sobre papel.
- `tone current` — Herda a cor do texto: noite, botão, ícone.

**Estados:** parado, idle (pisca), working (órbita).

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `tone` | `"brand" \| "current"` | `"brand"` | Degradê oficial ou cor do texto. |
| `state` | `"idle" \| "working"` | — | Sem estado fica parado. |
| `title` | `string` | — | Nome acessível. Sem title é decorativa. |
| `className` | `string` | — | Tamanho (h-* w-*). |

**Tokens:** `color.primary`.

**Teclado**
- —: Não é interativo.

**Leitor de tela**
- Sem title: aria-hidden.
- Com title: role=img nomeada.

**Fazer**
- working enquanto o agente age.

**Evitar**
- Redesenhar, esticar ou recolorir fora do degradê.
- Usar Sparkle ou robô no lugar.

**Conteúdo**
- title: "Agente trabalhando".

### Wordmark · Marca · `stable`

Imagotipo oficial: coruja + "hibou" + "pay". "pay" é sempre Vivid Blue; "hibou" é preto no claro e branco no escuro.

`import { Wordmark, HIBOU, PAY } from "@hiboupay/icons";`

**Quando usar**
- Topo da navegação.
- Login, e-mail, documento.

**Quando não usar**
- Espaço menor que 96px de largura: HibouMark.

**Anatomia:** Coruja em degradê; "hibou" em tinta; "pay" em Vivid Blue.

**Variantes**
- `onLight` — "hibou" preto.
- `onDark` — "hibou" branco.

**Estados:** estático.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `onDark` | `boolean` | `false` | Fundo escuro. |
| `title` | `string` | `"HibouPay"` | Nome acessível. |
| `className` | `string` | — | Altura (h-*); largura segue a proporção. |

**Tokens:** `brand.vividBlue`, `brand.black`, `brand.white`.

**Teclado**
- —: Se for link, o link recebe o foco.

**Leitor de tela**
- role=img com aria-label "HibouPay".

**Fazer**
- Respiro mínimo da altura da coruja em volta.

**Evitar**
- Trocar a cor de "pay".
- Aplicar sobre foto sem scrim.

**Conteúdo**
- —

### Checkbox · Entrada · `beta`

A mesma marca do FilterChip: papel com anel fino, azul preenchido com check branco. Independente ou em lote. Traço quando só alguns filhos estão marcados.

`import { Checkbox } from "@hiboupay/react";`

**Quando usar**
- Aceitar um termo no formulário.
- Marcar linhas ou filhos de um grupo.
- Preferência que não é ligar/desligar um sistema (isso é Switch).

**Quando não usar**
- Uma entre poucas opções exclusivas: Radio.
- Ligar uma configuração: Switch.
- Filtrar lista: FilterChip.

**Anatomia:** Caixa 16px, raio 5; Check branco ou traço (indeterminate); Rótulo Inter 14 à direita.

**Estados:** off, on, indeterminate, focus-visible (anel azul), disabled.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `label` | `ReactNode` | — | Texto clicável à direita da marca. |
| `indeterminate` | `boolean` | `false` | Traço. Use quando alguns filhos estão marcados. |
| `checked` | `boolean` | — | Controlado. |
| `onChange` | `ChangeEventHandler` | — | Nativo. Para controlado, leia e.target.checked. |

**Tokens:** `color.primary`, `radius.sm`, `motion.quick`.

**Teclado**
- Tab: Foca a caixa.
- Espaço: Liga ou desliga.

**Leitor de tela**
- input checkbox nativo. Rótulo associado por id.
- indeterminate é anunciado pelo sistema.

**Fazer**
- Rótulo que afirma o que fica marcado: "Enviar comprovante por e-mail".

**Evitar**
- Checkbox sozinho sem rótulo visível.
- Usar para on/off de produto (Switch).

**Conteúdo**
- Frase curta, sem ponto. Evite "Clique aqui".

### Radio · Entrada · `beta`

Uma entre poucas. Círculo da mesma família do FilterChip single. fieldset + legend em rótulo de máquina.

`import { RadioGroup, Radio } from "@hiboupay/react";`

**Quando usar**
- 2 a 5 opções exclusivas no formulário: método, prazo, tom.
- Quando todas as opções precisam aparecer ao mesmo tempo.

**Quando não usar**
- Lista longa: Select ou FilterChip.
- Ligar/desligar: Switch.
- Duas vistas: Tabs.

**Anatomia:** fieldset com legend (hb-label); Círculo 16px; Ponto branco interno quando ativo; Rótulo Inter 14.

**Estados:** off, on, focus-visible, disabled.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `RadioGroup.name` * | `string` | — | name nativo compartilhado. |
| `RadioGroup.value` | `string` | — | Controlado. |
| `RadioGroup.onValueChange` | `(value: string) => void` | — | Nova escolha. |
| `RadioGroup.label` | `ReactNode` | — | Legend visível. |
| `Radio.value` * | `string` | — | Valor desta opção. |
| `Radio.children` | `ReactNode` | — | Rótulo da opção. |

**Tokens:** `color.primary`, `radius.pill`, `motion.quick`.

**Teclado**
- Tab: Entra no grupo.
- ↑ ↓: Troca a opção.

**Leitor de tela**
- radiogroup nativo via fieldset. Cada Radio é input radio.

**Fazer**
- Mostre 2–5. A opção padrão deve ser a mais segura, não a mais lucrativa.

**Evitar**
- Radio para aceitar termo (Checkbox).
- Esconder opções atrás de "ver mais".

**Conteúdo**
- Rótulos paralelos: "PIX", "Boleto", "Cartão".

### Switch · Entrada · `beta`

Liga ou desliga uma configuração. Trilho com raio da escala: apagado em papel com anel Graffiti, aceso em Vivid Blue com luz interna. Thumb de papel.

`import { Switch } from "@hiboupay/react";`

**Quando usar**
- Preferência persistente: aviso por e-mail, modo compacto.
- Um bit. O efeito é imediato.

**Quando não usar**
- Escolher entre A e B: Radio.
- Filtrar lista: FilterChip.
- Ação com efeito irreversível: ConfirmGate.

**Anatomia:** Trilho 44×24, raio 8; Thumb 16 de papel, raio 5; kicker mono opcional; Rótulo Inter 14.

**Estados:** off, on, focus-visible, disabled.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `checked` | `boolean` | — | Controlado. |
| `onCheckedChange` | `(checked: boolean) => void` | — | Novo valor. |
| `label` | `ReactNode` | — | O que muda quando liga. |
| `kicker` | `string` | — | Rótulo de máquina acima do texto. |
| `disabled` | `boolean` | `false` | Não muda. |

**Tokens:** `color.primary`, `color.graffiti`, `radius.sm`, `motion.strike`, `motion.pupil`.

**Teclado**
- Tab: Foca.
- Espaço / Enter: Liga ou desliga.

**Leitor de tela**
- role=switch com aria-checked. label associado por id.

**Fazer**
- Rótulo descreve o estado ligado: "Receber aviso de liquidação".

**Evitar**
- Switch que pede confirmação depois. Se precisa de gate, é botão + ConfirmGate.

**Conteúdo**
- Afirmação, não pergunta. Sem "Ativar…" genérico.

### DropdownMenu · Diálogo · `beta`

Menu de vidro no desktop; folha de papel no celular. Item 34 (40 no toque), perigo em tinta rosa. Nunca é navegação e nunca é filtro.

`import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem } from "@hiboupay/react";`

**Quando usar**
- Ações secundárias de uma linha ou de um botão ícone.
- Exportar, atribuir, abrir detalhe, excluir (este último abre ConfirmGate).

**Quando não usar**
- Filtrar lista: FilterChip.
- Navegar entre páginas: links.
- Formulário: Select.

**Anatomia:** Trigger (quase sempre Button); Popover hb-glass + hb-lens a partir de md; Folha hb-sheet com scrim abaixo de md; Label mono; Item com ícone opcional e hint mono; Separador; Item com marca (check ou rádio).

**Variantes**
- `popover (md+)` — Vidro com pálpebra, alinhado ao gatilho.
- `folha (< md)` — Papel que sobe, a mesma gramática do Modal. Itens com altura de controle.

**Estados:** fechado (120ms), aberto (pálpebra, 280ms), folha aberta, highlighted (papel 80%), disabled, danger.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `DropdownMenu.open / onOpenChange` | `boolean / (open) => void` | — | Controlado. Base Radix. |
| `DropdownMenuContent.align` | `"start" \| "center" \| "end"` | `"end"` | Alinhamento do popover. A folha ignora e ocupa a largura. |
| `DropdownMenuItem.icon` | `ReactNode` | — | Ícone de 16 em muted, à esquerda. Não decore todo item. |
| `DropdownMenuItem.tone` | `"default" \| "danger"` | `"default"` | Perigo em danger-ink. Quase sempre abre ConfirmGate. |
| `DropdownMenuItem.hint` | `string` | — | Atalho ou metadado em mono à direita. |
| `DropdownMenuCheckboxItem.checked` | `boolean` | — | Marca da família ChoiceMark. |

**Tokens:** `radius.lg`, `radius.xl`, `glass.base.blur`, `color.dangerInk`, `control.md`, `motion.calm`.

**Teclado**
- Enter / Espaço: Abre.
- ↑ ↓: Percorre itens.
- Home / End: Primeiro e último item.
- Enter: Ativa o item.
- Esc: Fecha.

**Leitor de tela**
- menu / menuitem (Radix). Item perigoso precisa de rótulo que diga a ação.

**Fazer**
- Uma ação por item, verbo no infinitivo.
- Excluir no fim, separado, tone danger.

**Evitar**
- Menu com 15 itens. Quebre ou use Command no app.
- Ícones decorativos em todo item.

**Conteúdo**
- "Exportar CSV", "Atribuir responsável", "Excluir cobrança".

### Pagination · Dados · `beta`

Intervalo em Sora tabular e duas setas no rodapé da lista. No estreito o texto sai da vista e fica no leitor.

`import { Pagination } from "@hiboupay/react";`

**Quando usar**
- Rodapé de DataTable ou lista longa.
- Quando a pessoa precisa saber onde está (1–20 de 240).

**Quando não usar**
- Poucos itens: mostre tudo.
- Carregar mais no scroll (isso é do app, não do Core).

**Anatomia:** Texto hb-num: intervalo e total; Select de tamanho quando há opções; Campo mono para saltar de página; Botão ícone anterior; Botão ícone próximo.

**Variantes**
- `compacta` — Abaixo de md o intervalo fica só para o leitor. As setas continuam.
- `com salto` — Input mono "Ir para a página" quando a lista é longa.

**Estados:** meio, primeira (anterior disabled), última (próxima disabled), vazia ("Nenhum"), carregando (setas desabilitadas, aria-busy).

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `page` * | `number` | — | Página atual, 1-based. |
| `pageSize` * | `number` | — | Itens por página. |
| `total` * | `number` | — | Total de itens. |
| `onPageChange` * | `(page: number) => void` | — | Nova página. |
| `noun` * | `{ one: string; many: string }` | — | one: "pagamento". many: "pagamentos". |
| `pageSizeOptions` | `number[]` | — | Quando passa, um Select troca o tamanho. |
| `onPageSizeChange` | `(pageSize: number) => void` | — | Novo tamanho. Volte a página para 1 no app. |
| `jump` | `boolean` | `false` | Mostra o campo para ir a uma página. |
| `loading` | `boolean` | `false` | Desabilita as setas e marca aria-busy. |

**Tokens:** `control.sm`, `font.heading`, `font.mono`, `type.compact`.

**Teclado**
- Tab: Foca as setas.
- Enter / Espaço: Vai.

**Leitor de tela**
- nav aria-label Paginação. O intervalo tem aria-live, mesmo quando some no estreito.
- Setas com aria-label Página anterior / Próxima página.

**Fazer**
- pageSize estável (20 ou 50).
- Vazio é EmptyState na tabela; a paginação diz "Nenhum".

**Evitar**
- Pílulas 1 2 3 4 … 99.
- Escrever "N/A" quando total é 0.

**Conteúdo**
- one no singular e many no plural, minúsculos: "pagamento" / "pagamentos".

### Progress · Estado · `beta`

Trilha wash. Com número, o corpo é Graffiti e a ponta é Vivid Blue. Indeterminado: a coruja trabalha e a barra desliza.

`import { Progress } from "@hiboupay/react";`

**Quando usar**
- Upload, importação, job do agente com progresso conhecido.
- Espera longa sem número: indeterminate + label.

**Quando não usar**
- Ação curta: Button loading / Spinner.
- Proporção numa métrica: Bar do MetricWidget.
- Lista carregando: Skeleton.

**Anatomia:** Label + coruja (indeterminate); % em hb-num (determinado); Trilha 6px wash, ou n trilhos quando há etapas; Preenchimento em degradê da marca; Ponta Vivid Blue, apagada quando concluído.

**Variantes**
- `linear` — Uma trilha. O padrão.
- `com etapas` — N trilhos com gap 4, para importação em lotes. O valor 0–100 preenche em ordem.

**Estados:** 0%, parcial, 100%, concluído (barra em success-ink, ponta apagada), indeterminate (órbita + slide).

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `value` | `number` | `0` | 0–100. Ignorado se indeterminate. |
| `indeterminate` | `boolean` | `false` | Sem número. A coruja entra em working. |
| `label` | `ReactNode` | — | O que está acontecendo. |
| `segments` | `number` | — | Quantidade de trilhos. Ausente = um. |
| `tone` | `"primary" \| "success"` | `"primary"` | success só pinta a barra concluída (100%). Antes disso segue o degradê. |

**Tokens:** `color.primary`, `color.graffiti`, `color.successInk`, `radius.pill`, `motion.slow`.

**Teclado**
- —: Não é interativo.

**Leitor de tela**
- role=progressbar. indeterminate liga aria-busy e omite aria-valuenow.
- Coruja com title "Agente trabalhando".

**Fazer**
- Label concreto: "Lendo 1.204 linhas".

**Evitar**
- Progresso circular. Isso é Spinner.
- Animação se a pessoa pediu reduced-motion no app (respeite).

**Conteúdo**
- Verbo no gerúndio: "Lendo", "Enviando", "Aplicando".

### FileUpload · Entrada · `beta`

Zona rebaixada, anel tracejado, coruja vigiando. No arraste ela trabalha. Arquivo vira linha de papel com tamanho mono e X. Sem nuvem genérica.

`import { FileUpload } from "@hiboupay/react";`

**Quando usar**
- Importar planilha, comprovante, anexo único ou poucos arquivos.

**Quando não usar**
- Galeria de mídia. Campo de texto. Arrastar para mudar ordem (não é este componente).

**Anatomia:** Label; Zona sunken com coruja idle/working; Título Sora + dica; Lista de arquivos em papel; Linha de arquivo com Progress; Mensagem de erro ou hint.

**Estados:** vazia, arraste (anel azul, coruja working), com arquivos, enviando, erro (anel danger), disabled.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `files` * | `File[]` | — | Arquivos aceitos. |
| `onChange` * | `(files: File[]) => void` | — | Nova lista. |
| `accept` | `string` | — | accept nativo. Ex.: .csv,.xlsx. |
| `multiple` | `boolean` | `false` | Vários arquivos. |
| `maxSize` | `number` | — | Limite em bytes. O que passa não entra e vira erro. |
| `progress` | `Record<string, number>` | — | 0–100 por arquivo. A chave é nome-tamanho. |
| `renderFile` | `(file: File) => ReactNode` | — | Substitui o miolo da linha. O remover continua. |
| `emptyTitle` | `string` | `"Solte o arquivo aqui"` | Título da zona vazia. |
| `emptyDescription` | `string` | `"ou clique para escolher"` | Apoio. |
| `error` | `string` | — | Erro externo. Substitui o hint. |

**Tokens:** `radius.lg`, `color.primary`, `color.danger`, `elevation.0`.

**Teclado**
- Tab: Foca o botão da zona.
- Enter / Espaço: Abre o seletor.

**Leitor de tela**
- input file sr-only ligado ao label.
- Remover tem aria-label "Remover {nome}".

**Fazer**
- accept e maxSize explícitos no hint.
- Enquanto sobe, progress na linha do arquivo.
- disabled esmaece só a zona, não o texto da lista.

**Evitar**
- Ícone de nuvem.
- Aceitar qualquer tipo e recusar depois sem dizer o porquê.

**Conteúdo**
- hint: "CSV ou XLSX até 10 MB". emptyTitle fala o objeto: "Solte a planilha".

### Toast · Estado · `beta`

Aviso de vidro. Na mesa, canto inferior direito. No celular, topo, sob a barra. Tom é um ponto de luz, não uma faixa. Some sozinho. Não decide por ninguém.

`import { ToastProvider, useToast, ToastInput, ToastPosition } from "@hiboupay/react";`

**Quando usar**
- Confirmar que algo já aconteceu: "Planilha importada".
- Oferecer um desfazer leve (action).

**Quando não usar**
- Erro que bloqueia a tarefa: ErrorBanner no lugar.
- Ação irreversível: ConfirmGate antes, não toast depois como se fosse pergunta.
- Validação de campo: Input error.

**Anatomia:** Viewport no canto ou no topo; Cartão hb-glass + hb-lens; Ponto de tom; Título Sora; Descrição; Ação opcional; Fechar.

**Variantes**
- `mesa (canto inferior direito)` — Entra pela direita. Não cobre o dock porque o dock é do celular.
- `celular (topo, sob a barra)` — Entra de cima, com safe-area. O polegar continua livre embaixo.
- `default` — Ponto Vivid Blue. Aviso neutro.
- `success` — Ponto verde. Concluído.
- `warning` — Ponto âmbar. Delta ruim, a tela segue.
- `danger` — Ponto rosa. Falhou, mas a tela segue.

**Estados:** entrando (opacidade, depois o deslocamento), visível, pausado (hover ou foco), fechado.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `ToastProvider.position` | `"bottom-right" \| "top"` | — | Força o lugar. Sem isto, o celular usa o topo. |
| `useToast().push` | `(t: ToastInput) => id` | — | Empilha. Máximo 3. Some em 5s se duration omitido. |
| `ToastInput.title` * | `string` | — | O que aconteceu. |
| `ToastInput.description` | `string` | — | Apoio curto. |
| `ToastInput.tone` | `"default" \| "success" \| "warning" \| "danger"` | `"default"` | Cor do ponto. warning é aviso; danger é falha. |
| `ToastInput.action` | `{ label, onClick }` | — | Desfazer ou abrir. Pausa o timer enquanto o foco está no cartão. |
| `ToastInput.duration` | `number` | `5000` | ms. 0 = não some sozinho. Hover e foco pausam a contagem. |

**Tokens:** `radius.lg`, `glass.base.blur`, `color.success`, `color.warning`, `color.danger`, `motion.quick`.

**Teclado**
- Tab: Ação e fechar são focáveis.

**Leitor de tela**
- role=status. Não use alert — toast não interrompe.

**Fazer**
- Frase no passado: "Cobrança atribuída".
- Uma ação no máximo.

**Evitar**
- Toast de erro no lugar de ErrorBanner.
- Empilhar 8 toasts.
- Faixa colorida no topo do cartão.

**Conteúdo**
- Título sem ponto. Descrição só se o título não baste.

### Tooltip · Diálogo · `beta`

Chip Graffiti com seta, 12px, atraso de 400ms. Entra só por opacidade. Explica o que já está na tela. Nunca carrega a regra.

`import { Tooltip, TooltipProvider } from "@hiboupay/react";`

**Quando usar**
- Ícone sem texto (aria-label no gatilho + tooltip de apoio).
- Expandir um ID ou um atalho.

**Quando não usar**
- Informação essencial: coloque na tela.
- Erro: Input error ou ErrorBanner.
- Menu de ações: DropdownMenu.

**Anatomia:** Gatilho (asChild); Chip graffiti com seta, texto branco e atalho mono.

**Estados:** oculto, visível após 400ms, visível no foco, no toque: não aparece.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `content` * | `ReactNode` | — | Texto curto. |
| `children` * | `ReactNode` | — | Gatilho. Precisa aceitar ref (asChild). |
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"top"` | Lado preferido. |
| `shortcut` | `string` | — | Atalho em mono, à direita do texto. |
| `delay` | `number` | `400` | ms até abrir. O Provider também usa 400. |

**Tokens:** `color.graffiti`, `radius.sm`, `elevation.2`.

**Teclado**
- Tab / foco: Mostra.
- Esc: Esconde.

**Leitor de tela**
- O gatilho precisa de nome (aria-label ou texto). No toque o tooltip não abre: o nome do gatilho é o contrato.

**Fazer**
- Uma linha, até ~12 palavras.
- Botão ícone sempre com aria-label, tooltip é extra.

**Evitar**
- Tooltip em texto corrido.
- Tooltip com botão dentro.

**Conteúdo**
- Sem ponto final. "ID da cobrança", "Atalho ⌘K".

### Grid · Superfície · `beta`

1 a 6 colunas. Gap da escala (8 · 12 · 16 · 24 · 32) e os cortes da margem (md 768, xl 1280).

`import { Grid, GridItem, GridColumns, GridCols, GridGap } from "@hiboupay/react";`

**Quando usar**
- Fila de MetricWidget.
- Cartões lado a lado.
- Dois campos no mesmo andar de um formulário.

**Quando não usar**
- Tabela: DataTable.
- Empilhar Grid dentro de Grid mais de uma vez.
- Inventar gap fora da escala.

**Anatomia:** Contêiner display:grid; Colunas base / md / xl; Gap da escala; Alinhamento dos itens; GridItem com span, mdSpan e xlSpan.

**Variantes**
- `1` — Pilha. Celular, formulário estreito.
- `2 / 3 / 4` — Métricas e cartões.
- `6` — Só para peças miúdas (avatares, chips). Quase nunca.

**Estados:** estático.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `columns` | `1\|2\|3\|4\|6 \| { base, md?, xl? }` | `1` | md = 768, xl = 1280 — os mesmos da gutter. |
| `gap` | `2 \| 3 \| 4 \| 6 \| 8` | `4` | 8 · 12 · 16 · 24 · 32 px. |
| `align` | `"start" \| "center" \| "stretch"` | `"stretch"` | Alinhamento vertical dos itens. |
| `GridItem.span` | `1 \| 2 \| 3 \| 4 \| 6` | — | Largura em colunas na base. |
| `GridItem.mdSpan / xlSpan` | `1 \| 2 \| 3 \| 4 \| 6` | — | Largura nos cortes. |

**Tokens:** `space.2`, `space.3`, `space.4`, `space.6`, `space.8`, `gutter.md`, `gutter.xl`.

**Teclado**
- —: Layout. O foco mora nos filhos.

**Leitor de tela**
- É um div. Não anuncie a grade; anuncie o conteúdo.

**Fazer**
- No celular, base: 1. Métricas: { base: 1, md: 2, xl: 3 }.
- gap 4 (16) entre cartões, gap 6 (24) entre seções.

**Evitar**
- 12 colunas, offset, pull/push.
- gap-5 (20) ou gap arbitrário em px.

**Conteúdo**
- —

### ChoiceCard · Entrada · `beta`

Escolha que precisa mostrar o preço da decisão. É o Radio quando a opção não cabe numa linha: ícone, título, explicação e o total no pé. Marca no canto, luz azul quando ativo.

`import { ChoiceCardGroup, ChoiceCard } from "@hiboupay/react";`

**Quando usar**
- Forma de pagamento no checkout.
- Condição de parcelamento com valor e total.
- Adicional opcional com preço (seguro, garantia).

**Quando não usar**
- Opção que cabe numa linha: Radio.
- Aceitar um termo: Checkbox.
- Lista longa: Select.
- Filtrar uma tabela: FilterChip.

**Anatomia:** fieldset com legend (hb-label); Cartão de papel, raio 16; Ícone opcional em tile; Título type.lead; Etiqueta Badge opcional; Descrição; Rodapé separado por linha fina; ChoiceMark no canto superior direito.

**Variantes**
- `radio` — Uma entre poucas. Padrão.
- `multiple` — Vários adicionais ao mesmo tempo. A marca vira check.

**Estados:** off, on (papel azul + anel), hover, focus-visible, disabled, invalid.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `ChoiceCardGroup.name` * | `string` | — | name nativo compartilhado. |
| `ChoiceCardGroup.multiple` | `boolean` | `false` | Troca radio por checkbox; value vira string[]. |
| `ChoiceCardGroup.value` | `string \| string[]` | — | Controlado. Array quando multiple. |
| `ChoiceCardGroup.onValueChange` | `(value: string \| string[]) => void` | — | Nova escolha. |
| `ChoiceCardGroup.columns` | `1 \| 2 \| { base, md }` | `1` | Uma ou duas colunas. Objeto troca no corte md do contêiner. |
| `ChoiceCardGroup.invalid` | `boolean` | `false` | Grupo obrigatório sem escolha. aria-invalid no fieldset. |
| `ChoiceCardGroup.message` | `string` | — | Mensagem em danger-ink, ligada por aria-describedby. |
| `ChoiceCard.value` * | `string` | — | Valor desta opção. |
| `ChoiceCard.title` * | `ReactNode` | — | A decisão, em uma linha. |
| `ChoiceCard.badge` | `ReactNode` | — | Etiqueta "Mais barata" ou "Recomendada" com Badge. Nunca pílula. |
| `ChoiceCard.description` | `ReactNode` | — | O que muda se escolher. |
| `ChoiceCard.footer` | `ReactNode` | — | Total, prazo ou condição, separado por linha fina. |
| `ChoiceCard.icon` | `ReactNode` | — | Marca do meio de pagamento ou do adicional. |

**Tokens:** `color.primary`, `color.dangerInk`, `radius.lg`, `elevation.1`, `motion.quick`, `type.lead`.

**Teclado**
- Tab: Entra no grupo.
- ↑ ↓: Troca a opção (radio).
- Espaço: Marca (radio e multiple) ou desmarca (multiple).

**Leitor de tela**
- input radio ou checkbox nativo dentro de label. O cartão inteiro é o alvo.
- Grupo inválido: aria-invalid e aria-describedby na mensagem.

**Fazer**
- Mostre o total junto da parcela — a pessoa decide com os dois números.
- Ordene da condição mais barata para a mais longa.

**Evitar**
- Pré-selecionar a opção mais cara.
- Esconder taxa no rodapé em corpo menor que 12.

**Conteúdo**
- Título com o número que decide: "12x R$ 223,04". Rodapé com o total: "Total R$ 2.676,48".

### Stepper · Estado · `beta`

Onde a pessoa está numa jornada de poucos passos. Concluído vira check e nunca volta a ser número. No horizontal estreito cabe só o número; no vertical a descrição fica.

`import { Stepper, Step, StepStatus, StepperOrientation } from "@hiboupay/react";`

**Quando usar**
- Checkout em etapas.
- Onboarding, cadastro ou KYC em mais de duas telas.
- Qualquer fluxo em que voltar é possível e o fim é conhecido.

**Quando não usar**
- Processo de duração desconhecida: Progress.
- Duas vistas do mesmo dado: Tabs.
- Mais de seis passos — quebre o fluxo.

**Anatomia:** nav com ol; Disco 32px por passo; Conector que acende até o passo atual; Rótulo opcional; Descrição no vertical; Resumo "N de M" só no horizontal estreito.

**Variantes**
- `horizontal` — Discos em linha. Abaixo de md vira "N de M · rótulo".
- `vertical` — Lista com descrição por passo. Serve onboarding e KYC.

**Estados:** concluído (check azul), atual (azul com halo), pendente (wash), erro no passo (disco em danger).

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `steps` * | `Step[]` | — | { id, label?, description?, status? }. Sem label vira só o número. |
| `current` * | `number` | — | Índice base 0. Tudo antes está concluído, salvo status error. |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Vertical mostra a descrição e não some no celular. |
| `onStepClick` | `(index: number) => void` | — | Só o passo concluído vira botão. Pendente nunca é botão. |
| `label` | `string` | `"Progresso"` | Nome da jornada para o leitor de tela. |
| `Step.status` | `"complete" \| "current" \| "upcoming" \| "error"` | — | error pinta o disco em danger-ink. KYC recusado. |
| `Step.description` | `string` | — | Uma linha sob o rótulo, no vertical. |

**Tokens:** `color.primary`, `color.dangerInk`, `radius.pill`, `control.md`, `motion.calm`.

**Teclado**
- Tab: Só chega no passo concluído, quando há onStepClick.
- Enter / Espaço: Volta ao passo concluído.

**Leitor de tela**
- nav nomeada, ol de passos. O passo atual tem aria-current="step".
- Cada passo declara concluído, atual, pendente ou com erro em texto oculto.

**Fazer**
- Rótulo com substantivo: "Endereço", "Pagamento".
- No celular horizontal, só o resumo. Vertical quando cada passo precisa de descrição.

**Evitar**
- Deixar o passo pendente clicável.
- Contar passos que a pessoa não controla (processamento).

**Conteúdo**
- "Etapa 2 de 4" no título da página; o Stepper mostra, não repete.

### Notice · Estado · `stable`

Aviso preso ao conteúdo, nos quatro tons. Tom aparece como preenchimento e tinta — nunca como faixa colorida no topo do cartão.

`import { Notice } from "@hiboupay/react";`

**Quando usar**
- Consentimento e termo legal antes de uma ação.
- Garantia ao lado de dado sensível: "seus dados estão protegidos".
- Alerta que precisa ficar na tela enquanto durar a condição.

**Quando não usar**
- Confirmação efêmera de algo que deu certo: Toast.
- Falha com "tentar de novo": ErrorBanner.
- Bloquear uma ação com dinheiro: ConfirmGate.

**Anatomia:** Superfície tingida com anel fino; Ícone do tom em tile; Título opcional; Corpo; Ação opcional à direita.

**Variantes**
- `info` — Contexto, consentimento, regra. Azul.
- `success` — Garantia e proteção. Verde.
- `warning` — Condição que exige atenção agora. Âmbar.
- `danger` — Risco ou perda. Rosa. Vira role=alert.

**Estados:** estático.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `tone` | `"info" \| "success" \| "warning" \| "danger"` | `"info"` | danger é anunciado como alert. |
| `title` | `ReactNode` | — | Uma linha em Sora. |
| `icon` | `ReactNode` | — | Troca o ícone do tom. null remove. |
| `action` | `ReactNode` | — | Um botão ou link à direita. |

**Tokens:** `color.primary`, `color.successInk`, `color.warningInk`, `color.dangerInk`, `radius.lg`.

**Teclado**
- Tab: Foca a ação, se houver.

**Leitor de tela**
- danger é role=alert. Os outros tons não interrompem a leitura.

**Fazer**
- Diga a condição e o que fazer com ela.
- Consentimento com Checkbox dentro do Notice info.

**Evitar**
- Três Notice empilhados na mesma tela.
- Notice para elogiar o produto.

**Conteúdo**
- "Autorizo a consulta da minha margem consignável no eSocial." Cite a norma, não o processo interno.

### SummaryList · Dados · `beta`

Resumo de uma decisão: rótulo à esquerda, valor tabular à direita, linha fina entre as duas. O total é a única linha que grita. Papel sozinho; flush dentro de outro cartão.

`import { SummaryList, SummaryRow } from "@hiboupay/react";`

**Quando usar**
- Resumo do pedido antes de pagar.
- Condições da proposta: valor, parcela, primeiro desconto.
- Painel de detalhe de um registro.

**Quando não usar**
- Muitas linhas comparáveis com ordenação: DataTable.
- Um número em destaque: MetricWidget.
- Formulário editável: Field.

**Anatomia:** Papel com raio 16, ou flush sem cartão; Rótulo mono opcional no topo; dl com linhas divididas; Valor em hb-num alinhado à direita; Tom success ou warning no valor; hintValue na segunda linha à direita; Linha de ênfase para o total.

**Variantes**
- `papel` — Cartão próprio. O padrão.
- `flush` — Sem raio, sombra nem padding. Para dentro de Card ou Modal.

**Estados:** estático, carregando (Skeleton na coluna do valor).

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `rows` * | `SummaryRow[]` | — | { label, value?, hint?, hintValue?, emphasis?, tone? }. |
| `title` | `ReactNode` | — | Rótulo de máquina acima da lista. |
| `material` | `"paper" \| "flush"` | `"paper"` | flush tira o cartão. |
| `loading` | `boolean` | `false` | Skeleton no lugar de cada valor. |
| `SummaryRow.hint` | `ReactNode` | — | Segunda linha à esquerda: quantidade, prazo, identificador. |
| `SummaryRow.hintValue` | `ReactNode` | — | Segunda linha à direita: "12x". |
| `SummaryRow.tone` | `"success" \| "warning"` | — | Desconto é success. Taxa é warning. Sem danger. |
| `SummaryRow.emphasis` | `boolean` | `false` | Fecha a conta. Use em uma linha só. |

**Tokens:** `elevation.1`, `radius.lg`, `font.heading`, `color.muted`, `color.successInk`, `color.warningInk`.

**Teclado**
- —: Texto. O foco mora nas ações em volta.

**Leitor de tela**
- dl com dt e dd: o leitor lê rótulo e valor em par.
- Carregando: aria-busy na lista.

**Fazer**
- Valor sempre com moeda e em pt-BR — use formatBRL.
- Uma linha de ênfase por lista.

**Evitar**
- Célula vazia com "—" ou "N/A": deixe vazia.
- Três totais competindo pela mesma atenção.

**Conteúdo**
- Rótulo curto e substantivo: "Valor solicitado", "Total a pagar".

### Slider · Entrada · `beta`

Faixa contínua de valor. Trilho wash preenchido pelo degradê Graffiti → Vivid Blue, polegar de papel. O número atual fica visível o tempo todo.

`import { Slider } from "@hiboupay/react";`

**Quando usar**
- Quanto usar de um saldo: pontos, crédito, limite.
- Ajuste aproximado em que a pessoa reage ao resultado.

**Quando não usar**
- Valor exato que a pessoa já sabe: Input.
- Poucas opções discretas: Radio ou ChoiceCard.
- Ligar e desligar: Switch.

**Anatomia:** Rótulo à esquerda e valor em azul à direita; input range nativo com alvo de 40; Trilho 6px com preenchimento até o valor; Marcas de referência; Polegar 16px de papel; Input numérico opcional; Legendas das pontas.

**Estados:** padrão, arrastando, focus-visible (anel azul no polegar), no limite (polegar com anel graffiti), disabled.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `value` * | `number` | — | Controlado. |
| `onValueChange` | `(value: number) => void` | — | Novo valor a cada passo. |
| `min / max / step` | `number` | `0 / 100 / 1` | Faixa e granularidade. |
| `format` | `(value: number) => string` | — | Como o número aparece. Padrão: pt-BR sem casas. |
| `minLabel / maxLabel` | `ReactNode` | — | Legendas das pontas. |
| `marks` | `number[]` | — | Traços de referência no trilho. |
| `input` | `boolean` | `false` | Input numérico ao lado, mesma faixa e o mesmo valor. |

**Tokens:** `color.primary`, `color.graffiti`, `color.wash`, `radius.pill`, `control.md`.

**Teclado**
- ← →: Um passo.
- Home / End: Mínimo e máximo.
- PageUp / PageDown: Salto maior.

**Leitor de tela**
- input range nativo: valor, mínimo e máximo anunciados. Rótulo associado por id.

**Fazer**
- Mostre o valor formatado ao lado do rótulo.
- Deixe uma entrada exata por perto quando o valor importa no contrato.

**Evitar**
- Slider como única forma de escolher valor de dinheiro.
- Faixa sem legenda de mínimo e máximo.

**Conteúdo**
- Rótulo diz o que o valor faz: "Usar pontos", não "Selecione".

### StatusView · Estado · `stable`

Tela inteira que só tem um estado para contar: vazio, esperando, aprovado ou falhou. Um desenho para os quatro; o tom troca o ícone no tile branco. EmptyState é o preset neutro.

`import { StatusView } from "@hiboupay/react";`

**Quando usar**
- Espera de resultado que a pessoa não controla: análise, assinatura, liquidação.
- Fim de fluxo: aprovado ou recusado.
- Área sem dado (via EmptyState).

**Quando não usar**
- Aviso dentro de uma tela cheia: Notice.
- Confirmação efêmera: Toast.
- Carregar um pedaço da tela: Skeleton.

**Anatomia:** Tile de papel com a coruja ou ícone de tom; Título Sora 15; Descrição; Ação; Rodapé: barra de progresso, aviso, link de contrato.

**Variantes**
- `neutral` — Vazio. A coruja vigia. É o EmptyState.
- `working` — Esperando. A coruja trabalha e a tela vira aria-busy.
- `success` — Aprovado.
- `danger` — Recusado ou falhou.

**Estados:** neutral, working, success, danger.

| Prop | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `tone` | `"neutral" \| "working" \| "success" \| "danger"` | `"neutral"` | Troca halo e ícone; working marca aria-busy. |
| `title` * | `string` | — | O que está acontecendo, em uma frase. |
| `description` | `string` | — | Por quê, ou o que fazer agora. |
| `action` | `ReactNode` | — | Próximo passo. Tela sem saída é bug. |
| `footer` | `ReactNode` | — | Progress, aviso de não fechar a janela, link do contrato. |
| `compact` | `boolean` | `false` | Menos respiro, para dentro de cartão ou tabela. |

**Tokens:** `elevation.1`, `font.heading`, `color.muted`, `color.success`, `color.dangerInk`.

**Teclado**
- Tab: Foca a ação e o que estiver no rodapé.

**Leitor de tela**
- role=status. working adiciona aria-busy para a espera ser anunciada.

**Fazer**
- Na espera, diga quanto tempo leva e o que não fazer.
- No fim, ofereça o documento e o caminho de volta.

**Evitar**
- Espera sem saída nenhuma.
- "Erro inesperado" sem causa nem próximo passo.

**Conteúdo**
- "Confirmando sua autorização. Isso leva só um instante." Aviso de não fechar a janela vai no rodapé.

## Padrões

### Gate de decisão

Toda ação irreversível passa por um gate legível: o que acontece, o que não volta e por quê.

- Dinheiro, permissão, exclusão e execução de agente sempre abrem ConfirmGate.
- Se a ação entra no histórico, peça motivo (requireReason). Mínimo de 3 caracteres.
- Para exclusão em massa ou irreversível, peça a palavra exata (requireTypedConfirm).
- O botão de confirmação repete o verbo da ação: "Estornar R$ 1.200", não "OK".
- Cancelar é sempre ghost e fica à esquerda; no celular fica por baixo.

Evitar:
- Confirmar com "Tem certeza?" sem dizer a consequência.
- Gate que libera o botão sem motivo quando o motivo vai para auditoria.
- Toast de desfazer no lugar de gate para dinheiro.

### Agente propõe, humano decide

A coruja mostra o que encontrou, com fonte. A pessoa decide. O agente não inventa campo, estado nem componente.

- Enquanto trabalha, a coruja ganha órbita (HibouMark state="working"). Espera de tela é StatusView working, não um spinner de página.
- A proposta é papel: o que muda, de onde veio, e a confiança em faixa — não um número falso-preciso.
- Faltou componente, token ou campo no contrato: diga o que falta. Não invente.
- Decisão ainda aberta não vira código. A pessoa registra a escolha antes.
- Consultar margem, enviar ou cobrar passa também pelo Gate de decisão. Consentimento de dado pessoal nunca vem pré-marcado.
- A decisão fica registrada com autor humano.

Evitar:
- Ícone de brilho ou robô no lugar da coruja.
- Aceitar a proposta sozinho porque a confiança é alta.
- Esconder a fonte da sugestão.
- Inventar estado, campo ou componente que não está no contrato.

### Vazio, erro e carregando

Nenhuma tela termina num beco. Vazio diz o próximo passo, erro diz o que fazer, carregando mostra a forma do que vem.

- Vazio usa EmptyState com a coruja vigiando e uma ação quando houver.
- Erro recuperável usa ErrorBanner com "Tentar de novo". A mensagem diz a causa em linguagem da pessoa.
- Carregando lista ou cartão usa Skeleton com a forma final; ação curta usa Button loading.
- Espera que a pessoa não controla ocupa a tela com StatusView working: diga quanto demora e o que não fazer.
- Aviso que fica enquanto durar a condição é Notice, preso ao conteúdo. Toast é só para o que passa.
- Célula sem dado fica vazia. Nunca invente valor, nunca escreva "N/A" em dado financeiro.

Evitar:
- Ilustração genérica de caixa vazia.
- Mensagem técnica ("Error 500") sem próxima ação.
- Spinner de página inteira para uma lista.
- Tela de espera sem saída nenhuma.

### Dinheiro e números

Número é Sora tabular, alinhado à direita, com unidade. Variação ruim é aviso, não erro.

- Use hb-num (Sora, algarismos tabulares) para valor, contagem e percentual.
- Em tabela, coluna numérica usa align="right".
- Sempre com unidade: R$, %, pp, tokens. Valor em real usa formatBRL / formatCents do Core — o formato não é escolha de cada app.
- Guarde dinheiro em centavos e formate só na borda, com formatCents. Float em valor de contrato é bug.
- Delta bom usa success; delta ruim usa warning. Danger é só para falha.
- Célula vazia segue vazia.

Evitar:
- Valor monetário em Inter proporcional.
- Número centralizado em coluna.
- Arredondar sem indicar ("1,2k" sem contexto de milhar).

### Filtros

Filtros são chips no canvas. Ativo vira papel com anel azul; as opções abrem em popover de vidro.

- Um FilterChip por dimensão. O rótulo diz a dimensão; o valor aparece ao lado quando ativo.
- Mais de 8 opções: searchable.
- Dimensão exclusiva (período, ordenação): single.
- Chip ativo tem X para limpar sem abrir o popover.
- Resultado vazio por filtro mostra EmptyState com "Limpar filtros".

Evitar:
- Select nativo numa barra de filtros.
- Filtro que some quando não há resultado.
- Cor de estado no chip (o chip não é status).

### Navegação em vidro

Sidebar de vidro noturno que recolhe para trilho, espia no hover e marca a página com o tile de orelhas.

- Desktop: painel 256 aberto, trilho 84 recolhido. "[" alterna; preferência em cookie.
- No trilho, hover por 380 ms espia o painel sem mudar o layout.
- Item ativo: tile branco com orelhas (hb-ears) que desliza até o item.
- Celular: barra de vidro no topo e dock noturno flutuante com até 4 destinos + Mais.
- Busca global em ⌘K / Ctrl K, sempre.

Evitar:
- Barra lateral branca opaca.
- Mais de 4 itens no dock.
- Destacar ativo com faixa colorida na borda.

### Formulários

Rótulo de máquina em cima, controle com a mesma altura do botão, dica embaixo. Erro diz o que fazer.

- Rótulo sempre visível (hb-label). Placeholder é exemplo, não rótulo.
- Tamanho do campo acompanha o botão ao lado: sm 32, md 40, lg 48.
- Erro liga aria-invalid e aria-describedby, com ícone e instrução: "Falta o domínio. Ex.: nome@empresa.com".
- Campos relacionados em grade de 2 colunas a partir de md; texto longo ocupa a linha.
- Ação principal à direita no rodapé; no celular, empilhada com a principal por baixo.

Evitar:
- Erro só em cor.
- Validar a cada tecla antes de a pessoa terminar.
- Rótulo flutuante dentro do campo.

### Checkout com crédito

Contratar crédito dentro de uma compra. A pessoa precisa saber, em toda etapa, quanto vai pagar, o que autorizou e o que ainda falta.

- Stepper no topo com o fim conhecido. O título repete a etapa em texto: "Etapa 2 de 4".
- Consentimento é Notice info com Checkbox dentro, citando a norma. Autorizar consulta de dado pessoal nunca vem pré-marcado.
- Condição de pagamento é ChoiceCard: parcela e total no mesmo cartão. A opção padrão é a mais barata, não a mais lucrativa.
- Adicional com preço (seguro, garantia) é ChoiceCard multiple, desmarcado, com o custo mensal explícito.
- Antes de assinar, SummaryList repete valor solicitado, parcela, primeiro desconto e total. O total é a única linha em ênfase.
- Assinatura e aprovação são gate: o botão repete o verbo e o valor ("Confirmar e assinar"), e só libera com o aceite marcado.
- Espera de análise ou de assinatura usa StatusView working com o aviso de não fechar a janela no rodapé.
- O fim tem documento e caminho de volta: contrato (CCB) e retorno à loja, aprovado ou recusado.
- Sair da compra é sempre possível: Button link "Cancelar e voltar à loja", em todas as etapas.

Evitar:
- Pré-marcar consentimento de consulta de margem ou adicional pago.
- Mostrar parcela sem o total, ou total sem o custo efetivo.
- Barra de progresso falsa para uma análise de duração desconhecida.
- Tela de espera sem cancelar e sem suporte.
- Recusa sem motivo nem próximo passo.

## Acessibilidade

### Contraste

- Texto AA (4.5:1). Texto de estado usa a variante -ink (success-ink, warning-ink, danger-ink).
- Muted #686D75 sobre papel e canvas passa AA para texto de apoio.
- Vidro revalida contraste nos três níveis; texto sobre vidro é ink, nunca muted claro.

### Foco

- Todo interativo tem o mesmo foco: anel de 2px Vivid Blue com offset de 2px, imediato; halo de 4px entra 80ms depois (pupil). Utilitária hb-focus; campos usam hb-pupil com a mesma geometria.
- Foco nunca é removido sem substituto.
- Diálogo prende o foco e devolve ao gatilho ao fechar.

### Teclado

- Tab / Shift+Tab percorrem; Enter e Espaço ativam.
- Esc fecha diálogo, popover e busca.
- Setas navegam dentro de Tabs e listas.
- Enter no cabeçalho da tabela ordena; Tab foca o link da linha.

### Leitor de tela

- Botão só com ícone exige aria-label.
- Spinner sozinho exige label (vira role=status).
- Erro de campo é anunciado via aria-describedby.
- A coruja é decorativa sem title; com title vira img nomeada.

### Movimento

- prefers-reduced-motion zera animação e transição (órbita, piscar, shimmer, deslizes).
- Nenhuma informação depende só de movimento.

### Alvo

- Alvo mínimo de 28 px (control xs); 40 px no celular para ação principal.
- Espaço entre alvos de no mínimo 4 px.

## Voz e conteúdo

Direto, humano, sem jargão. Diga o que aconteceu e o que fazer agora.

- Verbo no botão. O rótulo diz o que acontece ao clicar.
- Sem ponto de exclamação e sem "por favor" em erro.
- Número sempre com unidade.
- IDs e códigos em mono, nunca traduzidos.
- Português do Brasil; termos técnicos consagrados ficam em inglês (token, deploy).

| Faça | Evite |
| --- | --- |
| Tentar de novo | Ocorreu um erro inesperado. Por favor, tente novamente mais tarde! |
| Falta o domínio. Ex.: nome@empresa.com | E-mail inválido |
| Estornar R$ 1.200 | Confirmar |
| Nada por aqui ainda. A primeira cobrança aparece aqui. | Nenhum registro encontrado. |
| A chave foi recusada pelo provedor. | Erro 401: Unauthorized |

## Governança

Ciclo:
1. Proposta: o problema real e onde aparece
2. Contrato no registry ou no manifesto, revisado por uma pessoa
3. Implementação no Core, sem funil nem copy de um app
4. Documentação e story no mesmo verbo
5. Release SemVer com changelog
6. Sensor: pnpm ds:check e pnpm verify

Maturidade:
- **experimental** — Pode mudar sem aviso. Não use em fluxo crítico.
- **beta** — API estável na intenção; pode ganhar props. Breaking só em minor com nota.
- **stable** — Breaking só em major, com guia de migração.
- **deprecated** — Tem substituto documentado. Sai na próxima major.

Regras:
- Tokens são a fonte da verdade. Derivados (CSS, JS, JSON, MD) são gerados; editar derivado é bug e o check de drift bloqueia.
- O Core não conhece funil, estágio nem copy de um app. Consentimento, margem e sessão ficam no produto.
- Componente, token ou padrão que não está no registry não se inventa. Diga o que falta.
- Componente novo entra como experimental e só sobe com documentação completa e uso real.
- Mudança de contrato começa no manifesto ou no registry, com revisão humana, antes do código. pnpm verify é o sensor: typecheck, drift, lint de valor solto, axe por story e uma story por slug.
- Tamanho de texto só com a escala text-hb-*. Foco só com a utilitária hb-focus; campo usa hb-pupil.
- Overlay no celular é folha (hb-sheet); toast nasce no topo.

---

Fonte: `HibouPay Design Tokens` + domain/manifest.json + domain/registry.json. Gerado — não editar.
