@aurea-uds/core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/aurea.css CHANGED
@@ -56,6 +56,7 @@
56
56
  --z-modal:80;
57
57
  --z-toast:100;
58
58
  --sidebar-width:16.5rem;
59
+ --sidebar-rail:5.5rem;
59
60
  --topbar-height:4rem;
60
61
  --content-max:102.5rem;
61
62
  --breakpoint-sm:640px;
@@ -272,681 +273,1141 @@
272
273
  --card-pad:1.5rem;
273
274
  --section-gap:2rem;
274
275
  }
275
- /* Aurea core — component styles. Built with @aurea-uds/tokens into dist/aurea.css. Load @aurea-uds/fonts/css for IBM Plex. */
276
- /* ========================================================================== */
277
- /* 02. RESET, ACESSIBILIDADE E BASE */
278
- /* ========================================================================== */
279
- * { box-sizing:border-box; }
280
- html { scroll-behavior:smooth; }
281
- body { margin:0; background:var(--background); color:var(--foreground); font-family:var(--font-ui); font-size:var(--text-md); line-height:var(--leading-normal); }
282
- ::selection { background:var(--selection); }
283
- button,input,textarea,select { font:inherit; }
284
- button,a,[role="button"],input,textarea,select { outline:none; }
285
- :focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; box-shadow:none; border-color:var(--focus-strong)!important; }
286
- button { color:inherit; }
287
- a { color:var(--primary); text-underline-offset:3px; }
288
- img,svg { display:block; }
289
- /* O `display:none` que o browser dá pro atributo `hidden` vem da folha do UA e PERDE de
290
- qualquer `display:` de classe nossa — o painel some do fluxo de acessibilidade mas
291
- continua na tela. Já mordeu o popover do MediaPlayer e as abas do catálogo. Aqui a
292
- regra fecha a classe inteira do bug, em vez de um remendo por componente.
293
- `until-found` fica de fora: ali o `hidden` é revelável e o browser cuida. */
294
- [hidden]:not([hidden="until-found"]) { display:none!important; }
295
- .sr-only { position:absolute!important; width:1px!important; height:1px!important; padding:0!important; margin:-1px!important; overflow:hidden!important; clip:rect(0,0,0,0)!important; white-space:nowrap!important; border:0!important; }
296
- .icon { width:var(--icon-md); height:var(--icon-md); fill:currentColor; flex:none; }
297
- .icon-sm { width:var(--icon-sm); height:var(--icon-sm); } .icon-lg { width:var(--icon-lg); height:var(--icon-lg); } .icon-xl { width:var(--icon-xl); height:var(--icon-xl); }
298
- .muted { color:var(--muted-foreground); }
299
- .stack { display:flex; flex-direction:column; gap:var(--space-4); }
300
- .cluster { display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); }
301
- /* Grid: o terceiro primitivo de layout, com o mesmo gap do .stack. A pele nasceu na Fase 11
302
- (achado A13): o componente existia desde sempre e saía `display:block` — empilhava. Ninguém
303
- via porque o chrome do catálogo REDEFINIA .grid, então ele parecia pronto na nossa
304
- documentação e chegava sem nada no consumidor. `min(...,100%)` porque `minmax(15rem,1fr)`
305
- estoura o contêiner quando o texto vai a 200% e a coluna fica maior que a caixa (lição do
306
- M12); --grid-min é o botão de ajuste, no idioma de --qr-module e --demo-height. */
307
- .grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(min(var(--grid-min,15rem),100%),1fr)); gap:var(--space-4); }
308
-
309
- /* ========================================================================== */
310
- /* 03. APP SHELL E DOCUMENTAÇÃO */
311
- /* ========================================================================== */
312
- /* Regiões do AppShell — SUPERFÍCIES FLUTUANTES (decisão do Victor, 23/07/2026): topo,
313
- lateral e conteúdo são caixas soltas de raio --radius-card, separadas por --space-4 e
314
- descoladas da borda da janela pelo padding do shell. NADA de barra colada com linha
315
- divisória: caixa retangular genérica é proibida (CLAUDE.md).
316
- Arranjo escolhido: o TOPO atravessa toda a largura (grid-column 1/-1) e leva a marca;
317
- a lateral começa abaixo dele. Ambos grudam na rolagem por `sticky` — o conteúdo passa
318
- por baixo e as duas caixas seguem visíveis.
319
- ATENÇÃO: `.app-shell` NÃO se toca. O catálogo escrito à mão (apps/docs) usa essa MESMA
320
- classe como raiz, com filhos próprios (.doc-sidebar/.main-wrap) — mexer nela desloca as
321
- 26 telas de referência inteiras (aconteceu; o portão visual pegou). Por isso a folga
322
- entre as caixas sai da MARGEM de cada filho, não de gap/padding no shell. As linhas
323
- extras do grid nascem sozinhas por auto-placement. */
324
- .app-shell { min-height:100vh; display:grid; grid-template-columns:var(--sidebar-width) minmax(0,1fr); background:var(--background); }
325
- /* A base do topo carrega só o ARRANJO; cada variante traz a própria superfície.
326
- floating = caixa solta (a que já estava) · flush = faixa encostada nas bordas com fio
327
- embaixo, para o topo virar moldura da janela · pill = cápsula centrada, de cabeçalho
328
- de site. Só floating e flush entram no grid do AppShell; a pill é peça avulsa (numa
329
- pílula não cabem busca + densidade + tema + comando). */
330
- .topbar { grid-column:1/-1; position:sticky; z-index:var(--z-sticky); display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); min-height:var(--topbar-height); }
331
- .topbar-floating { top:var(--space-4); margin:var(--space-4) var(--space-4) 0; padding:var(--space-2) var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); }
332
- /* flush: encostada nas bordas. SEM fio embaixo (pedido do Victor — a referência não tem)
333
- e sem superfície própria: ela se funde ao fundo e cola no conteúdo, que começa logo
334
- abaixo. É o topo do nosso catálogo. */
335
- .topbar-flush { top:0; padding:var(--space-2) var(--space-5); background:var(--background); }
336
- .topbar-pill { top:var(--space-4); width:max-content; max-width:calc(100% - var(--space-8)); margin:var(--space-4) auto 0; justify-content:flex-start; gap:var(--space-2); padding:var(--space-2); border:1px solid var(--border); border-radius:var(--radius-control); background:var(--card); color:var(--card-foreground); }
337
- /* .brand nasceu como cabeçalho VERTICAL da lateral (borda embaixo + respiro); no topo
338
- ele deita, então some com a borda e a margem — o lockup em si continua o mesmo. */
339
- .topbar .brand { min-height:auto; margin:0; padding:0; border:0; }
340
- /* altura fixa = janela menos topo e as 3 folgas; assim a caixa parece cheia mesmo com
341
- poucos itens (é o que as referências mostram) e sobra o que rolar por dentro. */
342
- .sidebar { --shell-top:calc(var(--topbar-height) + var(--space-4)*2); position:sticky; top:var(--shell-top); align-self:start; height:calc(100vh - var(--shell-top) - var(--space-4)); margin:var(--space-4); display:flex; flex-direction:column; gap:var(--space-1); padding:var(--space-4); overflow:auto; scrollbar-width:none; border:1px solid var(--sidebar-border); border-radius:var(--radius-card); background:var(--sidebar); color:var(--sidebar-foreground); }
343
- /* a lateral ROLA mas nunca mostra a barra (pedido do Victor): a barra nativa risca a
344
- superfície flutuante e não segue o raio de 22px. `scrollbar-width` cobre Firefox e
345
- Chrome 121+; o pseudo cobre Safari e Chrome antigo. */
346
- .sidebar::-webkit-scrollbar { width:0; height:0; }
347
- /* com o topo `flush` a barra COLA no conteúdo (pedido do Victor): a folga acima da lateral
348
- e do conteúdo é --space-1 (4px), e o --shell-top acompanha. */
349
- /* flush cola no conteúdo com --space-05 (2px) — o degrau mais apertado da escala, criado
350
- pra este caso (o Victor aprovou os 2px; tokenizado, não cru). */
351
- .app-shell-flush .sidebar { --shell-top:calc(var(--topbar-height) + var(--space-05)); margin-block-start:var(--space-05); }
352
- .app-shell-flush .content { margin-block-start:var(--space-05); }
353
- .content { min-width:0; margin:var(--space-4) var(--space-4) var(--space-4) 0; padding:var(--space-6); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); }
354
- /* O topo é presentational — não conhece o conteúdo — então garante o mínimo universal:
355
- os filhos encolhem em vez de forçar rolagem lateral. O COLAPSO de conteúdo (esconder a
356
- busca, trocar a nav por menu) é do consumidor, com os utilitários responsivos abaixo. */
357
- .topbar > * { min-width:0; }
358
- /* QUEBRA POR CONTEÚDO. O `flex-wrap` vivia só dentro do @media abaixo, ou seja, a barra só
359
- quebrava quando a JANELA era estreita. Texto a 200% (WCAG 1.4.4) aumenta o conteúdo sem
360
- mexer no viewport: o @media não disparava e a nav passava 27px da borda direita, em todas
361
- as páginas. Quebrar por conteúdo cobre os dois casos e não muda nada quando cabe. */
362
- .topbar,.topbar > nav { flex-wrap:wrap; }
363
- /* Colapso do shell abaixo de md (767 = md-1, escala): a lateral deixa de flutuar e de ter
364
- altura de janela — empilha acima do conteúdo, que passa a ocupar a largura toda. Sem
365
- estado (o off-canvas com botão é refino da Fase 3); o essencial é nunca rolar de lado. */
366
- /* Abaixo de md o topo QUEBRA em duas linhas em vez de esconder a navegação: perder o acesso
367
- a uma área inteira não é colapso responsivo, é bug. A nav vai pra segunda linha, centrada
368
- e na largura toda; badge e selo (secundários) seguem escondidos pelos utilitários. */
369
- @media (max-width:767px) { .app-shell { grid-template-columns:minmax(0,1fr); } .sidebar { position:static; height:auto; } .content { margin-inline-start:var(--space-4); }
370
- .topbar { flex-wrap:wrap; row-gap:var(--space-2); }
371
- .topbar > nav { order:3; flex-basis:100%; justify-content:center; }
372
- /* e a nav quebra entre os PRÓPRIOS itens: mandar a barra pra segunda linha não bastava,
373
- as cinco áreas somam 371px e estouravam a 320px (auditoria 26/07, A1). */
374
- .topbar > nav { flex-wrap:wrap; row-gap:var(--space-1); } }
375
-
376
- /* ── GAVETA DE NAVEGAÇÃO (achado A8) ───────────────────────────────────────────────
377
- Empilhar a lateral acima do conteúdo passava, no gate de rolagem lateral, e era
378
- inutilizável: medido em 375px, o `h1` da página começava 4,6 telas abaixo, atrás dos 65
379
- itens da navegação. Abaixo de md ela sai do fluxo e vira gaveta.
380
- SEM UMA LINHA DE JS: o <aside> é um popover nativo e o botão do topo é o invoker. O
381
- navegador entrega Escape, clique fora para fechar, `aria-expanded` no disparador, a volta
382
- do foco ao fechar e o ::backdrop. Baseline desde 04/2025; verificado em 30/07/2026.
383
- Não-modal de propósito (o foco não fica preso), como o CommandPaletteShell: declarar
384
- modalidade sem entregá-la é pior que não declarar. */
385
- .btn.nav-toggle { display:none; }
386
- /* O UA aplica um bloco inteiro a `[popover]` mesmo FECHADO — `width:fit-content`, `inset:0`,
387
- `margin:auto`, borda e padding próprios. A `.sidebar` já sobrescreve quase tudo; sobravam
388
- largura e insets, e isso encolheu a lateral do DESKTOP de 232px para 225px. Quem pegou foi o
389
- gate de pixel, não o olho: 4.573 pixels de diferença numa captura de 0,03 de proporção. */
390
- .sidebar[popover]:not(:popover-open) { width:auto; inset-inline:auto; inset-block-end:auto; }
391
- /* Quem esconde a gaveta fechada é ESTA regra, não o `display:none` do UA para `[popover]`:
392
- regra de autor vence regra de UA por ORIGEM, e a `.sidebar` já declara `display:flex`.
393
- Medido: sem esta linha a lateral seguia empilhada em 375px, com o botão já visível.
394
- O @supports é a rede: em navegador sem popover a regra inteira não aplica, a lateral volta a
395
- empilhar (o comportamento de antes, feio e usável) em vez de sumir sem botão que a abra. */
396
- @supports selector(:popover-open) {
397
- @media (max-width:767px) {
398
- .sidebar[popover]:not(:popover-open) { display:none; }
399
- .btn.nav-toggle { display:inline-flex; }
400
- /* altura EXPLÍCITA, não `auto`: medido em 30/07/2026, com `top` e `bottom` postos e
401
- `height:auto` o Chromium resolveu pela altura do CONTEÚDO (3.498px de lateral rolando a
402
- página inteira), não pelas âncoras. `dvh` porque a barra do navegador móvel some e volta. */
403
- .sidebar[popover]:popover-open { position:fixed; inset-block:var(--space-4); inset-inline-start:var(--space-4); width:min(20rem,calc(100vw - var(--space-8))); height:calc(100dvh - var(--space-4) * 2); margin:0; }
404
- .sidebar[popover]::backdrop { background:var(--overlay); }
405
- }
406
- }
407
-
408
- /* ── Visibilidade responsiva o mecanismo PADRÃO de colapsar uma barra ─────────────
409
- Regra do projeto (enraizada): todo layout horizontal que aperta esconde o secundário
410
- por breakpoint da escala, nunca rola de lado. Estas 4 classes são esse mecanismo; o
411
- gate (check 4b) trava os breakpoints. Mobile-first: `below` some acima do ponto. */
412
- @media (max-width:1023px) { .hide-below-lg { display:none !important; } }
413
- @media (max-width:767px) { .hide-below-md { display:none !important; } }
414
- .brand { min-height:var(--topbar-height); display:flex; align-items:center; gap:12px; padding:2px 4px 16px; margin:0 0 16px; border:0; border-bottom:1px solid var(--sidebar-border); border-radius:0; background:transparent; box-shadow:none; }
415
- .brand strong { display:block; letter-spacing:var(--tracking-tight); } .brand small { display:block; color:var(--muted-foreground); font-size:11px; }
416
- /* minmax(0,1fr): a trilha `auto` do grid crescia até o nome mais longo e os itens
417
- VAZAVAM pela direita da caixa (colavam na borda de um lado e sobrava do outro). */
418
- .doc-nav { padding:0 0 8px; display:grid; grid-template-columns:minmax(0,1fr); gap:var(--space-1); }
419
- .btn.mobile-nav-toggle,.btn.mobile-nav-close { display:none; }
420
- /* .nav-group-label é o rótulo de categoria SEM ser título de documento: mesma aparência do
421
- h4 histórico, mas o grupo se nomeia por aria-labelledby e não entra na lista de títulos
422
- do leitor de tela (senão a lateral vem antes do h1 da página). É o jeito preferido. */
423
- .doc-nav h4,.doc-nav .nav-group-label { margin:16px 8px 8px; color:var(--subtle-foreground); font-size:11px; letter-spacing:var(--tracking-widest); text-transform:uppercase; }
424
- .doc-nav .nav-group { display:grid; gap:var(--space-1); }
425
- .doc-nav a { display:flex; align-items:center; gap:10px; min-height:40px; margin:0; padding:9px 12px; border:1px solid transparent; border-radius:var(--radius-lg); background:transparent; color:var(--muted-foreground); text-decoration:none; box-shadow:none; }
426
- .doc-nav a:hover { color:var(--sidebar-accent-foreground); background:var(--sidebar-accent); border-color:var(--sidebar-border); }
427
- .doc-nav a.active { border-color:var(--sidebar-border); }
428
- /* o glifo da família fica na frente do rótulo e nunca encolhe o texto */
429
- .doc-nav a .icon { flex:none; opacity:.8; }
430
- .doc-nav a.active .icon { opacity:1; }
431
- .eyebrow { display:inline-flex; align-items:center; gap:8px; color:var(--primary); font-size:12px; font-weight:var(--weight-bold); letter-spacing:var(--tracking-widest); text-transform:uppercase; }
432
- /* No claro o amarelo puro como texto não alcança 4.5:1 (axe). Aprofundar com --foreground
433
- mantém o matiz da marca e passa o AA; no escuro segue o amarelo cheio. */
434
- [data-theme="light"] .eyebrow { color:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
435
- .demo { min-width:0; padding:var(--card-pad); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); box-shadow:none; }
436
-
437
- /* ========================================================================== */
438
- /* 04. COMPONENTES PRIMITIVOS */
439
- /* ========================================================================== */
440
- .btn { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:transparent; display:inline-flex; align-items:center; justify-content:center; gap:8px; height:var(--control-h-md); padding:0 15px; border:1px solid var(--btn-border); border-radius:var(--radius-control); background:var(--btn-bg); color:var(--btn-fg); font-size:var(--text-sm); font-weight:var(--weight-medium); line-height:var(--leading-none); text-decoration:none; white-space:nowrap; cursor:pointer; transition:transform var(--duration-fast) var(--ease-standard),background var(--duration-fast),border-color var(--duration-fast),color var(--duration-fast),opacity var(--duration-fast); }
441
- .btn:hover:not(:disabled) { transform:none; } .btn:active:not(:disabled) { transform:scale(.97); opacity:.9; }
442
- .btn:disabled { opacity:.45; cursor:not-allowed; }
443
- /* Cinco alturas, todas de token de densidade (autorizado pelo Victor em 24/07/2026). O raio
444
- é pill em TODAS — o tamanho muda altura e respiro, nunca a forma. Em ponteiro grosso o
445
- mínimo de 44px embaixo pega o xs e o sm, então alvo pequeno não vira alvo impossível. */
446
- .btn-xs { height:var(--control-h-xs); padding:0 10px; gap:6px; font-size:var(--text-xs); border-radius:var(--radius-control); }
447
- .btn-sm { height:var(--control-h-sm); padding:0 12px; font-size:var(--text-xs); border-radius:var(--radius-control); }
448
- .btn-lg { height:var(--control-h-lg); padding:0 20px; font-size:var(--text-base); border-radius:var(--radius-control); }
449
- .btn-xl { height:var(--control-h-xl); padding:0 26px; gap:10px; font-size:var(--text-base); border-radius:var(--radius-control); }
450
- /* Largura total: para rodapé de formulário e tela estreita. a largura muda. */
451
- .btn-block { width:100%; }
452
- .btn-primary { --btn-bg:var(--primary); --btn-fg:var(--primary-foreground); --btn-border:transparent; }
453
- .btn-primary:hover:not(:disabled) { --btn-bg:var(--primary-hover); --btn-border:transparent; }
454
- .btn-secondary { --btn-bg:var(--secondary); --btn-fg:var(--secondary-foreground); --btn-border:transparent; }
455
- .btn-secondary:hover:not(:disabled) { --btn-bg:var(--secondary-hover); --btn-border:transparent; }
456
- .btn-outline { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:var(--border-strong); }
457
- .btn-outline:hover:not(:disabled) { --btn-bg:var(--accent); }
458
- .btn-ghost { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:transparent; }
459
- .btn-ghost:hover:not(:disabled) { --btn-bg:var(--accent); --btn-fg:var(--accent-foreground); }
460
- .btn-danger { --btn-bg:var(--destructive); --btn-fg:var(--destructive-foreground); --btn-border:transparent; }
461
- .btn-danger:hover:not(:disabled) { --btn-bg:var(--danger-400); }
462
- /* Destrutivo em OUTRAS hierarquias: apagar não é sempre a ação principal da tela. O tom é o
463
- mesmo (--destructive); o que muda é o peso. No claro o vermelho puro como texto não chega
464
- a 4.5:1, então aprofunda com --foreground mesmo tratamento do amarelo. */
465
- .btn-danger-outline { --btn-bg:transparent; --btn-fg:var(--destructive); --btn-border:var(--destructive); }
466
- .btn-danger-outline:hover:not(:disabled) { --btn-bg:color-mix(in oklab, var(--destructive) 14%, transparent); }
467
- .btn-danger-ghost { --btn-bg:transparent; --btn-fg:var(--destructive); --btn-border:transparent; }
468
- .btn-danger-ghost:hover:not(:disabled) { --btn-bg:color-mix(in oklab, var(--destructive) 14%, transparent); }
469
- [data-theme="light"] .btn-danger-outline,[data-theme="light"] .btn-danger-ghost,[data-theme="light"] .btn-link-danger { --btn-fg:color-mix(in oklab, var(--destructive) 62%, var(--foreground)); }
470
- /* LINK: aparência de texto, sem cápsula e sem altura de controle — o sublinhado é o que diz
471
- que é clicável, então ele existe desde o repouso (revelar no hover esconde a affordance).
472
- Três tons: neutro, marca e destrutivo. No claro a marca aprofunda, senão não passa o AA. */
473
- /* `min-height:24px` = WCAG 2.2 SC 2.5.8. A exceção de "alvo em linha" NÃO cobre este caso:
474
- medido no catálogo, o "Learn more" é ação isolada dentro de um flex, não link no meio de
475
- prosa, e media 67×13px. O texto continua onde está — a caixa cresce centrada nele. */
476
- .btn-link { height:auto; min-height:24px; padding:0; gap:6px; border:0; border-radius:var(--radius-sm); --btn-bg:transparent; --btn-fg:var(--foreground); text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px; }
477
- .btn-link:hover:not(:disabled) { text-decoration-thickness:2px; }
478
- .btn-link-primary { --btn-fg:var(--primary); }
479
- [data-theme="light"] .btn-link-primary { --btn-fg:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
480
- .btn-link-danger { --btn-fg:var(--destructive); }
481
- /* pressed entra na linguagem única de "selecionado" acima; só-ícone não leva o fio. */
482
- /* Kbd a tecla que dispara a ação, dentro ou fora do botão. Peça própria, não decoração. */
483
- .kbd { display:inline-flex; align-items:center; justify-content:center; gap:2px; min-width:20px; height:20px; padding:0 6px; border:1px solid var(--border-strong); border-radius:var(--radius-sm); background:var(--secondary); color:var(--muted-foreground); font:500 11px/1 var(--font-code); }
484
- /* dentro do botão a tecla herda a cor do rótulo, SEM opacidade: a 85% o contraste caía
485
- para 3.67:1 (axe) e o atalho é informação, não enfeite. O fundo já o separa. */
486
- /* Dentro do botão a tecla NÃO ganha fundo próprio: qualquer tinta escurece a cápsula e o
487
- rótulo perde contraste (no primary claro caiu a 3.55:1). Sem fundo, o contraste da
488
- tecla é o MESMO do rótulo — se o botão passa, ela passa. A borda de currentColor separa. */
489
- .btn .kbd { background:transparent; border-color:color-mix(in oklab, currentColor 35%, transparent); color:inherit; }
490
-
491
- /* nav: item de navegação. Normal = ghost silencioso; o ATIVO (aria-current) ganha texto
492
- amarelo + traço embaixo — SEM borda (pedido do Victor). aria-current, não uma variante
493
- "ativa", porque ele também anuncia "página atual" pro leitor de tela. O traço é
494
- ::after, pill, alinhado ao padding do texto; posição pelo eixo bloco (não espelha RTL). */
495
- .btn-nav { --btn-bg:transparent; --btn-fg:var(--muted-foreground); --btn-border:transparent; }
496
- .btn-nav:hover:not(:disabled) { --btn-fg:var(--foreground); }
497
- .btn-icon { width:var(--control-h-md); padding:0; border-radius:var(--radius-md); }
498
- .btn-icon.btn-xs { width:var(--control-h-xs); border-radius:var(--radius-sm); }
499
- .btn-icon.btn-sm { width:var(--control-h-sm); border-radius:var(--radius-sm); } .btn-icon.btn-lg { width:var(--control-h-lg); border-radius:var(--radius-md); }
500
- /* xs e xl também precisam de largura: sem isto a altura mudava e a largura ficava na do
501
- md, e o alvo só-ícone deixava de ser quadrado (achado na onda Actions, 24/07). */
502
- .btn-icon.btn-xl { width:var(--control-h-xl); border-radius:var(--radius-md); }
503
- /* wrap, não estoura: os botões do grupo são pills com gap (não segmentos colados), então
504
- quebrar linha em tela estreita preserva o visual. Único gap de eixo horizontal que a
505
- auditoria da Etapa 3 achou sem proteção — o resto já rola (tabela) ou já quebra (toolbar). */
506
- .btn-group { display:inline-flex; flex-wrap:wrap; align-items:center; gap:4px; } .btn-group .btn { border-radius:var(--radius-control); margin-inline-start:0; }
507
- .btn-group .btn:first-child { margin-inline-start:0; border-radius:var(--radius-control); }
508
- .btn-group .btn:last-child { border-radius:var(--radius-control); }
509
- /* Toolbar: contêiner de controles; o roving tabindex vem do Base UI. */
510
- .toolbar { display:flex; align-items:center; flex-wrap:wrap; gap:6px; padding:6px; border:1px solid var(--border); border-radius:var(--radius-control); background:var(--secondary); }
511
- /* Vertical sai da pílula: --radius-control (999px) clampa em 50% da menor
512
- dimensão e vira um oval num container alto. */
513
- .toolbar[data-orientation="vertical"] { flex-direction:column; align-items:stretch; border-radius:var(--radius-lg); }
514
- .toolbar-group { display:flex; align-items:center; gap:4px; }
515
- .toolbar-sep { flex:none; align-self:stretch; inline-size:1px; min-block-size:20px; margin-inline:4px; margin-block:0; background:var(--border); border:0; }
516
- .toolbar[data-orientation="vertical"] .toolbar-sep { align-self:auto; inline-size:auto; block-size:1px; min-block-size:0; margin-inline:0; margin-block:4px; }
517
- .spinner { width:16px; height:16px; border:2px solid currentColor; border-right-color:transparent; border-radius:50%; animation:spin .7s linear infinite; } @keyframes spin { to { transform:rotate(360deg); } }
518
-
519
- .field { display:flex; flex-direction:column; gap:7px; }
520
- .label { display:flex; justify-content:space-between; gap:12px; color:var(--foreground); font-size:var(--text-sm); font-weight:var(--weight-semibold); }
521
- .hint { color:var(--muted-foreground); font-size:var(--text-xs); }
522
- .input,.textarea,.select { width:100%; min-width:0; height:var(--control-h-md); padding:0 13px; color:var(--foreground); background:var(--field-bg); border:1px solid var(--border-strong); border-radius:var(--radius-control); transition:border-color var(--duration-fast),background-color var(--duration-fast),box-shadow var(--duration-fast); }
523
- .input:hover,.textarea:hover,.select:hover { border-color:var(--border); }
524
- .input::placeholder,.textarea::placeholder { color:var(--subtle-foreground); }
525
- .input:disabled,.textarea:disabled,.select:disabled { opacity:.5; cursor:not-allowed; }
526
- .input[aria-invalid="true"],.textarea[aria-invalid="true"] { border-color:var(--danger-400); }
527
- .textarea { min-height:104px; padding:12px 14px; resize:vertical; border-radius:var(--radius-lg); }
528
- /* chevron: cor = --muted-foreground de cada tema (dark #a1a1a1, light #505050); tokenizar na Fase 1 */
529
- .select { appearance:none; padding-inline-end:38px; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23a1a1a1' stroke-width='1.6'/%3E%3C/svg%3E"); background-position:calc(100% - 12px) 50%; background-size:12px 8px; background-repeat:no-repeat; }
530
- [data-theme="light"] .select { background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23505050' stroke-width='1.6'/%3E%3C/svg%3E"); }
531
- /* Widgets nativos dos campos (calendário do type=date, dropdown do select, scrollbar do textarea) seguem o tema */
532
- .input,.textarea,.select { color-scheme:dark; }
533
- [data-theme="light"] .input,[data-theme="light"] .textarea,[data-theme="light"] .select { color-scheme:light; }
534
- .input-wrap { position:relative; } .input-wrap>.icon { position:absolute; inset-inline-start:12px; top:50%; transform:translateY(-50%); color:var(--subtle-foreground); pointer-events:none; } .input-wrap .input { padding-inline-start:40px; }
535
- .field-error { color:var(--danger-400); font-size:var(--text-xs); }
536
- .checkbox,.radio { display:inline-flex; align-items:flex-start; gap:9px; color:var(--foreground); cursor:pointer; }
537
- .checkbox input,.radio input { position:absolute; opacity:0; pointer-events:none; }
538
- .control-mark { width:18px; height:18px; margin-top:1px; display:grid; place-items:center; border:1px solid var(--border-strong); background:var(--field-bg); }
539
- .checkbox .control-mark { border-radius:5px; } .radio .control-mark { border-radius:50%; }
540
- .checkbox input:checked + .control-mark { background:var(--control-selected); border-color:var(--control-selected); color:var(--control-selected-foreground); }
541
- .checkbox input:focus-visible + .control-mark,.radio input:focus-visible + .control-mark,.switch input:focus-visible + .switch-track { outline:2px solid var(--focus-strong); outline-offset:2px; }
542
- .checkbox input:checked + .control-mark:after { content:""; width:8px; height:4px; border-left:2px solid currentColor; border-bottom:2px solid currentColor; transform:rotate(-45deg) translateY(-1px); }
543
- .radio input:checked + .control-mark:after { content:""; width:8px; height:8px; border-radius:50%; background:var(--control-selected); }
544
- .switch { display:inline-flex; align-items:center; gap:10px; cursor:pointer; } .switch input { position:absolute; opacity:0; }
545
- .switch-track { position:relative; width:42px; height:24px; border-radius:999px; background:var(--muted); border:1px solid var(--border); transition:background var(--duration-base) var(--ease-standard),border-color var(--duration-base) var(--ease-standard); }
546
- .switch-track:after { content:""; position:absolute; inset-inline-start:3px; top:3px; width:16px; height:16px; border-radius:50%; background:var(--muted-foreground); transition:transform var(--duration-base) var(--ease-standard),background var(--duration-base) var(--ease-standard); }
547
- .switch input:checked + .switch-track { background:var(--control-selected); border-color:var(--control-selected); } .switch input:checked + .switch-track:after { transform:translateX(18px); background:var(--control-selected-foreground); }
548
- .range { width:100%; accent-color:var(--primary); }
549
- /* A CÁPSULA é que tem de medir igual a um botão/campo ao lado — não o botão de dentro.
550
- Antes: `height:30px` no filho, cru. Em comfortable dava 30+3+3=36 = --control-h-md por
551
- coincidência; em compact a cápsula ficava 4px alta demais e em spacious 4px baixa demais,
552
- porque a densidade não alcançava a regra (achado A1, 26/07/2026). Com a altura na cápsula
553
- os filhos esticam sozinhos (align-items é stretch por padrão) e a conta fecha nas TRÊS. */
554
- .segmented { display:inline-flex; gap:3px; padding:3px; min-height:var(--control-h-md); border-radius:var(--radius-control); background:var(--muted); border:1px solid transparent; box-shadow:none; }
555
- .segmented button { padding:0 14px; border:0; border-radius:var(--radius-control); color:var(--muted-foreground); background:transparent; cursor:pointer; }
556
- .segmented button.active { color:var(--foreground); background:var(--secondary); box-shadow:none; border:0; }
557
- /* ── SELECIONADO: uma linguagem ────────────────────────────────────────────────────
558
- Item de nav ativo, aba ativa, item de lista lateral e botão alternado ligado mostram o
559
- MESMO sinal: cápsula elevada + rótulo na cor da marca + fio amarelo encostado na borda de
560
- baixo. Um usuário aprende uma vez e reconhece em todo lugar (pedido do Victor, 24/07).
561
- O fio é 2px (fração de pixel borra), inset 15px, e o amarelo entra a 75% — cheio ele
562
- grita mais que o próprio rótulo. No tema CLARO o amarelo puro como texto 1.6:1, longe
563
- dos 4.5:1 do AA, então o rótulo aprofunda com --foreground e segue âmbar. */
564
- .is-selected,.btn-nav[aria-current]:not([aria-current="false"]),.btn[aria-pressed="true"],
565
- .segmented button.active,.doc-nav a.active { --btn-bg:var(--secondary); --btn-fg:var(--primary); position:relative; background:var(--secondary); color:var(--primary); }
566
- .is-selected::after,.btn-nav[aria-current]:not([aria-current="false"])::after,
567
- .btn[aria-pressed="true"]:not(.btn-icon)::after,.segmented button.active::after,
568
- .doc-nav a.active::after { content:""; position:absolute; inset-inline:15px; bottom:0; height:2px; border-radius:var(--radius-full); background:color-mix(in oklab, var(--primary) 75%, transparent); }
569
- [data-theme="light"] .is-selected,[data-theme="light"] .btn-nav[aria-current]:not([aria-current="false"]),
570
- [data-theme="light"] .btn[aria-pressed="true"],[data-theme="light"] .segmented button.active,
571
- [data-theme="light"] .doc-nav a.active { --btn-fg:color-mix(in oklab, var(--primary) 45%, var(--foreground)); color:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
572
-
573
- /* `line-height:1` não é enfeite: sem ele o .badge herdava a entrelinha do pai e a MESMA
574
- classe media 26px dentro do .brand (lh 18px) e 22px dentro de um .btn-nav (lh 12px) na
575
- mesma barra (achado A2, 26/07/2026). Geometria de componente não se decide pelo contexto.
576
- O .chip do catálogo tinha aprendido isso; o .badge não. */
577
- .badge { display:inline-flex; align-items:center; gap:6px; min-height:var(--space-6); padding:3px 9px; border:1px solid var(--border); border-radius:var(--radius-control); background:var(--secondary); color:var(--secondary-foreground); font-size:var(--text-xs); font-weight:var(--weight-medium); line-height:var(--leading-none); white-space:nowrap; }
578
- /* O glifo do badge tem tamanho PRÓPRIO, senão o `.icon` padrão (20px) estoura os 24px e o
579
- badge com ícone fica mais alto que o badge de texto na mesma fila. 16px + 3+3 de padding
580
- + 1+1 de borda = 24px exatos: com ícone ou sem, mede o mesmo. */
581
- .badge .icon { width:var(--icon-sm); height:var(--icon-sm); }
582
- .badge-primary { color:var(--primary-emphasis); border-color:var(--primary-outline); background:color-mix(in srgb,var(--primary) 10%,transparent); }
583
-
584
- .badge-success { color:var(--success-400); background:var(--success-bg); border-color:color-mix(in srgb,var(--success-400) 35%,var(--border)); }
585
- .badge-warning { color:var(--warning-400); background:var(--warning-bg); border-color:color-mix(in srgb,var(--warning-400) 35%,var(--border)); }
586
- .badge-danger { color:var(--danger-400); background:var(--danger-bg); border-color:color-mix(in srgb,var(--danger-400) 35%,var(--border)); }
587
- .badge-info,.badge-running { color:var(--info-400); background:var(--info-bg); border-color:color-mix(in srgb,var(--info-400) 35%,var(--border)); }
588
- .status-dot { width:8px; height:8px; border-radius:50%; background:currentColor; box-shadow:0 0 0 3px color-mix(in srgb,currentColor 14%,transparent); }
589
- /* Status: condição operacional (DIRECTION §3.6) NÃO é o pill do Badge; é ponto + rótulo
590
- solto na linha. A variante pinta o .status (o ponto herda por currentColor) e o rótulo
591
- volta pra --foreground, senão a frase inteira fica colorida e ilegível no claro. Os
592
- nomes operacionais e os semânticos apontam pro mesmo par de tokens de propósito. */
593
- .status { display:inline-flex; align-items:center; gap:var(--space-2); color:var(--muted-foreground); font-size:var(--text-sm); }
594
- .status-label { color:var(--foreground); }
595
- .status-online,.status-success { color:var(--success-400); }
596
- .status-away,.status-warning { color:var(--warning-400); }
597
- .status-busy,.status-danger { color:var(--danger-400); }
598
- .status-running,.status-info { color:var(--info-400); }
599
- /* offline é o único que NÃO é "mais uma cor": --subtle-foreground e --muted-foreground
600
- colapsam no MESMO valor no tema claro, então offline sairia idêntico ao neutral. Ponto
601
- VAZADO resolve nos dois temas e ainda diz a coisa certa (ausência, não outro estado).
602
- Escopado em .status-offline: o .status-dot dentro do Badge (Chat) segue preenchido. */
603
- .status-offline { color:var(--subtle-foreground); }
604
- .status-offline .status-dot { background:transparent; box-shadow:inset 0 0 0 2px currentColor,0 0 0 3px color-mix(in srgb,currentColor 14%,transparent); }
605
-
606
- .card { min-width:0; padding:var(--card-pad); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); box-shadow:none; }
607
- .card-raised { box-shadow:none; } .card-inset { background:var(--secondary); }
608
- .card-interactive { cursor:pointer; transition:transform var(--duration-fast),border-color var(--duration-fast),background var(--duration-fast); }
609
- .card-interactive:hover { transform:translateY(-2px); border-color:var(--border-strong); background:var(--surface-2); }
610
- .card-selected { border-color:var(--primary-outline); box-shadow:inset 0 0 0 1px var(--primary-outline); }
611
- .card-danger { border-color:color-mix(in srgb,var(--danger-400) 50%,var(--border)); background:var(--danger-bg); }
612
- .card-header { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:16px; }
613
- .card-title { margin:0; font-size:var(--text-base); } .card-subtitle { margin:3px 0 0; color:var(--muted-foreground); font-size:var(--text-sm); }
614
-
615
- .alert { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:start; gap:12px; padding:14px 16px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--secondary); box-shadow:none; }
616
- /* Banner: aviso de largura de página. Base ANTES das variantes mesma
617
- especificidade, então a última regra vence e a base apagaria as cores. */
618
- .banner { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:12px; padding:14px 18px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--secondary); }
619
- .alert-info,.banner-info { border-color:color-mix(in srgb,var(--info-400) 38%,var(--border)); background:var(--info-bg); }
620
- .alert-success,.banner-success { border-color:color-mix(in srgb,var(--success-400) 38%,var(--border)); background:var(--success-bg); }
621
- .alert-warning,.banner-warning { border-color:color-mix(in srgb,var(--warning-400) 38%,var(--border)); background:var(--warning-bg); }
622
- .alert-danger,.banner-danger { border-color:color-mix(in srgb,var(--danger-400) 38%,var(--border)); background:var(--danger-bg); }
623
- .alert h4 { margin:0 0 2px; font-size:var(--text-sm); } .alert p { margin:0; color:var(--muted-foreground); font-size:var(--text-sm); }
624
- .banner strong { font-size:var(--text-sm); } .banner p { margin:0; color:var(--muted-foreground); font-size:var(--text-sm); }
625
- @media (max-width:640px) { .banner { grid-template-columns:auto minmax(0,1fr); } .banner>:last-child { grid-column:2; justify-self:start; } }
626
- .progress { height:8px; overflow:hidden; border-radius:999px; background:var(--surface-3); }
627
- .progress>span { display:block; height:100%; border-radius:inherit; background:var(--primary); }
628
- .skeleton { position:relative; overflow:hidden; border-radius:7px; background:var(--surface-3); }
629
- .skeleton { animation:skeleton-pulse 1.35s ease-in-out infinite; } @keyframes skeleton-pulse { 50% { opacity:.55; } }
630
-
631
- /* Mesma regra da .segmented: a cápsula carrega a altura da densidade, o filho estica. */
632
- .tabs { display:inline-flex; gap:3px; padding:3px; min-height:var(--control-h-md); border:0; border-radius:var(--radius-control); background:var(--muted); }
633
- .tab { position:relative; padding:0 14px; border:0; border-radius:var(--radius-control); background:transparent; color:var(--muted-foreground); cursor:pointer; }
634
- .tab.active,.tab[data-active] { color:var(--foreground); background:var(--secondary); } .tab.active:after,.tab[data-active]:after { display:none; }
635
- .breadcrumb { display:flex; align-items:center; flex-wrap:wrap; gap:7px; color:var(--muted-foreground); font-size:var(--text-sm); }
636
- .breadcrumb a { color:inherit; text-decoration:none; } .breadcrumb a:hover { color:var(--foreground); }
637
- .pagination { display:flex; gap:6px; align-items:center; }
638
- /* `:not(.btn)` porque isto é o quadrado de NÚMERO de página. Sem o :not, o seletor de
639
- elemento vencia o `.btn-sm` do Anterior/Próxima por especificidade (0,1,1 contra 0,1,0)
640
- e forçava 34px neles era a terceira altura de .btn-sm que a auditoria mediu (B3). */
641
- .pagination button:not(.btn) { width:var(--control-h-md); height:var(--control-h-md); border:1px solid var(--border); border-radius:var(--radius-control); background:transparent; color:var(--muted-foreground); cursor:pointer; }
642
- .pagination button.active { color:var(--primary-foreground); background:var(--primary); border-color:var(--primary); }
643
- .step { position:relative; text-align:center; color:var(--subtle-foreground); }
644
- .step:before { content:""; position:absolute; top:15px; inset-inline-start:-50%; width:100%; height:2px; background:var(--border); } .step:first-child:before { display:none; }
645
- .step.done,.step.active { color:var(--foreground); } .step.done:before,.step.active:before { background:var(--primary); } .step.done .step-dot { color:var(--primary-foreground); background:var(--primary); border-color:var(--primary); } .step.active .step-dot { border-color:var(--primary); box-shadow:0 0 0 4px color-mix(in srgb,var(--primary) 14%,transparent); }
646
-
647
- .table-wrap { overflow:auto; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
648
- table { width:100%; border-collapse:collapse; min-width:720px; } th,td { height:var(--row-h); padding:0 14px; text-align:start; border-bottom:1px solid var(--border); }
649
- th { color:var(--subtle-foreground); background:var(--surface-2); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; } tr:last-child td { border-bottom:0; } tbody tr:hover { background:var(--surface-2); }
650
- .avatar { width:36px; height:36px; display:grid; place-items:center; border-radius:50%; background:var(--surface-3); color:var(--foreground); border:1px solid var(--border-strong); font-weight:var(--weight-bold); } .avatar-group .avatar { margin-inline-start:-9px; border:2px solid var(--surface-1); } .avatar-group .avatar:first-child { margin-inline-start:0; }
651
- .kpi { display:flex; flex-direction:column; gap:5px; }
652
- /* DataList: chave/valor em duas colunas. O termo é secundário mesmo idioma do cabeçalho de
653
- tabela. Sem a regra (achado A13) o <dl> saía cru: dt e dd empilhados, com o recuo de 40px
654
- que o navegador ao <dd>. */
655
- .data-list { display:grid; grid-template-columns:max-content minmax(0,1fr); gap:var(--space-2) var(--space-6); margin:0; }
656
- .data-list dt { color:var(--muted-foreground); }
657
- .data-list dd { min-width:0; margin:0; }
658
- /* Timeline: o trilho existia e o PONTO não — `.timeline-dot` media 0×0 (achado A13), então a
659
- linha do tempo saía com a régua e sem as marcas, e o <ol> ainda mostrava a numeração do
660
- navegador. Agora os dois derivam do MESMO token: o trilho fica a meio diâmetro do ponto, e
661
- alinham por construção em vez de por número copiado. Saem junto os 24px de recuo e os 7px
662
- do trilho dois px crus a menos na catraca do check 12. */
663
- .timeline { position:relative; display:flex; flex-direction:column; gap:var(--space-5); margin:0; padding:0; list-style:none; }
664
- .timeline:before { content:""; position:absolute; inset-inline-start:calc(var(--space-3) / 2); inset-block:var(--space-2); width:1px; background:var(--border); }
665
- .timeline > li { display:flex; align-items:flex-start; gap:var(--space-3); }
666
- /* A coluna de conteúdo é uma PILHA, e não por estética: sem isso o item empilhava quando
667
- tinha descrição (o <p> é bloco) e colava título e horário na mesma linha quando não tinha —
668
- "Shipped14:20". Geometria de componente não depende do conteúdo (achados A2/A3). */
669
- .timeline > li > div { display:flex; flex-direction:column; gap:var(--space-05); min-width:0; }
670
- .timeline > li > div > p { margin:0; }
671
- .timeline-dot { flex:none; width:var(--space-3); height:var(--space-3); border-radius:var(--radius-full); margin-block-start:var(--space-1); background:var(--primary); box-shadow:0 0 0 var(--space-05) var(--surface-1); }
672
- /* Accordion: anatomia medida no Carbon em 30/07/2026 (REFERENCES.md) — cabeçalho na altura de
673
- controle, divisória de 1px por item, corpo com respiro pequeno em cima e grande embaixo.
674
- O marcador é o NATIVO do <summary>: gira sozinho e custa zero código, e é por isso que o
675
- summary continua `display:list-item` pôr flex nele apagaria o marcador. */
676
- .accordion { display:flex; flex-direction:column; border-block:1px solid var(--border); }
677
- .accordion > details + details { border-top:1px solid var(--border); }
678
- .accordion > details > summary { min-height:var(--control-h-md); padding-block:var(--space-2); padding-inline:var(--space-5) var(--space-2); color:var(--foreground); font-weight:var(--weight-medium); line-height:var(--leading-normal); cursor:pointer; }
679
- .accordion > details > summary:hover { background:var(--surface-hover); }
680
- .accordion > details > summary::marker { color:var(--muted-foreground); }
681
- /* Recuo INLINE, não `padding-left`: o marcador do <summary> troca de lado em RTL e o corpo
682
- tem de acompanhar. Foi o gate de RTL que cobrou — ele mede o eixo inline do core. */
683
- .accordion-content { padding-block:0 var(--space-6); padding-inline:var(--space-5) var(--space-2); color:var(--muted-foreground); }
684
- /* Mockup do kit de origem (apps/docs) — não é o Accordion do sistema, que é <details> nativo. */
685
- .accordion-item.open .accordion-panel { display:block; } .accordion-item.open .accordion-trigger .icon { transform:rotate(180deg); }
686
-
687
- /* QRCode: superfície flutuante Aurea, mas o miolo é de alto contraste FIXO — um QR
688
- não segue o tema (dark inverteria e nem todo leitor decodifica invertido). Override
689
- consciente por --qr-module/--qr-quiet. Módulos suaves (rx no SVG) sem perder escaneabilidade. */
690
- .qrcode { --qr-module:#0b0b0c; --qr-quiet:#fff; box-sizing:border-box; padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--qr-quiet); }
691
- .qrcode .qr-mod { fill:var(--qr-module); }
692
- .qrcode .qr-eye-gap { fill:var(--qr-quiet); }
693
-
694
- .code-block { overflow:auto; padding:16px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--surface-inset); color:var(--muted-foreground); font:12px/1.7 var(--font-code); }
695
- /* Bloco de código COM copiar: a moldura não rola, o <pre> rola por dentro e o gatilho fica
696
- parado no canto. O comportamento é do core (aurea.js, [data-aurea-copy]); aqui a pele.
697
- O check em amarelo é confirmação instantânea dura 1,5s e volta ao ícone de copiar. */
698
- /* checkbox com rótulo fora da tela: sem o texto, o alvo é só a marca nada de gap sobrando. */
699
- .checkbox-bare { gap:0; }
700
- .code-block-wrap { position:relative; display:flex; min-width:0; }
701
- .code-block-wrap > .code-block { flex:1; min-width:0; margin:0; }
702
- .copy-code { position:absolute; top:var(--space-3); inset-inline-end:var(--space-3); z-index:1; }
703
- .copy-code .c-done { display:none; }
704
- .copy-code.is-done .c-copy { display:none; } .copy-code.is-done .c-done { display:block; color:var(--primary); }
705
- /* "On this page": índice da própria página, fixo na lateral. Navegação, não decoração
706
- por isso é <nav> com rótulo, e o item atual se marca com aria-current. */
707
- .toc { position:sticky; top:var(--space-4); align-self:start; display:grid; gap:var(--space-1); max-height:calc(100vh - var(--space-8)); overflow:auto; scrollbar-width:none; padding:var(--space-2) 0; }
708
- .toc::-webkit-scrollbar { width:0; }
709
- .toc-label { margin:0 0 var(--space-1); padding-inline:var(--space-3); color:var(--subtle-foreground); font-size:11px; letter-spacing:var(--tracking-widest); text-transform:uppercase; }
710
- .toc a { padding:var(--space-1) var(--space-3); border-inline-start:2px solid var(--border); color:var(--muted-foreground); font-size:var(--text-sm); text-decoration:none; }
711
- .toc a:hover { color:var(--foreground); }
712
- .toc a[aria-current] { border-inline-start-color:var(--primary); color:var(--foreground); }
713
- .toc a.toc-sub { padding-inline-start:var(--space-6); }
714
- .log-stream { height:240px; overflow:auto; padding:14px; border:1px solid var(--border); border-radius:var(--radius-md); background:var(--surface-inset); font:12px/1.65 var(--font-code); }
715
- .log-line { display:grid; grid-template-columns:86px 72px minmax(0,1fr); gap:12px; padding:3px 0; } .log-time { color:var(--subtle-foreground); } .log-level { color:var(--primary); } .log-level.warn { color:var(--warning-400); } .log-level.error { color:var(--danger-400); }
716
-
717
- /* ========================================================================== */
718
- /* 05. GRÁFICOS E VISUALIZAÇÃO */
719
- /* ========================================================================== */
720
- .chart { width:100%; height:220px; } .chart-bar.alt { fill:var(--chart-1); } .health-cell.warn { background:var(--warning-500); } .health-cell.bad { background:var(--danger-500); } .health-cell.off { background:var(--surface-3); }
721
- .message { display:grid; grid-template-columns:auto minmax(0,1fr); gap:12px; } .message-bubble { padding:12px 14px; border-radius:6px var(--radius-lg) var(--radius-lg) var(--radius-lg); background:var(--secondary); } .message-bubble p { margin:0; }
722
- /* Chat React (Fase 5.4): estrutura fixa em classe, não inline (auditoria 18/07, M14). */
723
- .message-status { display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); margin-block-start:var(--space-2); }
724
- .message-composer { display:flex; align-items:center; gap:var(--space-3); flex-wrap:nowrap; } .message-composer .input-wrap { flex:1; min-width:0; }
725
-
726
- /* ========================================================================== */
727
- /* 07. MÍDIA */
728
- /* ========================================================================== */
729
- .media-player { position:relative; isolation:isolate; overflow:hidden; min-height:360px; aspect-ratio:16/9; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--media-canvas); color:var(--media-foreground); }
730
- .media-viewport { position:absolute; inset:0; display:grid; place-items:center; background:var(--media-canvas); }
731
- .media-fill { width:100%; height:100%; object-fit:contain; } /* vídeo React preenche o viewport (auditoria 18/07, M14) */
732
- .media-placeholder { display:grid; place-items:center; gap:10px; color:var(--media-muted); text-align:center; }
733
- .media-placeholder .icon { width:48px; height:48px; color:var(--media-foreground); }
734
- .media-placeholder strong { color:var(--media-foreground); }
735
- .media-overlay-title { position:absolute; top:16px; inset-inline:16px; z-index:2; display:flex; align-items:flex-start; justify-content:space-between; gap:16px; color:var(--media-foreground); }
736
- .media-overlay-title>div>span { display:block; color:var(--media-muted); font-size:var(--text-xs); }
737
- .media-player .badge { border-color:var(--media-track); background:var(--media-track); color:var(--media-foreground); }
738
- .media-controls { position:absolute; inset:auto 0 0; z-index:3; display:grid; gap:8px; padding:12px; background:var(--media-scrim); color:var(--media-foreground); }
739
- .media-seek { --media-played:34%; position:relative; height:20px; display:flex; align-items:center; }
740
- .media-seek-track { position:absolute; inset-inline:0; height:4px; overflow:hidden; border-radius:var(--radius-full); background:var(--media-track); pointer-events:none; }
741
- .media-seek-buffered,.media-seek-played { position:absolute; inset-block:0; inset-inline:0 auto; border-radius:inherit; }
742
- .media-seek-buffered { width:62%; background:var(--media-buffered); }
743
- .media-seek-played { width:var(--media-played); background:var(--primary); }
744
- .media-seek input { position:relative; z-index:1; width:100%; height:20px; margin:0; appearance:none; background:transparent; cursor:pointer; }
745
- .media-seek input::-webkit-slider-runnable-track { height:4px; background:transparent; }
746
- .media-seek input::-webkit-slider-thumb { width:14px; height:14px; margin-top:-5px; appearance:none; border:2px solid var(--media-foreground); border-radius:50%; background:var(--primary); }
747
- .media-seek input::-moz-range-track { height:4px; background:transparent; }
748
- .media-seek input::-moz-range-thumb { width:12px; height:12px; border:2px solid var(--media-foreground); border-radius:50%; background:var(--primary); }
749
- .media-seek input:focus-visible { outline:2px solid var(--primary); outline-offset:3px; box-shadow:none; }
750
- .media-control-row,.media-control-group { display:flex; align-items:center; gap:4px; }
751
- .media-control-row { min-width:0; justify-content:space-between; }
752
- .media-control { min-width:36px; height:36px; display:inline-grid; place-items:center; padding:0 8px; border:1px solid transparent; border-radius:var(--radius-md); background:transparent; color:var(--media-foreground); cursor:pointer; }
753
- .media-control:hover,.media-control.active { background:var(--media-track); color:var(--primary); }
754
- .media-time { padding:0 7px; color:var(--media-muted); font:var(--text-xs) var(--font-code); white-space:nowrap; }
755
- .media-volume { width:82px; accent-color:var(--primary); }
756
-
757
- /* ========================================================================== */
758
- /* 08. ÍCONES */
759
- /* ========================================================================== */
760
- .icon-search { max-width:420px; }
761
- .icon-category { margin-top:26px; } .icon-category h3 { margin:0 0 12px; font-size:var(--text-base); }
762
- .icon-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:8px; }
763
- .icon-tile { min-height:86px; display:flex; flex-direction:column; align-items:flex-start; justify-content:space-between; gap:12px; padding:13px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--secondary); color:var(--muted-foreground); cursor:pointer; text-align:start; transition:transform var(--duration-fast) var(--ease-standard),border-color var(--duration-fast),color var(--duration-fast); }
764
- @media (hover:hover) and (pointer:fine) { .icon-tile:hover { color:var(--foreground); border-color:var(--primary); transform:translateY(-1px); } } .icon-tile:focus-visible { color:var(--foreground); border-color:var(--primary); } .icon-tile span { overflow:hidden; max-width:100%; text-overflow:ellipsis; white-space:nowrap; font:11px var(--font-code); }
765
- .icon-tile.copied { color:var(--primary); border-color:var(--primary); } .overlay.open { display:grid; }
766
- .dialog-backdrop { position:fixed; inset:0; z-index:var(--z-modal); background:var(--overlay); backdrop-filter:blur(5px); }
767
- .dialog { position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); z-index:var(--z-modal); width:min(560px,calc(100vw - 40px)); max-height:calc(100vh - 40px); overflow:auto; padding:24px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
768
- .dialog>header,.drawer>header { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:16px; } .dialog-body { display:grid; gap:14px; }
769
- .drawer-wrap { position:fixed; inset:0; z-index:var(--z-drawer); display:none; background:var(--overlay); } .drawer-wrap.open { display:block; }
770
- .drawer-backdrop { position:fixed; inset:0; z-index:var(--z-drawer); background:var(--overlay); }
771
- .drawer { position:fixed; top:0; bottom:0; z-index:var(--z-drawer); width:min(480px,92vw); padding:24px; background:var(--surface-1); box-shadow:var(--shadow-lg); overflow:auto; } .drawer-right { right:0; border-left:1px solid var(--border); } .drawer-left { left:0; border-right:1px solid var(--border); }
772
- .toast-stack { position:fixed; inset-inline-end:22px; bottom:22px; z-index:var(--z-toast); width:min(360px,calc(100vw - 44px)); display:flex; flex-direction:column; gap:10px; } .toast { display:flex; align-items:center; gap:11px; padding:14px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-md); transition:opacity .28s var(--ease-standard), transform .28s var(--ease-emphasized); } .toast>:first-child { flex:1; min-width:0; } .toast-text { display:flex; flex-direction:column; gap:2px; min-width:0; } .toast[data-starting-style],.toast[data-ending-style] { opacity:0; transform:translateY(12px); }
773
- /* CommandPaletteShell (Fase 11, achado A13). A superfície existia aqui sob o nome
774
- `.command` e `.command` é chrome da página de docs manual: nenhum componente React a
775
- emite. Ela passava no check 15 por ACIDENTE, porque o rótulo padrão "Type a command…" põe
776
- a palavra "command" numa string do pacote React e o gate lê as strings do fonte. Foi para
777
- apps/docs/docs.css, que é a casa dela; o sistema fica com os dois nomes que o componente
778
- emite de verdade. Não-modal de propósito, como a gaveta do AppShell (ADR-0003): o foco não
779
- fica preso, então não declaramos modalidade que não entregamos. */
780
- .command-overlay { position:fixed; inset:0; z-index:var(--z-modal); display:grid; place-items:start center; padding:var(--space-16) var(--space-5) var(--space-5); background:var(--overlay); backdrop-filter:blur(5px); }
781
- .command-palette { display:grid; gap:var(--space-2); width:min(680px,100%); max-height:100%; overflow:auto; padding:var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
782
- /* Camadas flutuantes (Base UI): tooltip, popover, menu. z-modal para funcionarem sobre diálogos; empilhamento mútuo pela ordem do portal no DOM. */
783
- .tooltip { z-index:var(--z-modal); max-width:260px; padding:6px 10px; border:1px solid var(--border); border-radius:var(--radius-md); background:var(--surface-inset); color:var(--foreground); font-size:var(--text-xs); line-height:var(--leading-normal); box-shadow:var(--shadow-md); }
784
- .popover { z-index:var(--z-modal); min-width:220px; max-width:min(360px,calc(100vw - 32px)); padding:16px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); display:grid; gap:10px; }
785
- .menu { z-index:var(--z-modal); min-width:200px; max-width:min(320px,calc(100vw - 32px)); padding:6px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
786
- .menu-item { display:flex; align-items:center; gap:10px; width:100%; min-height:var(--control-h-md); padding:0 10px; border:0; border-radius:var(--radius-sm); background:transparent; color:var(--foreground); font:inherit; text-align:start; cursor:pointer; }
787
- .menu-item[data-highlighted],.menu-item:hover:not([data-disabled]) { background:var(--surface-3); outline:none; }
788
- .menu-item[data-disabled] { opacity:.5; cursor:not-allowed; }
789
- .menu-sep { height:1px; margin:6px 4px; background:var(--border); border:0; }
790
- /* Combobox: reaproveita .menu/.menu-item; dimensionamento e a coluna do check são próprios.
791
- --anchor-width e --available-height vêm do positioner do Base UI. */
792
- .combobox-group { position:relative; display:flex; align-items:center; }
793
- .combobox-group .input { padding-inline-end:calc(var(--control-h-sm) * 2 + 10px); }
794
- .combobox-actions { position:absolute; inset-inline-end:5px; display:flex; align-items:center; gap:2px; }
795
- .combobox-popup { inline-size:var(--anchor-width); min-inline-size:0; max-inline-size:none; max-block-size:min(320px,var(--available-height)); overflow-y:auto; overscroll-behavior:contain; }
796
- .combobox-empty { padding:12px 10px; color:var(--muted-foreground); font-size:var(--text-sm); }
797
- .combobox-empty:empty { display:none; }
798
- .combobox-item { display:grid; grid-template-columns:16px minmax(0,1fr); gap:8px; align-items:center; }
799
- .combobox-check { display:inline-flex; color:var(--primary); }
800
- /* MultiCombobox (Fase 4) chips de multi-seleção + grupos. Espaçamento novo tokenizado. */
801
- .combobox-multi { display:flex; align-items:center; gap:var(--space-2); width:100%; min-width:0; min-height:var(--control-h-md); padding:var(--space-1) var(--space-2); background:var(--field-bg); border:1px solid var(--border-strong); border-radius:var(--radius-control); transition:border-color var(--duration-fast); }
802
- .combobox-multi:hover { border-color:var(--border); }
803
- .combobox-multi:focus-within { outline:2px solid var(--focus-strong); outline-offset:2px; border-color:var(--focus-strong); }
804
- .combobox-multi>.combobox-actions { position:static; inset-inline-end:auto; }
805
- .combobox-chips { display:flex; flex:1; flex-wrap:wrap; align-items:center; gap:var(--space-2); min-width:0; }
806
- .combobox-chip { display:inline-flex; align-items:center; gap:var(--space-1); padding-block:var(--space-1); padding-inline:var(--space-3) var(--space-1); background:var(--secondary); color:var(--foreground); border-radius:var(--radius-control); font-size:var(--text-sm); }
807
- .combobox-chip-remove { display:inline-flex; padding:0; border:0; background:transparent; color:var(--muted-foreground); border-radius:var(--radius-full); cursor:pointer; }
808
- .combobox-chip-remove:hover { color:var(--foreground); }
809
- .combobox-chip-input { flex:1; min-width:6ch; height:var(--control-h-sm); border:0; background:transparent; padding:0 var(--space-1); color:var(--foreground); }
810
- .combobox-chip-input::placeholder { color:var(--subtle-foreground); }
811
- .combobox-chip-input:focus-visible { outline:none; }
812
- .combobox-group-label { padding:var(--space-1) var(--space-2); color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); }
813
- /* FileInput / DropZone (Fase 4) a UI de entrada; upload real é a Fase 5.
814
- A zona é um <label> em volta do <input> nativo: clique/foco/teclado de graça,
815
- o arrastar-soltar é reforço. Espaçamento novo tokenizado; superfície = card. */
816
- .dropzone { display:flex; flex-direction:column; align-items:center; gap:var(--space-2); padding:var(--space-8) var(--space-6); text-align:center; color:var(--muted-foreground); background:var(--surface-inset); border:1px dashed var(--border-strong); border-radius:var(--radius-card); cursor:pointer; transition:border-color var(--duration-fast),background-color var(--duration-fast); }
817
- .dropzone:hover { border-color:var(--border); }
818
- .dropzone:focus-within { outline:2px solid var(--focus-strong); outline-offset:2px; border-color:var(--focus-strong); }
819
- .dropzone[data-dragging="true"] { border-style:solid; border-color:var(--primary); background:var(--surface-2); }
820
- .dropzone strong { color:var(--foreground); }
821
- .dropzone-icon { color:var(--primary); }
822
- .dropzone-hint { font-size:var(--text-xs); }
823
- .file-list { display:flex; flex-direction:column; gap:var(--space-2); margin-top:var(--space-3); padding:0; list-style:none; }
824
- .file-item { display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2) var(--space-3); background:var(--surface-2); border:1px solid var(--border); border-radius:var(--radius-md); }
825
- .file-progress { flex-basis:var(--space-20); flex-shrink:0; } /* largura fixa da barra de upload (auditoria 18/07, M14) */
826
- .file-name { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
827
- .file-size { flex:none; color:var(--muted-foreground); font-size:var(--text-xs); font-variant-numeric:tabular-nums; }
828
-
829
- /* TreeView (Fase 4) — árvore headless própria (Base UI 1.6 não tem tree).
830
- role=tree/treeitem/group + roving tabindex vêm do React; aqui só a pele.
831
- A indentação por nível é padding-inline-start inline (style). Espaço novo
832
- tokenizado; a linha não é pill (radius-sm, como o .menu-item). */
833
- .tree, .tree-group { margin:0; padding:0; list-style:none; }
834
- .tree-item { outline:none; }
835
- .tree-node { display:flex; align-items:center; gap:var(--space-2); min-height:var(--control-h-md); padding-block:var(--space-1); padding-inline-end:var(--space-3); border-radius:var(--radius-sm); color:var(--foreground); cursor:pointer; user-select:none; }
836
- .tree-node:hover { background:var(--surface-3); }
837
- .tree-item:focus-visible > .tree-node { outline:2px solid var(--focus-strong); outline-offset:-2px; }
838
- .tree-node[data-selected] { background:color-mix(in srgb,var(--primary) 14%,transparent); }
839
- .tree-twist { flex:none; color:var(--muted-foreground); transition:transform var(--duration-fast); }
840
- .tree-item[aria-expanded="true"] > .tree-node > .tree-twist { transform:rotate(90deg); }
841
- .tree-item[aria-expanded="false"]:dir(rtl) > .tree-node > .tree-twist { transform:rotate(180deg); }
842
- .tree-indent { flex:none; width:1rem; }
843
- .tree-label { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
844
-
845
- /* NotificationCenter (Fase 4) — compõe o .popover; aqui só o gatilho com count,
846
- o cabeçalho e as linhas do feed. Espaço novo tokenizado; a linha usa radius-sm
847
- (não é pill, como o .menu-item/.tree-node). O painel NÃO é live region — a
848
- chegada é anunciada por um role=status à parte no React. */
849
- .notification-trigger { position:relative; display:inline-flex; }
850
- .notification-count { position:absolute; inset-block-start:-4px; inset-inline-end:-4px; display:inline-flex; align-items:center; justify-content:center; min-width:18px; height:18px; padding:0 var(--space-1); border-radius:var(--radius-control); background:var(--primary); color:var(--primary-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); line-height:var(--leading-none); pointer-events:none; }
851
- .notification-panel { width:min(360px,calc(100vw - 32px)); max-width:none; padding:0; gap:0; display:flex; flex-direction:column; overflow:hidden; }
852
- .notification-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); padding:var(--space-3) var(--space-4); border-block-end:1px solid var(--border); }
853
- .notification-head strong { font-size:var(--text-sm); }
854
- .notification-list { margin:0; padding:var(--space-1); overflow-y:auto; max-height:min(60vh,440px); }
855
- .notification-sublist { margin:0; padding:0; list-style:none; }
856
- .notification-group-label { margin:0; padding:var(--space-2) var(--space-3) var(--space-1); color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); letter-spacing:var(--tracking-wide); text-transform:uppercase; }
857
- .notification-item { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:baseline; column-gap:var(--space-3); row-gap:var(--space-1); width:100%; padding:var(--space-3); border:0; border-radius:var(--radius-sm); background:none; color:var(--foreground); font:inherit; text-align:start; }
858
- button.notification-item { cursor:pointer; }
859
- button.notification-item:hover { background:var(--surface-3); }
860
- .notification-item:focus-visible { outline:2px solid var(--focus-strong); outline-offset:-2px; }
861
- .notification-dot { grid-row:1/-1; align-self:center; width:8px; height:8px; border-radius:50%; background:var(--primary); }
862
- .notification-item[data-read] .notification-dot { visibility:hidden; }
863
- .notification-item-title { grid-column:2; display:flex; align-items:center; gap:var(--space-2); min-width:0; font-size:var(--text-sm); font-weight:var(--weight-medium); }
864
- .notification-item[data-read] .notification-item-title { color:var(--muted-foreground); font-weight:var(--weight-regular); }
865
- .notification-item-desc { grid-column:2; color:var(--muted-foreground); font-size:var(--text-xs); }
866
- .notification-time { grid-column:3; grid-row:1; color:var(--muted-foreground); font-size:var(--text-xs); white-space:nowrap; }
867
- .notification-empty { margin:0; padding:var(--space-8) var(--space-4); color:var(--muted-foreground); font-size:var(--text-sm); text-align:center; }
868
- /* EmptyState é o membro GRANDE da família "aqui não nada" .notification-empty acima e
869
- .datagrid-empty abaixo respondiam a mesma pergunta do mesmo jeito (centralizado, muted),
870
- e o componente público era o único dos três sem regra (achado A13). A ordem das partes
871
- (imagem título corpo → ação) é a do padrão de empty state do Carbon, registrado em
872
- REFERENCES.md; a pele é Aurea. O título vinha com o h3 do NAVEGADOR (16,38px). */
873
- .empty-state { display:flex; flex-direction:column; align-items:center; gap:var(--space-3); padding:var(--space-10) var(--space-6); color:var(--muted-foreground); text-align:center; }
874
- .empty-state > .icon { color:var(--subtle-foreground); }
875
- .empty-state p { max-width:46ch; margin:0; }
876
- .empty-title { margin:0; color:var(--foreground); font-size:var(--text-base); font-weight:var(--weight-semibold); line-height:var(--leading-tight); }
877
-
878
- /* DataGrid (Fase 4) TanStack Table v8 headless; aqui só a pele, que reusa
879
- .table-wrap/table/th/td. aria-sort fica só no th ordenado; a linha
880
- selecionada usa data-selected (o estado acessível é o checkbox da linha).
881
- Espaço novo tokenizado. */
882
- .datagrid { display:grid; gap:var(--space-3); }
883
- .datagrid > .table-wrap:focus-visible { outline:none; box-shadow:0 0 0 3px color-mix(in oklab,var(--focus) 38%,transparent); }
884
- .datagrid > .pagination { justify-self:end; }
885
- /* Fix aditivo: os 34px dos botões numéricos do .pagination esmagavam os botões
886
- de TEXTO (Anterior/Próxima) do Pagination React. Não muda os numéricos. */
887
- /* text-transform/letter-spacing: o UA reseta nos <button>; herdar explícito
888
- para o cabeçalho ordenável ficar idêntico ao th estático. */
889
- .datagrid-sort { display:inline-flex; align-items:center; gap:var(--space-1); padding:0; border:0; background:none; color:inherit; font:inherit; text-transform:inherit; letter-spacing:inherit; cursor:pointer; }
890
- .datagrid-sort:focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; border-radius:var(--radius-sm); }
891
- .datagrid-sort-icon { flex:none; color:var(--subtle-foreground); }
892
- th[aria-sort] .datagrid-sort-icon { color:var(--primary); }
893
- .datagrid-selcol { width:1%; white-space:nowrap; }
894
- .datagrid tbody tr[data-selected] { background:color-mix(in srgb,var(--primary) 14%,transparent); }
895
- .datagrid-empty { padding:var(--space-6); color:var(--muted-foreground); text-align:center; }
896
-
897
- /* ========================================================================== */
898
- /* 10. RESPONSIVIDADE */
899
- /* ========================================================================== */
900
- @media (max-width:1366px) and (min-width:821px) { :root { --sidebar-width:216px; } }
901
- /* Aperto de tela baixa: é densidade do SHELL DOS DOCS (.doc-sidebar), não da .doc-nav em
902
- si sem o escopo ela zerava o gap de qualquer nav, inclusive a do catálogo. */
903
- @media (max-height:800px) and (min-width:821px) { .doc-sidebar .brand { min-height:var(--target-min); padding:0 2px 8px; margin-bottom:6px; } .doc-sidebar .brand strong { font-size:var(--text-sm); } .doc-sidebar .brand small { font-size:10px; } .doc-sidebar .doc-nav { gap:0; padding-bottom:0; } .doc-sidebar .doc-nav h4 { margin:8px 4px 4px; font-size:10px; } .doc-sidebar .doc-nav a { min-height:30px; padding:5px 8px; font-size:var(--text-xs); } }
904
- /* Legado do catálogo escrito à mão: `display:block` desmancha o grid e empilha a lateral.
905
- Agora vale SÓ para o shell que não tem gaveta — o `apps/docs/index.html`, que traz a
906
- própria `.doc-sidebar` e o próprio JS de abrir. O shell do sistema segue em grid de uma
907
- coluna com a gaveta acima. Sai junto com aquela página (achado M13). */
908
- @media (max-width:820px) { .app-shell:not(:has(> .sidebar[popover])) { display:block; } }
909
- @media (max-width:820px) { .btn.mobile-nav-toggle { display:inline-flex; } .btn.mobile-nav-close { display:inline-flex; margin-inline-start:auto; } .mobile-nav-backdrop.open { display:block; } }
910
- @media (max-width:640px) { :root { --card-pad:16px; --section-gap:16px; } .doc-actions .select { flex:1; width:auto!important; min-width:0; } .doc-actions>.btn-primary { width:var(--control-h-md); padding:0; font-size:0; } .doc-actions>.btn-primary .mono { display:none; } .tabs,.pagination,.segmented { max-width:100%; overflow-x:auto; } .step:nth-child(3):before { display:none; } .alert { grid-template-columns:auto minmax(0,1fr); } .alert>:last-child { grid-column:2; justify-self:start; } .log-line { grid-template-columns:70px 58px minmax(0,1fr); gap:6px; } .queue-item>.badge { grid-column:2; justify-self:start; } .event-row>.badge { grid-column:3; justify-self:start; } .message { grid-template-columns:1fr; } .media-player { min-height:330px; aspect-ratio:auto; } .media-overlay-title { top:12px; left:12px; right:12px; } .media-controls { padding:10px; } .media-control-row { align-items:flex-start; flex-direction:column; gap:8px; } .media-control-row>.media-control-group { width:100%; justify-content:space-between; } .media-optional,.media-volume { display:none; } .media-time { padding:0 4px; font-size:11px; } .toast-stack { inset-inline:12px; bottom:12px; } .toast { width:100%; min-width:0; grid-template-columns:minmax(0,1fr) auto; } .drawer { width:100%; padding:20px; } .dialog { padding:20px; } }
911
- @media (prefers-reduced-motion:reduce) { *,*:before,*:after { scroll-behavior:auto!important; animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important; } }
912
-
913
- /* Padrões compostos Aurea 1.7: blocos de operação, não widgets de domínio. */
914
- .operational-grid .demo{min-height:0}@media(max-width:640px){.operational-grid .demo{grid-column:span 12}}
915
- .universal-grid .demo { min-height:0; }
916
- .pattern-toolbar .input-wrap { flex:1 1 180px; }
917
- .media-library-row.active { border-color:var(--primary); }
918
- /* Alvo de toque. `.tabs`/`.segmented` no lugar de `.tab`/`.segmented button`: agora a altura
919
- mora na cápsula, então é ela que precisa crescer — o filho estica atrás. */
920
- @media (pointer:coarse) { .btn:not(.btn-link),.tabs,.segmented,.pagination button,.schedule-grid button,.media-control { min-height:var(--target-min); } .btn-icon,.media-control { min-width:var(--target-min); } }
921
- @media (forced-colors:active) {
922
- :focus-visible { outline:2px solid CanvasText; outline-offset:2px; box-shadow:none; }
923
- .badge,.pattern-gallery button[aria-pressed="true"],.device-grid button[aria-pressed="true"] { border:1px solid CanvasText; }
924
- }
925
-
926
-
927
- /* Aurea v1.7 stabilization patch visual design unchanged. */
928
- .badge-running{background:var(--success-bg);color:var(--success-400);border-color:color-mix(in oklab,var(--success-400) 32%,transparent)}
929
- .table-region{overflow:auto;outline:none;border-radius:var(--radius-lg)}
930
- .table-region:focus-visible{box-shadow:0 0 0 3px color-mix(in oklab,var(--focus) 38%,transparent)}
931
- @media(max-width:400px){
932
- .media-controls{flex-wrap:wrap;overflow:visible!important;gap:8px}
933
- .media-controls .time-display{order:2;width:100%}
934
- .media-control{min-width:var(--target-min);min-height:var(--target-min)}
935
- }
936
- @media(pointer:coarse){
937
- .media-control,.btn-icon{min-width:var(--target-min);min-height:var(--target-min)}
938
- }
939
-
940
- /* ========================================================================== */
941
- /* 12. RTL */
942
- /* ========================================================================== */
943
- /* O eixo inline do CSS acima é lógico e espelha sozinho. Aqui ficam só os
944
- casos sem forma lógica: background-position e translateX.
945
- Deliberadamente NÃO espelham:
946
- - o check do .checkbox e o .spinner são glifos desenhados com borda;
947
- - .map-* e .builder-* são geografia/arte com rotate() fixo — espelhar os
948
- nós sem as arestas quebraria o desenho;
949
- - .drawer-left/.drawer-right prometem lado físico no nome (prop side). */
950
- :dir(rtl) .select { background-position:12px 50%; }
951
- :dir(rtl) .switch input:checked + .switch-track:after { transform:translateX(-18px); }
276
+ /* Aurea core — component styles. Built with @aurea-uds/tokens into dist/aurea.css. Load @aurea-uds/fonts/css for IBM Plex. */
277
+ /* ========================================================================== */
278
+ /* 02. RESET, ACESSIBILIDADE E BASE */
279
+ /* ========================================================================== */
280
+ * { box-sizing:border-box; }
281
+ html { scroll-behavior:smooth; }
282
+ body { margin:0; background:var(--background); color:var(--foreground); font-family:var(--font-ui); font-size:var(--text-md); line-height:var(--leading-normal); }
283
+ ::selection { background:var(--selection); }
284
+ button,input,textarea,select { font:inherit; }
285
+ button,a,[role="button"],input,textarea,select { outline:none; }
286
+ :focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; box-shadow:none; border-color:var(--focus-strong)!important; }
287
+ button { color:inherit; }
288
+ a { color:var(--primary); text-underline-offset:3px; }
289
+ img,svg { display:block; }
290
+ /* O `display:none` que o browser dá pro atributo `hidden` vem da folha do UA e PERDE de
291
+ qualquer `display:` de classe nossa — o painel some do fluxo de acessibilidade mas
292
+ continua na tela. Já mordeu o popover do MediaPlayer e as abas do catálogo. Aqui a
293
+ regra fecha a classe inteira do bug, em vez de um remendo por componente.
294
+ `until-found` fica de fora: ali o `hidden` é revelável e o browser cuida. */
295
+ [hidden]:not([hidden="until-found"]) { display:none!important; }
296
+ .sr-only { position:absolute!important; width:1px!important; height:1px!important; padding:0!important; margin:-1px!important; overflow:hidden!important; clip:rect(0,0,0,0)!important; white-space:nowrap!important; border:0!important; }
297
+ .icon { width:var(--icon-md); height:var(--icon-md); fill:currentColor; flex:none; }
298
+ .icon-sm { width:var(--icon-sm); height:var(--icon-sm); } .icon-lg { width:var(--icon-lg); height:var(--icon-lg); } .icon-xl { width:var(--icon-xl); height:var(--icon-xl); }
299
+ .muted { color:var(--muted-foreground); }
300
+ .stack { display:flex; flex-direction:column; gap:var(--space-4); }
301
+ .cluster { display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); }
302
+ /* Grid: o terceiro primitivo de layout, com o mesmo gap do .stack. A pele nasceu na Fase 11
303
+ (achado A13): o componente existia desde sempre e saía `display:block` — empilhava. Ninguém
304
+ via porque o chrome do catálogo REDEFINIA .grid, então ele parecia pronto na nossa
305
+ documentação e chegava sem nada no consumidor. `min(...,100%)` porque `minmax(15rem,1fr)`
306
+ estoura o contêiner quando o texto vai a 200% e a coluna fica maior que a caixa (lição do
307
+ M12); --grid-min é o botão de ajuste, no idioma de --qr-module e --demo-height. */
308
+ .grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(min(var(--grid-min,15rem),100%),1fr)); gap:var(--space-4); }
309
+
310
+ /* ========================================================================== */
311
+ /* 03. APP SHELL E DOCUMENTAÇÃO */
312
+ /* ========================================================================== */
313
+ /* Regiões do AppShell — SUPERFÍCIES FLUTUANTES (decisão do Victor, 23/07/2026): topo,
314
+ lateral e conteúdo são caixas soltas de raio --radius-card, separadas por --space-4 e
315
+ descoladas da borda da janela pelo padding do shell. NADA de barra colada com linha
316
+ divisória: caixa retangular genérica é proibida (CLAUDE.md).
317
+ Arranjo escolhido: o TOPO atravessa toda a largura (grid-column 1/-1) e leva a marca;
318
+ a lateral começa abaixo dele. Ambos grudam na rolagem por `sticky` — o conteúdo passa
319
+ por baixo e as duas caixas seguem visíveis.
320
+ ATENÇÃO: `.app-shell` NÃO se toca. O catálogo escrito à mão (apps/docs) usa essa MESMA
321
+ classe como raiz, com filhos próprios (.doc-sidebar/.main-wrap) — mexer nela desloca as
322
+ 26 telas de referência inteiras (aconteceu; o portão visual pegou). Por isso a folga
323
+ entre as caixas sai da MARGEM de cada filho, não de gap/padding no shell. As linhas
324
+ extras do grid nascem sozinhas por auto-placement. */
325
+ .app-shell { min-height:100vh; display:grid; grid-template-columns:var(--sidebar-width) minmax(0,1fr); background:var(--background); }
326
+ /* A base do topo carrega só o ARRANJO; cada variante traz a própria superfície.
327
+ floating = caixa solta (a que já estava) · flush = faixa encostada nas bordas com fio
328
+ embaixo, para o topo virar moldura da janela · pill = cápsula centrada, de cabeçalho
329
+ de site. Só floating e flush entram no grid do AppShell; a pill é peça avulsa (numa
330
+ pílula não cabem busca + densidade + tema + comando). */
331
+ .topbar { grid-column:1/-1; position:sticky; z-index:var(--z-sticky); display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); min-height:var(--topbar-height); }
332
+ .topbar-floating { top:var(--space-4); margin:var(--space-4) var(--space-4) 0; padding:var(--space-2) var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); }
333
+ /* flush: encostada nas bordas. SEM fio embaixo (pedido do Victor — a referência não tem)
334
+ e sem superfície própria: ela se funde ao fundo e cola no conteúdo, que começa logo
335
+ abaixo. É o topo do nosso catálogo. */
336
+ .topbar-flush { top:0; padding:var(--space-2) var(--space-5); background:var(--background); }
337
+ .topbar-pill { top:var(--space-4); width:max-content; max-width:calc(100% - var(--space-8)); margin:var(--space-4) auto 0; justify-content:flex-start; gap:var(--space-2); padding:var(--space-2); border:1px solid var(--border); border-radius:var(--radius-control); background:var(--card); color:var(--card-foreground); }
338
+ /* .brand nasceu como cabeçalho VERTICAL da lateral (borda embaixo + respiro); no topo
339
+ ele deita, então some com a borda e a margem — o lockup em si continua o mesmo. */
340
+ .topbar .brand { min-height:auto; margin:0; padding:0; border:0; }
341
+ /* altura fixa = janela menos topo e as 3 folgas; assim a caixa parece cheia mesmo com
342
+ poucos itens (é o que as referências mostram) e sobra o que rolar por dentro. */
343
+ .sidebar { --shell-top:calc(var(--topbar-height) + var(--space-4)*2); position:sticky; top:var(--shell-top); align-self:start; height:calc(100vh - var(--shell-top) - var(--space-4)); margin:var(--space-4); display:flex; flex-direction:column; gap:var(--space-1); padding:var(--space-4); overflow:auto; scrollbar-width:none; border:1px solid var(--sidebar-border); border-radius:var(--radius-card); background:var(--sidebar); color:var(--sidebar-foreground); }
344
+ /* a lateral ROLA mas nunca mostra a barra (pedido do Victor): a barra nativa risca a
345
+ superfície flutuante e não segue o raio de 22px. `scrollbar-width` cobre Firefox e
346
+ Chrome 121+; o pseudo cobre Safari e Chrome antigo. */
347
+ .sidebar::-webkit-scrollbar { width:0; height:0; }
348
+ /* com o topo `flush` a barra COLA no conteúdo (pedido do Victor): a folga acima da lateral
349
+ e do conteúdo é --space-1 (4px), e o --shell-top acompanha. */
350
+ /* flush cola no conteúdo com --space-05 (2px) — o degrau mais apertado da escala, criado
351
+ pra este caso (o Victor aprovou os 2px; tokenizado, não cru). */
352
+ .app-shell-flush .sidebar { --shell-top:calc(var(--topbar-height) + var(--space-05)); margin-block-start:var(--space-05); }
353
+ .app-shell-flush .content { margin-block-start:var(--space-05); }
354
+ .content { min-width:0; margin:var(--space-4) var(--space-4) var(--space-4) 0; padding:var(--space-6); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); }
355
+ /* O topo é presentational — não conhece o conteúdo — então garante o mínimo universal:
356
+ os filhos encolhem em vez de forçar rolagem lateral. O COLAPSO de conteúdo (esconder a
357
+ busca, trocar a nav por menu) é do consumidor, com os utilitários responsivos abaixo. */
358
+ .topbar > * { min-width:0; }
359
+ /* QUEBRA POR CONTEÚDO. O `flex-wrap` vivia só dentro do @media abaixo, ou seja, a barra só
360
+ quebrava quando a JANELA era estreita. Texto a 200% (WCAG 1.4.4) aumenta o conteúdo sem
361
+ mexer no viewport: o @media não disparava e a nav passava 27px da borda direita, em todas
362
+ as páginas. Quebrar por conteúdo cobre os dois casos e não muda nada quando cabe. */
363
+ .topbar,.topbar > nav { flex-wrap:wrap; }
364
+ /* Colapso do shell abaixo de md (767 = md-1, escala): a lateral deixa de flutuar e de ter
365
+ altura de janela — empilha acima do conteúdo, que passa a ocupar a largura toda. Sem
366
+ estado (o off-canvas com botão é refino da Fase 3); o essencial é nunca rolar de lado. */
367
+ /* Abaixo de md o topo QUEBRA em duas linhas em vez de esconder a navegação: perder o acesso
368
+ a uma área inteira não é colapso responsivo, é bug. A nav vai pra segunda linha, centrada
369
+ e na largura toda; badge e selo (secundários) seguem escondidos pelos utilitários. */
370
+ @media (max-width:767px) { .app-shell { grid-template-columns:minmax(0,1fr); } .sidebar { position:static; height:auto; } .content { margin-inline-start:var(--space-4); }
371
+ .topbar { flex-wrap:wrap; row-gap:var(--space-2); }
372
+ .topbar > nav { order:3; flex-basis:100%; justify-content:center; }
373
+ /* e a nav quebra entre os PRÓPRIOS itens: mandar a barra pra segunda linha não bastava,
374
+ as cinco áreas somam 371px e estouravam a 320px (auditoria 26/07, A1). */
375
+ .topbar > nav { flex-wrap:wrap; row-gap:var(--space-1); } }
376
+
377
+ /* ── GAVETA DE NAVEGAÇÃO (achado A8) ───────────────────────────────────────────────
378
+ Empilhar a lateral acima do conteúdo passava, no gate de rolagem lateral, e era
379
+ inutilizável: medido em 375px, o `h1` da página começava 4,6 telas abaixo, atrás dos 65
380
+ itens da navegação. Abaixo de md ela sai do fluxo e vira gaveta.
381
+ SEM UMA LINHA DE JS: o <aside> é um popover nativo e o botão do topo é o invoker. O
382
+ navegador entrega Escape, clique fora para fechar, `aria-expanded` no disparador, a volta
383
+ do foco ao fechar e o ::backdrop. Baseline desde 04/2025; verificado em 30/07/2026.
384
+ Não-modal de propósito (o foco não fica preso), como o CommandPaletteShell: declarar
385
+ modalidade sem entregá-la é pior que não declarar. */
386
+ .btn.nav-toggle { display:none; }
387
+ /* O UA aplica um bloco inteiro a `[popover]` mesmo FECHADO — `width:fit-content`, `inset:0`,
388
+ `margin:auto`, borda e padding próprios. A `.sidebar` já sobrescreve quase tudo; sobravam
389
+ largura e insets, e isso encolheu a lateral do DESKTOP de 232px para 225px. Quem pegou foi o
390
+ gate de pixel, não o olho: 4.573 pixels de diferença numa captura de 0,03 de proporção. */
391
+ .sidebar[popover]:not(:popover-open) { width:auto; inset-inline:auto; inset-block-end:auto; }
392
+ /* Quem esconde a gaveta fechada é ESTA regra, não o `display:none` do UA para `[popover]`:
393
+ regra de autor vence regra de UA por ORIGEM, e a `.sidebar` já declara `display:flex`.
394
+ Medido: sem esta linha a lateral seguia empilhada em 375px, com o botão já visível.
395
+ O @supports é a rede: em navegador sem popover a regra inteira não aplica, a lateral volta a
396
+ empilhar (o comportamento de antes, feio e usável) em vez de sumir sem botão que a abra. */
397
+ @supports selector(:popover-open) {
398
+ @media (max-width:767px) {
399
+ .sidebar[popover]:not(:popover-open) { display:none; }
400
+ .btn.nav-toggle { display:inline-flex; }
401
+ /* altura EXPLÍCITA, não `auto`: medido em 30/07/2026, com `top` e `bottom` postos e
402
+ `height:auto` o Chromium resolveu pela altura do CONTEÚDO (3.498px de lateral rolando a
403
+ página inteira), não pelas âncoras. `dvh` porque a barra do navegador móvel some e volta. */
404
+ .sidebar[popover]:popover-open { position:fixed; inset-block:var(--space-4); inset-inline-start:var(--space-4); width:min(20rem,calc(100vw - var(--space-8))); height:calc(100dvh - var(--space-4) * 2); margin:0; }
405
+ .sidebar[popover]::backdrop { background:var(--overlay); }
406
+ }
407
+ }
408
+
409
+ /* ── A LISTA DA LATERAL (PLANO-1.0, item B1) ───────────────────────────────────────
410
+ Até 06/08/2026 a Sidebar era só a caixa: item, grupo, aninhamento e marca de item atual
411
+ existiam neste arquivo como `.doc-nav`, que é CHROME do catálogo (achado A6) e nunca foi
412
+ API pública. Consequência medida: nenhum consumidor conseguia desenhar uma navegação sem
413
+ reescrevê-la, e a peça estava aqui.
414
+ ITEM ATUAL por [aria-current] e não por uma classe `.active`: o estado que o leitor de tela
415
+ anuncia e o que o olho passam a ser o MESMO atributo, então não como marcar um e
416
+ esquecer o outro que é como o `.doc-nav` carrega `.active` E `aria-current` até hoje. */
417
+ .sidebar-nav { display:flex; flex-direction:column; gap:var(--space-1); }
418
+ .sidebar-list { display:flex; flex-direction:column; gap:var(--space-1); margin:0; padding:0; list-style:none; }
419
+ /* `width:100%` + `font:inherit` + `border:0`: o item também é <button> quando não navega, e
420
+ sem isto o botão vem com a caixa do navegador e quebra a fila. */
421
+ .sidebar-item { display:flex; align-items:center; gap:var(--space-2); width:100%; min-height:var(--control-h-md); padding:var(--space-1) var(--space-3); border:0; border-radius:var(--radius-control); background:transparent; color:var(--sidebar-foreground); font:inherit; font-size:var(--text-sm); text-align:start; text-decoration:none; cursor:pointer; }
422
+ .sidebar-item .icon { flex:none; opacity:.8; }
423
+ /* min-width:0 é o que deixa o ellipsis funcionar dentro do flex; sem ele o nome longo empurra
424
+ o acessório para fora da caixa em vez de cortar. */
425
+ .sidebar-label { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
426
+ @media (hover:hover) and (pointer:fine) { .sidebar-item:hover { background:var(--secondary); } }
427
+ .sidebar-item[aria-current] { background:var(--secondary); color:var(--foreground); font-weight:var(--weight-medium); }
428
+ .sidebar-item[aria-current] .icon { opacity:1; }
429
+ .sidebar-group-label { margin:var(--space-3) var(--space-2) var(--space-1); color:var(--subtle-foreground); font-size:var(--text-xs); font-weight:var(--weight-medium); letter-spacing:var(--tracking-widest); text-transform:uppercase; }
430
+ .sidebar-sub { padding-inline-start:var(--space-5); }
431
+ /* RECOLHIDA — a lateral vira trilho de ícones. O rótulo NÃO some do DOM: o React troca a
432
+ classe dele por `.sr-only`, então quem usa leitor de tela continua ouvindo o nome. Ícone
433
+ sozinho não nomeia nada, e essa é a falha clássica do padrão de trilho. */
434
+ .sidebar-collapsed { padding-inline:var(--space-2); }
435
+ .sidebar-collapsed .sidebar-item { justify-content:center; padding-inline:var(--space-2); }
436
+ .sidebar-collapsed .sidebar-sub { padding-inline-start:0; }
437
+ /* A ÚNICA regra que toca `.app-shell`, e está aqui porque a coluna do grid é do shell: a
438
+ lateral não consegue encolher a própria coluna.
439
+ O `>` NÃO É ENFEITE, e quem achou isso foi o navegador em 06/08/2026. Escrita como
440
+ `:has(.sidebar-collapsed)`, a regra casa com QUALQUER descendente — e a página do catálogo
441
+ que mostra uma lateral recolhida como EXEMPLO, dentro do <main>, recolhia a lateral de
442
+ verdade do documento inteiro. Medido: a coluna do shell saía 88px numa página que não pediu
443
+ nada. Gate nenhum via, porque `sidebar.html` não tem baseline de pixel. */
444
+ .app-shell:has(> .sidebar-collapsed) { grid-template-columns:var(--sidebar-rail) minmax(0,1fr); }
445
+
446
+ /* ── Visibilidade responsiva o mecanismo PADRÃO de colapsar uma barra ─────────────
447
+ Regra do projeto (enraizada): todo layout horizontal que aperta esconde o secundário
448
+ por breakpoint da escala, nunca rola de lado. Estas 4 classes são esse mecanismo; o
449
+ gate (check 4b) trava os breakpoints. Mobile-first: `below` some acima do ponto. */
450
+ @media (max-width:1023px) { .hide-below-lg { display:none !important; } }
451
+ @media (max-width:767px) { .hide-below-md { display:none !important; } }
452
+ .brand { min-height:var(--topbar-height); display:flex; align-items:center; gap:12px; padding:2px 4px 16px; margin:0 0 16px; border:0; border-bottom:1px solid var(--sidebar-border); border-radius:0; background:transparent; box-shadow:none; }
453
+ .brand strong { display:block; letter-spacing:var(--tracking-tight); } .brand small { display:block; color:var(--muted-foreground); font-size:11px; }
454
+ /* minmax(0,1fr): a trilha `auto` do grid crescia até o nome mais longo e os itens
455
+ VAZAVAM pela direita da caixa (colavam na borda de um lado e sobrava do outro). */
456
+ .doc-nav { padding:0 0 8px; display:grid; grid-template-columns:minmax(0,1fr); gap:var(--space-1); }
457
+ /* .nav-group-label é o rótulo de categoria SEM ser título de documento: mesma aparência do
458
+ h4 histórico, mas o grupo se nomeia por aria-labelledby e não entra na lista de títulos
459
+ do leitor de tela (senão a lateral vem antes do h1 da página). É o jeito preferido. */
460
+ .doc-nav h4,.doc-nav .nav-group-label { margin:16px 8px 8px; color:var(--subtle-foreground); font-size:11px; letter-spacing:var(--tracking-widest); text-transform:uppercase; }
461
+ .doc-nav .nav-group { display:grid; gap:var(--space-1); }
462
+ .doc-nav a { display:flex; align-items:center; gap:10px; min-height:40px; margin:0; padding:9px 12px; border:1px solid transparent; border-radius:var(--radius-lg); background:transparent; color:var(--muted-foreground); text-decoration:none; box-shadow:none; }
463
+ .doc-nav a:hover { color:var(--sidebar-accent-foreground); background:var(--sidebar-accent); border-color:var(--sidebar-border); }
464
+ .doc-nav a.active { border-color:var(--sidebar-border); }
465
+ /* o glifo da família fica na frente do rótulo e nunca encolhe o texto */
466
+ .doc-nav a .icon { flex:none; opacity:.8; }
467
+ .doc-nav a.active .icon { opacity:1; }
468
+ .eyebrow { display:inline-flex; align-items:center; gap:8px; color:var(--primary); font-size:12px; font-weight:var(--weight-bold); letter-spacing:var(--tracking-widest); text-transform:uppercase; }
469
+ /* No claro o amarelo puro como texto não alcança 4.5:1 (axe). Aprofundar com --foreground
470
+ mantém o matiz da marca e passa o AA; no escuro segue o amarelo cheio. */
471
+ [data-theme="light"] .eyebrow { color:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
472
+ .demo { min-width:0; padding:var(--card-pad); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); box-shadow:none; }
473
+
474
+ /* ========================================================================== */
475
+ /* 04. COMPONENTES PRIMITIVOS */
476
+ /* ========================================================================== */
477
+ .btn { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:transparent; display:inline-flex; align-items:center; justify-content:center; gap:8px; height:var(--control-h-md); padding:0 15px; border:1px solid var(--btn-border); border-radius:var(--radius-control); background:var(--btn-bg); color:var(--btn-fg); font-size:var(--text-sm); font-weight:var(--weight-medium); line-height:var(--leading-none); text-decoration:none; white-space:nowrap; cursor:pointer; transition:transform var(--duration-fast) var(--ease-standard),background var(--duration-fast),border-color var(--duration-fast),color var(--duration-fast),opacity var(--duration-fast); }
478
+ .btn:hover:not(:disabled) { transform:none; } .btn:active:not(:disabled) { transform:scale(.97); opacity:.9; }
479
+ .btn:disabled { opacity:.45; cursor:not-allowed; }
480
+ /* Cinco alturas, todas de token de densidade (autorizado pelo Victor em 24/07/2026). O raio
481
+ é pill em TODAS — o tamanho muda altura e respiro, nunca a forma. Em ponteiro grosso o
482
+ mínimo de 44px embaixo pega o xs e o sm, então alvo pequeno não vira alvo impossível. */
483
+ .btn-xs { height:var(--control-h-xs); padding:0 10px; gap:6px; font-size:var(--text-xs); border-radius:var(--radius-control); }
484
+ .btn-sm { height:var(--control-h-sm); padding:0 12px; font-size:var(--text-xs); border-radius:var(--radius-control); }
485
+ .btn-lg { height:var(--control-h-lg); padding:0 20px; font-size:var(--text-base); border-radius:var(--radius-control); }
486
+ .btn-xl { height:var(--control-h-xl); padding:0 26px; gap:10px; font-size:var(--text-base); border-radius:var(--radius-control); }
487
+ /* Largura total: para rodapé de formulário e tela estreita. a largura muda. */
488
+ .btn-block { width:100%; }
489
+ .btn-primary { --btn-bg:var(--primary); --btn-fg:var(--primary-foreground); --btn-border:transparent; }
490
+ .btn-primary:hover:not(:disabled) { --btn-bg:var(--primary-hover); --btn-border:transparent; }
491
+ .btn-secondary { --btn-bg:var(--secondary); --btn-fg:var(--secondary-foreground); --btn-border:transparent; }
492
+ .btn-secondary:hover:not(:disabled) { --btn-bg:var(--secondary-hover); --btn-border:transparent; }
493
+ .btn-outline { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:var(--border-strong); }
494
+ .btn-outline:hover:not(:disabled) { --btn-bg:var(--accent); }
495
+ .btn-ghost { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:transparent; }
496
+ .btn-ghost:hover:not(:disabled) { --btn-bg:var(--accent); --btn-fg:var(--accent-foreground); }
497
+ .btn-danger { --btn-bg:var(--destructive); --btn-fg:var(--destructive-foreground); --btn-border:transparent; }
498
+ .btn-danger:hover:not(:disabled) { --btn-bg:var(--danger-400); }
499
+ /* Destrutivo em OUTRAS hierarquias: apagar não é sempre a ação principal da tela. O tom é o
500
+ mesmo (--destructive); o que muda é o peso. No claro o vermelho puro como texto não chega
501
+ a 4.5:1, então aprofunda com --foreground mesmo tratamento do amarelo. */
502
+ .btn-danger-outline { --btn-bg:transparent; --btn-fg:var(--destructive); --btn-border:var(--destructive); }
503
+ .btn-danger-outline:hover:not(:disabled) { --btn-bg:color-mix(in oklab, var(--destructive) 14%, transparent); }
504
+ .btn-danger-ghost { --btn-bg:transparent; --btn-fg:var(--destructive); --btn-border:transparent; }
505
+ .btn-danger-ghost:hover:not(:disabled) { --btn-bg:color-mix(in oklab, var(--destructive) 14%, transparent); }
506
+ [data-theme="light"] .btn-danger-outline,[data-theme="light"] .btn-danger-ghost,[data-theme="light"] .btn-link-danger { --btn-fg:color-mix(in oklab, var(--destructive) 62%, var(--foreground)); }
507
+ /* LINK: aparência de texto, sem cápsula e sem altura de controle — o sublinhado é o que diz
508
+ que é clicável, então ele existe desde o repouso (revelar no hover esconde a affordance).
509
+ Três tons: neutro, marca e destrutivo. No claro a marca aprofunda, senão não passa o AA. */
510
+ /* `min-height:24px` = WCAG 2.2 SC 2.5.8. A exceção de "alvo em linha" NÃO cobre este caso:
511
+ medido no catálogo, o "Learn more" é ação isolada dentro de um flex, não link no meio de
512
+ prosa, e media 67×13px. O texto continua onde está a caixa cresce centrada nele. */
513
+ .btn-link { height:auto; min-height:24px; padding:0; gap:6px; border:0; border-radius:var(--radius-sm); --btn-bg:transparent; --btn-fg:var(--foreground); text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px; }
514
+ .btn-link:hover:not(:disabled) { text-decoration-thickness:2px; }
515
+ .btn-link-primary { --btn-fg:var(--primary); }
516
+ [data-theme="light"] .btn-link-primary { --btn-fg:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
517
+ .btn-link-danger { --btn-fg:var(--destructive); }
518
+ /* pressed entra na linguagem única de "selecionado" acima; só-ícone não leva o fio. */
519
+ /* Kbd — a tecla que dispara a ação, dentro ou fora do botão. Peça própria, não decoração. */
520
+ .kbd { display:inline-flex; align-items:center; justify-content:center; gap:2px; min-width:20px; height:20px; padding:0 6px; border:1px solid var(--border-strong); border-radius:var(--radius-sm); background:var(--secondary); color:var(--muted-foreground); font:500 11px/1 var(--font-code); }
521
+ /* dentro do botão a tecla herda a cor do rótulo, SEM opacidade: a 85% o contraste caía
522
+ para 3.67:1 (axe) e o atalho é informação, não enfeite. O fundo já o separa. */
523
+ /* Dentro do botão a tecla NÃO ganha fundo próprio: qualquer tinta escurece a cápsula e o
524
+ rótulo perde contraste (no primary claro caiu a 3.55:1). Sem fundo, o contraste da
525
+ tecla é o MESMO do rótulo — se o botão passa, ela passa. A borda de currentColor separa. */
526
+ .btn .kbd { background:transparent; border-color:color-mix(in oklab, currentColor 35%, transparent); color:inherit; }
527
+
528
+ /* nav: item de navegação. Normal = ghost silencioso; o ATIVO (aria-current) ganha texto
529
+ amarelo + traço embaixo SEM borda (pedido do Victor). aria-current, não uma variante
530
+ "ativa", porque ele também anuncia "página atual" pro leitor de tela. O traço é
531
+ ::after, pill, alinhado ao padding do texto; posição pelo eixo bloco (não espelha RTL). */
532
+ .btn-nav { --btn-bg:transparent; --btn-fg:var(--muted-foreground); --btn-border:transparent; }
533
+ .btn-nav:hover:not(:disabled) { --btn-fg:var(--foreground); }
534
+ .btn-icon { width:var(--control-h-md); padding:0; border-radius:var(--radius-md); }
535
+ .btn-icon.btn-xs { width:var(--control-h-xs); border-radius:var(--radius-sm); }
536
+ .btn-icon.btn-sm { width:var(--control-h-sm); border-radius:var(--radius-sm); } .btn-icon.btn-lg { width:var(--control-h-lg); border-radius:var(--radius-md); }
537
+ /* xs e xl também precisam de largura: sem isto a altura mudava e a largura ficava na do
538
+ md, e o alvo só-ícone deixava de ser quadrado (achado na onda Actions, 24/07). */
539
+ .btn-icon.btn-xl { width:var(--control-h-xl); border-radius:var(--radius-md); }
540
+ /* wrap, não estoura: os botões do grupo são pills com gap (não segmentos colados), então
541
+ quebrar linha em tela estreita preserva o visual. Único gap de eixo horizontal que a
542
+ auditoria da Etapa 3 achou sem proteção o resto rola (tabela) ou já quebra (toolbar). */
543
+ .btn-group { display:inline-flex; flex-wrap:wrap; align-items:center; gap:4px; } .btn-group .btn { border-radius:var(--radius-control); margin-inline-start:0; }
544
+ .btn-group .btn:first-child { margin-inline-start:0; border-radius:var(--radius-control); }
545
+ .btn-group .btn:last-child { border-radius:var(--radius-control); }
546
+ /* Toolbar: contêiner de controles; o roving tabindex vem do Base UI. */
547
+ .toolbar { display:flex; align-items:center; flex-wrap:wrap; gap:6px; padding:6px; border:1px solid var(--border); border-radius:var(--radius-control); background:var(--secondary); }
548
+ /* Vertical sai da pílula: --radius-control (999px) clampa em 50% da menor
549
+ dimensão e vira um oval num container alto. */
550
+ .toolbar[data-orientation="vertical"] { flex-direction:column; align-items:stretch; border-radius:var(--radius-lg); }
551
+ .toolbar-group { display:flex; align-items:center; gap:var(--space-1); }
552
+ .toolbar-sep { flex:none; align-self:stretch; inline-size:1px; min-block-size:20px; margin-inline:4px; margin-block:0; background:var(--border); border:0; }
553
+ .toolbar[data-orientation="vertical"] .toolbar-sep { align-self:auto; inline-size:auto; block-size:1px; min-block-size:0; margin-inline:0; margin-block:4px; }
554
+ /* Spinner: o glifo existia desde sempre e era usado por dentro do Button. Ganhou componente
555
+ e escala no Lote 1, e o eixo passou a ser o dos ÍCONES — mas o tamanho base continua o que
556
+ sempre foi: `--icon-sm` são os mesmos 16px de antes, então nenhum botão em nenhuma página
557
+ mudou de aparência. Cheguei a apontar o base para `--icon-md` (20px) e os baselines de pixel
558
+ acusaram na hora: teria trocado o tamanho do spinner dentro de todo botão sem ninguém pedir.
559
+ A casa do spinner é dentro de um controle, ao lado de texto; os degraus maiores existem para
560
+ quando ele é a única coisa na tela. */
561
+ .spinner { width:var(--icon-sm); height:var(--icon-sm); border:2px solid currentColor; border-right-color:transparent; border-radius:50%; animation:spin .7s linear infinite; }
562
+ .spinner-md { width:var(--icon-md); height:var(--icon-md); }
563
+ .spinner-lg { width:var(--icon-lg); height:var(--icon-lg); } @keyframes spin { to { transform:rotate(360deg); } }
564
+
565
+ .field { display:flex; flex-direction:column; gap:7px; }
566
+ .label { display:flex; justify-content:space-between; gap:12px; color:var(--foreground); font-size:var(--text-sm); font-weight:var(--weight-semibold); }
567
+ .hint { color:var(--muted-foreground); font-size:var(--text-xs); }
568
+ .input,.textarea,.select { width:100%; min-width:0; height:var(--control-h-md); padding:0 13px; color:var(--foreground); background:var(--field-bg); border:1px solid var(--border-strong); border-radius:var(--radius-control); transition:border-color var(--duration-fast),background-color var(--duration-fast),box-shadow var(--duration-fast); }
569
+ .input:hover,.textarea:hover,.select:hover { border-color:var(--border); }
570
+ .input::placeholder,.textarea::placeholder { color:var(--subtle-foreground); }
571
+ .input:disabled,.textarea:disabled,.select:disabled { opacity:.5; cursor:not-allowed; }
572
+ .input[aria-invalid="true"],.textarea[aria-invalid="true"] { border-color:var(--danger-400); }
573
+ .textarea { min-height:104px; padding:12px 14px; resize:vertical; border-radius:var(--radius-lg); }
574
+ /* chevron: cor = --muted-foreground de cada tema (dark #a1a1a1, light #505050); tokenizar na Fase 1 */
575
+ .select { appearance:none; padding-inline-end:38px; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23a1a1a1' stroke-width='1.6'/%3E%3C/svg%3E"); background-position:calc(100% - 12px) 50%; background-size:12px 8px; background-repeat:no-repeat; }
576
+ [data-theme="light"] .select { background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23505050' stroke-width='1.6'/%3E%3C/svg%3E"); }
577
+ /* Widgets nativos dos campos (calendário do type=date, dropdown do select, scrollbar do textarea) seguem o tema */
578
+ .input,.textarea,.select { color-scheme:dark; }
579
+ [data-theme="light"] .input,[data-theme="light"] .textarea,[data-theme="light"] .select { color-scheme:light; }
580
+ /* NumberField (Lote 1): menos · campo · mais numa peça só, que é onde as referências convergem.
581
+ O campo é `.input` a mesma altura, o mesmo raio, o mesmo foco de qualquer campo do sistema.
582
+ Largura em token para caber de 3 a 4 dígitos sem virar caixa de texto. */
583
+ .number-field { display:inline-flex; }
584
+ .number-field-group { display:inline-flex; align-items:center; gap:var(--space-1); }
585
+ .number-field-input { width:var(--space-16); text-align:center; }
586
+ /* OTPField (Lote 1): um quadrado por dígito. O lado é a altura de controle, então a fila mede o
587
+ mesmo que qualquer campo ao lado dela; o padding zera porque o dígito é centralizado. */
588
+ .otp-field { display:inline-flex; align-items:center; gap:var(--space-2); }
589
+ .otp-slot { width:var(--control-h-md); padding:0; text-align:center; font-family:var(--font-code); }
590
+ /* HoverCard (Lote 1) reusa `.popover`; limita a largura, porque prévia é conteúdo, não rótulo. */
591
+ .hover-card { max-width:min(20rem,calc(100vw - var(--space-8))); }
592
+ .input-wrap { position:relative; } .input-wrap>.icon { position:absolute; inset-inline-start:12px; top:50%; transform:translateY(-50%); color:var(--subtle-foreground); pointer-events:none; } .input-wrap .input { padding-inline-start:40px; }
593
+ .field-error { color:var(--danger-400); font-size:var(--text-xs); }
594
+ .checkbox,.radio { display:inline-flex; align-items:flex-start; gap:9px; color:var(--foreground); cursor:pointer; }
595
+ .checkbox input,.radio input { position:absolute; opacity:0; pointer-events:none; }
596
+ .control-mark { width:18px; height:18px; margin-top:1px; display:grid; place-items:center; border:1px solid var(--border-strong); background:var(--field-bg); }
597
+ .checkbox .control-mark { border-radius:5px; } .radio .control-mark { border-radius:50%; }
598
+ .checkbox input:checked + .control-mark { background:var(--control-selected); border-color:var(--control-selected); color:var(--control-selected-foreground); }
599
+ .checkbox input:focus-visible + .control-mark,.radio input:focus-visible + .control-mark,.switch input:focus-visible + .switch-track { outline:2px solid var(--focus-strong); outline-offset:2px; }
600
+ .checkbox input:checked + .control-mark:after { content:""; width:8px; height:4px; border-left:2px solid currentColor; border-bottom:2px solid currentColor; transform:rotate(-45deg) translateY(-1px); }
601
+ .radio input:checked + .control-mark:after { content:""; width:8px; height:8px; border-radius:50%; background:var(--control-selected); }
602
+ .switch { display:inline-flex; align-items:center; gap:10px; cursor:pointer; } .switch input { position:absolute; opacity:0; }
603
+ .switch-track { position:relative; width:42px; height:24px; border-radius:999px; background:var(--muted); border:1px solid var(--border); transition:background var(--duration-base) var(--ease-standard),border-color var(--duration-base) var(--ease-standard); }
604
+ .switch-track:after { content:""; position:absolute; inset-inline-start:3px; top:3px; width:16px; height:16px; border-radius:50%; background:var(--muted-foreground); transition:transform var(--duration-base) var(--ease-standard),background var(--duration-base) var(--ease-standard); }
605
+ .switch input:checked + .switch-track { background:var(--control-selected); border-color:var(--control-selected); } .switch input:checked + .switch-track:after { transform:translateX(18px); background:var(--control-selected-foreground); }
606
+ .range { width:100%; accent-color:var(--primary); }
607
+ /* A CÁPSULA é que tem de medir igual a um botão/campo ao lado — não o botão de dentro.
608
+ Antes: `height:30px` no filho, cru. Em comfortable dava 30+3+3=36 = --control-h-md por
609
+ coincidência; em compact a cápsula ficava 4px alta demais e em spacious 4px baixa demais,
610
+ porque a densidade não alcançava a regra (achado A1, 26/07/2026). Com a altura na cápsula
611
+ os filhos esticam sozinhos (align-items é stretch por padrão) e a conta fecha nas TRÊS. */
612
+ .segmented { display:inline-flex; gap:3px; padding:3px; min-height:var(--control-h-md); border-radius:var(--radius-control); background:var(--muted); border:1px solid transparent; box-shadow:none; }
613
+ .segmented button { padding:0 14px; border:0; border-radius:var(--radius-control); color:var(--muted-foreground); background:transparent; cursor:pointer; }
614
+ .segmented button.active { color:var(--foreground); background:var(--secondary); box-shadow:none; border:0; }
615
+ /* Toggle (Lote 1): pressionado usa o MESMO par de tokens do segmento aceso, logo acima. Um
616
+ toggle solto e um segmento selecionado dizem a mesma coisa se parecessem diferentes, o
617
+ sistema teria dois idiomas para "isto está ligado". O estado vem do motor (`data-pressed`),
618
+ não de uma classe que alguém precisa lembrar de pôr. */
619
+ .toggle[data-pressed] { --btn-bg:var(--secondary); --btn-fg:var(--primary); }
620
+ /* ── SELECIONADO: uma linguagem ────────────────────────────────────────────────────
621
+ Item de nav ativo, aba ativa, item de lista lateral e botão alternado ligado mostram o
622
+ MESMO sinal: cápsula elevada + rótulo na cor da marca + fio amarelo encostado na borda de
623
+ baixo. Um usuário aprende uma vez e reconhece em todo lugar (pedido do Victor, 24/07).
624
+ O fio é 2px (fração de pixel borra), inset 15px, e o amarelo entra a 75% — cheio ele
625
+ grita mais que o próprio rótulo. No tema CLARO o amarelo puro como texto dá 1.6:1, longe
626
+ dos 4.5:1 do AA, então o rótulo aprofunda com --foreground e segue âmbar. */
627
+ .is-selected,.btn-nav[aria-current]:not([aria-current="false"]),.btn[aria-pressed="true"],
628
+ .segmented button.active,.doc-nav a.active { --btn-bg:var(--secondary); --btn-fg:var(--primary); position:relative; background:var(--secondary); color:var(--primary); }
629
+ .is-selected::after,.btn-nav[aria-current]:not([aria-current="false"])::after,
630
+ .btn[aria-pressed="true"]:not(.btn-icon)::after,.segmented button.active::after,
631
+ .doc-nav a.active::after { content:""; position:absolute; inset-inline:15px; bottom:0; height:2px; border-radius:var(--radius-full); background:color-mix(in oklab, var(--primary) 75%, transparent); }
632
+ [data-theme="light"] .is-selected,[data-theme="light"] .btn-nav[aria-current]:not([aria-current="false"]),
633
+ [data-theme="light"] .btn[aria-pressed="true"],[data-theme="light"] .segmented button.active,
634
+ [data-theme="light"] .doc-nav a.active { --btn-fg:color-mix(in oklab, var(--primary) 45%, var(--foreground)); color:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
635
+
636
+ /* `line-height:1` não é enfeite: sem ele o .badge herdava a entrelinha do pai e a MESMA
637
+ classe media 26px dentro do .brand (lh 18px) e 22px dentro de um .btn-nav (lh 12px) — na
638
+ mesma barra (achado A2, 26/07/2026). Geometria de componente não se decide pelo contexto.
639
+ O .chip do catálogo tinha aprendido isso; o .badge não. */
640
+ .badge { display:inline-flex; align-items:center; gap:6px; min-height:var(--space-6); padding:3px 9px; border:1px solid var(--border); border-radius:var(--radius-control); background:var(--secondary); color:var(--secondary-foreground); font-size:var(--text-xs); font-weight:var(--weight-medium); line-height:var(--leading-none); white-space:nowrap; }
641
+ /* O glifo do badge tem tamanho PRÓPRIO, senão o `.icon` padrão (20px) estoura os 24px e o
642
+ badge com ícone fica mais alto que o badge de texto na mesma fila. 16px + 3+3 de padding
643
+ + 1+1 de borda = 24px exatos: com ícone ou sem, mede o mesmo. */
644
+ .badge .icon { width:var(--icon-sm); height:var(--icon-sm); }
645
+ .badge-primary { color:var(--primary-emphasis); border-color:var(--primary-outline); background:color-mix(in srgb,var(--primary) 10%,transparent); }
646
+
647
+ .badge-success { color:var(--success-400); background:var(--success-bg); border-color:color-mix(in srgb,var(--success-400) 35%,var(--border)); }
648
+ .badge-warning { color:var(--warning-400); background:var(--warning-bg); border-color:color-mix(in srgb,var(--warning-400) 35%,var(--border)); }
649
+ .badge-danger { color:var(--danger-400); background:var(--danger-bg); border-color:color-mix(in srgb,var(--danger-400) 35%,var(--border)); }
650
+ .badge-info,.badge-running { color:var(--info-400); background:var(--info-bg); border-color:color-mix(in srgb,var(--info-400) 35%,var(--border)); }
651
+ .status-dot { width:8px; height:8px; border-radius:50%; background:currentColor; box-shadow:0 0 0 3px color-mix(in srgb,currentColor 14%,transparent); }
652
+ /* Status: condição operacional (DIRECTION §3.6) NÃO é o pill do Badge; é ponto + rótulo
653
+ solto na linha. A variante pinta o .status (o ponto herda por currentColor) e o rótulo
654
+ volta pra --foreground, senão a frase inteira fica colorida e ilegível no claro. Os
655
+ nomes operacionais e os semânticos apontam pro mesmo par de tokens de propósito. */
656
+ .status { display:inline-flex; align-items:center; gap:var(--space-2); color:var(--muted-foreground); font-size:var(--text-sm); }
657
+ .status-label { color:var(--foreground); }
658
+ .status-online,.status-success { color:var(--success-400); }
659
+ .status-away,.status-warning { color:var(--warning-400); }
660
+ .status-busy,.status-danger { color:var(--danger-400); }
661
+ .status-running,.status-info { color:var(--info-400); }
662
+ /* offline é o único que NÃO é "mais uma cor": --subtle-foreground e --muted-foreground
663
+ colapsam no MESMO valor no tema claro, então offline sairia idêntico ao neutral. Ponto
664
+ VAZADO resolve nos dois temas e ainda diz a coisa certa (ausência, não outro estado).
665
+ Escopado em .status-offline: o .status-dot dentro do Badge (Chat) segue preenchido. */
666
+ .status-offline { color:var(--subtle-foreground); }
667
+ .status-offline .status-dot { background:transparent; box-shadow:inset 0 0 0 2px currentColor,0 0 0 3px color-mix(in srgb,currentColor 14%,transparent); }
668
+
669
+ .card { min-width:0; padding:var(--card-pad); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); box-shadow:none; }
670
+ .card-raised { box-shadow:none; } .card-inset { background:var(--secondary); }
671
+ .card-interactive { cursor:pointer; transition:transform var(--duration-fast),border-color var(--duration-fast),background var(--duration-fast); }
672
+ .card-interactive:hover { transform:translateY(-2px); border-color:var(--border-strong); background:var(--surface-2); }
673
+ .card-selected { border-color:var(--primary-outline); box-shadow:inset 0 0 0 1px var(--primary-outline); }
674
+ .card-danger { border-color:color-mix(in srgb,var(--danger-400) 50%,var(--border)); background:var(--danger-bg); }
675
+ .card-header { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:16px; }
676
+ .card-title { margin:0; font-size:var(--text-base); } .card-subtitle { margin:3px 0 0; color:var(--muted-foreground); font-size:var(--text-sm); }
677
+
678
+ .alert { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:start; gap:12px; padding:14px 16px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--secondary); box-shadow:none; }
679
+ /* Banner: aviso de largura de página. Base ANTES das variantes mesma
680
+ especificidade, então a última regra vence e a base apagaria as cores. */
681
+ .banner { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:12px; padding:14px 18px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--secondary); }
682
+ .alert-info,.banner-info { border-color:color-mix(in srgb,var(--info-400) 38%,var(--border)); background:var(--info-bg); }
683
+ .alert-success,.banner-success { border-color:color-mix(in srgb,var(--success-400) 38%,var(--border)); background:var(--success-bg); }
684
+ .alert-warning,.banner-warning { border-color:color-mix(in srgb,var(--warning-400) 38%,var(--border)); background:var(--warning-bg); }
685
+ .alert-danger,.banner-danger { border-color:color-mix(in srgb,var(--danger-400) 38%,var(--border)); background:var(--danger-bg); }
686
+ .alert h4 { margin:0 0 2px; font-size:var(--text-sm); } .alert p { margin:0; color:var(--muted-foreground); font-size:var(--text-sm); }
687
+ .banner strong { font-size:var(--text-sm); } .banner p { margin:0; color:var(--muted-foreground); font-size:var(--text-sm); }
688
+ @media (max-width:640px) { .banner { grid-template-columns:auto minmax(0,1fr); } .banner>:last-child { grid-column:2; justify-self:start; } }
689
+ .progress { height:8px; overflow:hidden; border-radius:999px; background:var(--surface-3); }
690
+ .progress>span { display:block; height:100%; border-radius:inherit; background:var(--primary); }
691
+ .skeleton { position:relative; overflow:hidden; border-radius:7px; background:var(--surface-3); }
692
+ .skeleton { animation:skeleton-pulse 1.35s ease-in-out infinite; } @keyframes skeleton-pulse { 50% { opacity:.55; } }
693
+
694
+ /* Mesma regra da .segmented: a cápsula carrega a altura da densidade, o filho estica. */
695
+ .tabs { display:inline-flex; gap:3px; padding:3px; min-height:var(--control-h-md); border:0; border-radius:var(--radius-control); background:var(--muted); }
696
+ .tab { position:relative; padding:0 14px; border:0; border-radius:var(--radius-control); background:transparent; color:var(--muted-foreground); cursor:pointer; }
697
+ .tab.active,.tab[data-active] { color:var(--foreground); background:var(--secondary); } .tab.active:after,.tab[data-active]:after { display:none; }
698
+ .breadcrumb { display:flex; align-items:center; flex-wrap:wrap; gap:7px; color:var(--muted-foreground); font-size:var(--text-sm); }
699
+ .breadcrumb a { color:inherit; text-decoration:none; } .breadcrumb a:hover { color:var(--foreground); }
700
+ .pagination { display:flex; gap:6px; align-items:center; }
701
+ /* `:not(.btn)` porque isto é o quadrado de NÚMERO de página. Sem o :not, o seletor de
702
+ elemento vencia o `.btn-sm` do Anterior/Próxima por especificidade (0,1,1 contra 0,1,0)
703
+ e forçava 34px neles — era a terceira altura de .btn-sm que a auditoria mediu (B3). */
704
+ .pagination button:not(.btn) { width:var(--control-h-md); height:var(--control-h-md); border:1px solid var(--border); border-radius:var(--radius-control); background:transparent; color:var(--muted-foreground); cursor:pointer; }
705
+ .pagination button.active { color:var(--primary-foreground); background:var(--primary); border-color:var(--primary); }
706
+ /* Stepper (Lote 2): o container NUNCA existiu no core — só no docs.css, e era
707
+ `repeat(4,1fr)`, com o número de etapas cravado. `grid-auto-flow:column` distribui N etapas
708
+ iguais sem saber quanto é N, e colunas iguais são o que faz a linha conectora (`.step:before`,
709
+ ancorada em -50%) cair no lugar certo. */
710
+ .stepper { display:grid; grid-auto-flow:column; grid-auto-columns:1fr; }
711
+ .step { position:relative; text-align:center; color:var(--subtle-foreground); }
712
+ /* O ponto também morava só no docs.css, como o container. Os estados dele (`.step.done`,
713
+ `.step.active`) estavam no core e miravam uma regra que não existia aqui — meia peça. */
714
+ .step-dot { position:relative; z-index:1; width:32px; height:32px; margin:0 auto var(--space-2); display:grid; place-items:center; border-radius:50%; border:1px solid var(--border-strong); background:var(--surface-1); }
715
+ /* Erro é o estado que faltava: uma etapa que reprovou na validação não tinha como se mostrar.
716
+ Cor semântica, não a primária reprovar não é progredir. */
717
+ .step-error { color:var(--danger-400); } .step-error .step-dot { color:var(--danger-400); border-color:var(--danger-400); }
718
+ .step-optional { display:block; color:var(--muted-foreground); font-size:var(--text-xs); }
719
+ /* Etapa clicável (fluxo não-linear): o alvo é um botão de verdade, com o foco do sistema. */
720
+ .step-trigger { display:contents; border:0; padding:0; background:none; color:inherit; font:inherit; cursor:pointer; }
721
+ .step:before { content:""; position:absolute; top:15px; inset-inline-start:-50%; width:100%; height:2px; background:var(--border); } .step:first-child:before { display:none; }
722
+ .step.done,.step.active { color:var(--foreground); } .step.done:before,.step.active:before { background:var(--primary); } .step.done .step-dot { color:var(--primary-foreground); background:var(--primary); border-color:var(--primary); } .step.active .step-dot { border-color:var(--primary); box-shadow:0 0 0 4px color-mix(in srgb,var(--primary) 14%,transparent); }
723
+
724
+ /* TABELA DE DADOS — e só ela. Estas cinco regras eram de ELEMENTO (`table`, `th,td`, `th`,
725
+ `tr:last-child td`, `tbody tr:hover`) e valiam para toda <table> do documento, inclusive a de
726
+ um consumidor. Nenhum gate as via: os checks 15 e 18 comparam CLASSE, e seletor de elemento
727
+ não tem nenhuma. O Lote 4 pagou a conta — a grade do calendário nasceu com `min-width:720px`
728
+ e o cabeçalho de dias herdou a faixa cinza, a caixa alta e o espaçamento de letra de um
729
+ cabeçalho de tabela de dados. Medido no navegador em 01 e 02/08/2026.
730
+ Escopar é seguro e foi medido antes: `Table` embrulha em `.table-region` e emite `.table` na
731
+ tabela; `DataGrid` embrulha em `.table-wrap`; e as 3 tabelas da página legada estão todas
732
+ dentro de `.table-wrap`. Nenhuma <table> do repositório fica descoberta o gate de pixel é
733
+ quem prova, e ele tem de acusar ZERO mudança nas superfícies de tabela. */
734
+ .table-wrap { overflow:auto; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
735
+ .table,.table-wrap table { width:100%; border-collapse:collapse; min-width:720px; }
736
+ .table th,.table td,.table-wrap th,.table-wrap td { height:var(--row-h); padding:0 14px; text-align:start; border-bottom:1px solid var(--border); }
737
+ .table th,.table-wrap th { color:var(--subtle-foreground); background:var(--surface-2); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; }
738
+ .table tr:last-child td,.table-wrap tr:last-child td { border-bottom:0; }
739
+ .table tbody tr:hover,.table-wrap tbody tr:hover { background:var(--surface-2); }
740
+ /* Avatar (Lote 2): `overflow:hidden` e o `object-fit` da imagem são a correção do defeito que
741
+ a medição achou foto que não é quadrada vazava inteira da caixa redonda, porque
742
+ `border-radius` não recorta filho sozinho. Foto de perfil raramente vem quadrada, então isto
743
+ quebrava no primeiro uso real. */
744
+ /* B5 do PLANO-1.0: os 36px viraram --control-h-md, que É 2.25rem = os mesmos 36px em
745
+ `comfortable`. Nada muda de aparência aqui; o que muda é que agora a DENSIDADE alcança o
746
+ avatar, e a escala é a mesma dos controles — avatar e botão na mesma linha ficam da mesma
747
+ altura por construção, em vez de por coincidência. */
748
+ .avatar { width:var(--control-h-md); height:var(--control-h-md); display:grid; place-items:center; overflow:hidden; border-radius:50%; background:var(--surface-3); color:var(--foreground); border:1px solid var(--border-strong); font-weight:var(--weight-bold); }
749
+ .avatar-sm { width:var(--control-h-sm); height:var(--control-h-sm); font-size:var(--text-xs); }
750
+ .avatar-lg { width:var(--control-h-lg); height:var(--control-h-lg); }
751
+ .avatar img { width:100%; height:100%; object-fit:cover; }
752
+ /* O container do grupo NUNCA teve regra: a sobreposição abaixo existia e os avatares empilhavam
753
+ na vertical porque o pai saía `display:block`. Mesma falha do `.stepper` e do `.command`. */
754
+ .avatar-group { display:inline-flex; align-items:center; }
755
+ .avatar-group .avatar { margin-inline-start:-9px; border:2px solid var(--surface-1); } .avatar-group .avatar:first-child { margin-inline-start:0; }
756
+ /* O "+N" é um avatar como os outros — mesma caixa, mesma borda —, só que menor por dentro e sem
757
+ peso de nome próprio: ele conta, não identifica. */
758
+ .avatar-count { background:var(--secondary); color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-medium); }
759
+ .kpi { display:flex; flex-direction:column; gap:5px; }
760
+ /* DataList: chave/valor em duas colunas. O termo é secundário — mesmo idioma do cabeçalho de
761
+ tabela. Sem a regra (achado A13) o <dl> saía cru: dt e dd empilhados, com o recuo de 40px
762
+ que o navegador ao <dd>. */
763
+ .data-list { display:grid; grid-template-columns:max-content minmax(0,1fr); gap:var(--space-2) var(--space-6); margin:0; }
764
+ .data-list dt { color:var(--muted-foreground); }
765
+ .data-list dd { min-width:0; margin:0; }
766
+ /* Timeline: o trilho existia e o PONTO não — `.timeline-dot` media 0×0 (achado A13), então a
767
+ linha do tempo saía com a régua e sem as marcas, e o <ol> ainda mostrava a numeração do
768
+ navegador. Agora os dois derivam do MESMO token: o trilho fica a meio diâmetro do ponto, e
769
+ alinham por construção em vez de por número copiado. Saem junto os 24px de recuo e os 7px
770
+ do trilho dois px crus a menos na catraca do check 12. */
771
+ .timeline { position:relative; display:flex; flex-direction:column; gap:var(--space-5); margin:0; padding:0; list-style:none; }
772
+ .timeline:before { content:""; position:absolute; inset-inline-start:calc(var(--space-3) / 2); inset-block:var(--space-2); width:1px; background:var(--border); }
773
+ .timeline > li { display:flex; align-items:flex-start; gap:var(--space-3); }
774
+ /* A coluna de conteúdo é uma PILHA, e não por estética: sem isso o item empilhava quando
775
+ tinha descrição (o <p> é bloco) e colava título e horário na mesma linha quando não tinha —
776
+ "Shipped14:20". Geometria de componente não depende do conteúdo (achados A2/A3). */
777
+ .timeline > li > div { display:flex; flex-direction:column; gap:var(--space-05); min-width:0; }
778
+ .timeline > li > div > p { margin:0; }
779
+ .timeline-dot { flex:none; width:var(--space-3); height:var(--space-3); border-radius:var(--radius-full); margin-block-start:var(--space-1); background:var(--primary); box-shadow:0 0 0 var(--space-05) var(--surface-1); }
780
+ /* Accordion: anatomia medida no Carbon em 30/07/2026 (REFERENCES.md) — cabeçalho na altura de
781
+ controle, divisória de 1px por item, corpo com respiro pequeno em cima e grande embaixo.
782
+ O marcador é o NATIVO do <summary>: gira sozinho e custa zero código, e é por isso que o
783
+ summary continua `display:list-item` pôr flex nele apagaria o marcador. */
784
+ .accordion { display:flex; flex-direction:column; border-block:1px solid var(--border); }
785
+ .accordion > details + details { border-top:1px solid var(--border); }
786
+ .accordion > details > summary { min-height:var(--control-h-md); padding-block:var(--space-2); padding-inline:var(--space-5) var(--space-2); color:var(--foreground); font-weight:var(--weight-medium); line-height:var(--leading-normal); cursor:pointer; }
787
+ .accordion > details > summary:hover { background:var(--surface-hover); }
788
+ .accordion > details > summary::marker { color:var(--muted-foreground); }
789
+ /* Recuo INLINE, não `padding-left`: o marcador do <summary> troca de lado em RTL e o corpo
790
+ tem de acompanhar. Foi o gate de RTL que cobrou — ele mede o eixo inline do core. */
791
+ .accordion-content { padding-block:0 var(--space-6); padding-inline:var(--space-5) var(--space-2); color:var(--muted-foreground); }
792
+ /* Mockup do kit de origem (apps/docs) — não é o Accordion do sistema, que é <details> nativo. */
793
+
794
+ /* QRCode: superfície flutuante Aurea, mas o miolo é de alto contraste FIXO — um QR
795
+ não segue o tema (dark inverteria e nem todo leitor decodifica invertido). Override
796
+ consciente por --qr-module/--qr-quiet. Módulos suaves (rx no SVG) sem perder escaneabilidade. */
797
+ /* B6 do PLANO-1.0: o tamanho saiu dos atributos width/height do SVG e virou --qr-size, no
798
+ mesmo idioma dos dois botões de ajuste que este componente já tinha. `md` são os 10rem =
799
+ 160px de antes (com box-sizing:border-box o quiet zone continua por dentro da medida).
800
+ Três degraus e não um número livre: o número livre era o que obrigava pixel cru no
801
+ consumidor. Quem precisar de um QR de cartaz sobrescreve --qr-size, que é o mesmo caminho
802
+ documentado para trocar a cor dos módulos. */
803
+ .qrcode { --qr-module:#0b0b0c; --qr-quiet:#fff; --qr-size:10rem; box-sizing:border-box; width:var(--qr-size); height:var(--qr-size); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--qr-quiet); }
804
+ .qrcode-sm { --qr-size:7.5rem; }
805
+ .qrcode-lg { --qr-size:15rem; }
806
+ .qrcode .qr-mod { fill:var(--qr-module); }
807
+ .qrcode .qr-eye-gap { fill:var(--qr-quiet); }
808
+
809
+ /* `overflow:auto` faz do <pre> uma REGIÃO ROLÁVEL, e região rolável sem nada focável dentro
810
+ é inalcançável por teclado (axe scrollable-region-focusable). Quem escreve esta marcação à
811
+ mão precisa de `tabindex="0"` no <pre> — o componente React já o emite. */
812
+ .code-block { overflow:auto; padding:16px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--surface-inset); color:var(--muted-foreground); font:12px/1.7 var(--font-code); }
813
+ /* Bloco de código COM copiar: a moldura não rola, o <pre> rola por dentro e o gatilho fica
814
+ parado no canto. O comportamento é do core (aurea.js, [data-aurea-copy]); aqui a pele.
815
+ O check em amarelo é confirmação instantânea dura 1,5s e volta ao ícone de copiar. */
816
+ /* checkbox com rótulo fora da tela: sem o texto, o alvo é a marca nada de gap sobrando. */
817
+ .checkbox-bare { gap:0; }
818
+ .code-block-wrap { position:relative; display:flex; min-width:0; }
819
+ .code-block-wrap > .code-block { flex:1; min-width:0; margin:0; }
820
+ .copy-code { position:absolute; top:var(--space-3); inset-inline-end:var(--space-3); z-index:1; }
821
+ .copy-code .c-done { display:none; }
822
+ .copy-code.is-done .c-copy { display:none; } .copy-code.is-done .c-done { display:block; color:var(--primary); }
823
+ /* "On this page": índice da própria página, fixo na lateral. Navegação, não decoração —
824
+ por isso é <nav> com rótulo, e o item atual se marca com aria-current. */
825
+ .toc { position:sticky; top:var(--space-4); align-self:start; display:grid; gap:var(--space-1); max-height:calc(100vh - var(--space-8)); overflow:auto; scrollbar-width:none; padding:var(--space-2) 0; }
826
+ .toc::-webkit-scrollbar { width:0; }
827
+ .toc-label { margin:0 0 var(--space-1); padding-inline:var(--space-3); color:var(--subtle-foreground); font-size:11px; letter-spacing:var(--tracking-widest); text-transform:uppercase; }
828
+ .toc a { padding:var(--space-1) var(--space-3); border-inline-start:2px solid var(--border); color:var(--muted-foreground); font-size:var(--text-sm); text-decoration:none; }
829
+ .toc a:hover { color:var(--foreground); }
830
+ .toc a[aria-current] { border-inline-start-color:var(--primary); color:var(--foreground); }
831
+ .toc a.toc-sub { padding-inline-start:var(--space-6); }
832
+ /* `--log-h` é a QUINTA válvula de altura do core, no idioma de `--chart-h` (I4), `--qr-size` (B6),
833
+ `--datagrid-max-h` (F6) e `--media-h` (I7). Default 240px o de antes —, então nada muda para
834
+ quem não passa nada. A causa é medida em 11/08/2026: a altura era FIXA, e por isso três prévias
835
+ do catálogo gastavam 240px para mostrar três linhas de log e rolavam dentro da caixa de
836
+ demonstração, contra a ADR-0002. Altura é decisão da página, e não vira prop (check 23). */
837
+ .log-stream { height:var(--log-h,240px); overflow:auto; padding:14px; border:1px solid var(--border); border-radius:var(--radius-md); background:var(--surface-inset); font:12px/1.65 var(--font-code); }
838
+ .log-line { display:grid; grid-template-columns:86px 72px minmax(0,1fr); gap:12px; padding:3px 0; } .log-time { color:var(--subtle-foreground); } .log-level { color:var(--primary); } .log-level.warn { color:var(--warning-400); } .log-level.error { color:var(--danger-400); }
839
+ /* O nível pintado a partir da classe que o pacote React emite no CONTAINER (`log-error`).
840
+ As duas formas convivem de propósito: `.log-level.warn` serve a apps/docs/index.html,
841
+ escrito à mão, e sai junto com ele na Parte D. Medido no item E13: sem estas três, a
842
+ prop `level` do LogStream não pintava nada — pele que existe e não faz nada. */
843
+ .log-info .log-level { color:var(--info-400); }
844
+ .log-warn .log-level { color:var(--warning-400); }
845
+ .log-error .log-level { color:var(--danger-400); }
846
+
847
+ /* ========================================================================== */
848
+ /* 05. GRÁFICOS E VISUALIZAÇÃO */
849
+ /* ========================================================================== */
850
+ /* I4 do PLANO-1.0: a altura vira válvula de escape, no MESMO idioma do `--qr-size` e do
851
+ `--datagrid-max-h` quem sabe quanta tela tem é quem hospeda, e altura não vira prop
852
+ numérica (check 23). O default é os mesmos 220px de antes, então nada muda para quem já
853
+ usa: medido, zero pixel nas capturas. O que ele destrava é a composição o `I4
854
+ AnalyticsWorkbench` põe gráfico E tabela na mesma caixa de altura fixa (ADR-0002), e com
855
+ 220 fixos as duas peças sozinhas já passavam do vão do painel em 95px. O fallback é
856
+ deliberado e é o que o check 29 exige de quem não declara o token. */
857
+ .chart { width:100%; height:var(--chart-h,220px); } .chart-bar.alt { fill:var(--chart-1); } .health-cell.warn { background:var(--warning-500); } .health-cell.bad { background:var(--danger-500); } .health-cell.off { background:var(--surface-3); }
858
+ /* Chart (Lote 3) a pele do desenho que o Recharts emite. Os seletores são de ELEMENTO, nunca
859
+ `.recharts-*`: nome de classe de terceiro dentro do core reprovaria o check 15 (o core só
860
+ declara classe que a Aurea produz), e é a mesma razão pela qual a pele do CodeEditor vem pelo
861
+ EditorView.theme do CodeMirror e não daqui. O motor põe stroke/fill como ATRIBUTO de
862
+ apresentação (#ccc nas linhas de grade, #666 nos rótulos), e CSS vence atributo — medido em
863
+ 01/08/2026: 20 <line> e 9 <text> num gráfico de linha com grade e dois eixos. */
864
+ .chart svg line { stroke:var(--border); } .chart svg text { fill:var(--muted-foreground); font-size:var(--text-xs); }
865
+ .chart-tooltip { display:grid; gap:var(--space-1); pointer-events:none; } .chart-tooltip strong { color:var(--foreground); font-weight:var(--weight-medium); }
866
+ .chart-tooltip b { margin-inline-start:auto; color:var(--foreground); font-variant-numeric:tabular-nums; }
867
+ .chart-legend { display:flex; flex-wrap:wrap; justify-content:center; gap:var(--space-1) var(--space-4); margin:0; padding:0; list-style:none; }
868
+ .chart-key { display:flex; align-items:center; gap:var(--space-2); color:var(--muted-foreground); font-size:var(--text-xs); } .chart-key i { flex:none; width:9px; height:9px; border-radius:var(--radius-sm); }
869
+ /* O cursor precisa de DOIS pontos de especificidade porque `.chart svg line` acima já tem dois
870
+ elementos: `.chart .chart-cursor` (0,2,0) vence `.chart svg line` (0,1,2). Em gráfico de linha
871
+ o cursor é <line> e em barra é <rect>, então a regra stroke E fill cada um usa o seu. */
872
+ .chart .chart-cursor { stroke:var(--border-strong); fill:var(--secondary); }
873
+ /* Calendar (Lote 4) — a pele da grade que o react-day-picker emite. Mesma regra do Chart e pela
874
+ mesma razão: nada de `.rdp-*` aqui (o check 15 reprova nome de classe que a Aurea não produz).
875
+ Três nomes vêm renomeados pela API `classNames` do motor; o resto é ELEMENTO e `data-*`, e
876
+ atributo não é classe. Estado medido em 01/08/2026: data-selected, data-today, data-outside,
877
+ data-disabled e data-hidden este último é obrigatório, senão o dia escondido das pontas
878
+ APARECE (o motor conta com o CSS dele para escondê-lo, e nós não importamos o CSS dele). */
879
+ .calendar { display:inline-block; padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--surface-1); }
880
+ .calendar-months { position:relative; }
881
+ .calendar nav { position:absolute; inset-block-start:0; inset-inline:0; display:flex; justify-content:space-between; }
882
+ .calendar nav button { display:inline-flex; align-items:center; justify-content:center; width:var(--control-h-sm); height:var(--control-h-sm); padding:0; border:0; border-radius:var(--radius-control); background:transparent; color:var(--muted-foreground); cursor:pointer; }
883
+ .calendar nav button:hover:not(:disabled) { background:var(--secondary); color:var(--foreground); }
884
+ .calendar nav button:disabled { opacity:.4; cursor:not-allowed; }
885
+ .calendar nav svg { width:var(--icon-sm); height:var(--icon-sm); fill:currentColor; }
886
+ .calendar-caption { display:flex; align-items:center; justify-content:center; height:var(--control-h-sm); color:var(--foreground); font-size:var(--text-sm); font-weight:var(--weight-medium); }
887
+ /* Estas três foram um DESFAZIMENTO: `width:auto`, `min-width:0`, `height:auto` e
888
+ `border-bottom:0` existiam para anular a regra de elemento da seção de dados. A regra foi
889
+ escopada em 02/08/2026 e o antídoto saiu junto corrigir onde nasce, não onde dói.
890
+ O `padding:0` FICA, e não é antídoto: é o 1px que o NAVEGADOR dá a `<td>`/`<th>`. Tirá-lo
891
+ junto com o resto engordou a célula para 38px e a grade para 266px — medido, e voltado. */
892
+ .calendar table { border-collapse:collapse; margin-block-start:var(--space-2); }
893
+ .calendar th { padding:0 0 var(--space-1); text-align:center; color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-regular); }
894
+ .calendar td { padding:0; text-align:center; }
895
+ .calendar td button { display:flex; align-items:center; justify-content:center; width:var(--control-h-md); height:var(--control-h-md); border:0; border-radius:var(--radius-control); background:transparent; color:var(--foreground); font-size:var(--text-sm); font-variant-numeric:tabular-nums; cursor:pointer; }
896
+ .calendar td button:hover:not(:disabled) { background:var(--secondary); }
897
+ .calendar td[data-today] button { font-weight:var(--weight-medium); box-shadow:inset 0 0 0 1px var(--border-strong); }
898
+ .calendar td[data-selected] button { background:var(--primary); color:var(--primary-foreground); font-weight:var(--weight-medium); box-shadow:none; }
899
+ .calendar td[data-outside] button { color:var(--subtle-foreground); }
900
+ .calendar td button:disabled { opacity:.35; cursor:not-allowed; }
901
+ .calendar td[data-hidden] { visibility:hidden; }
902
+ .message { display:grid; grid-template-columns:auto minmax(0,1fr); gap:12px; } .message-bubble { padding:12px 14px; border-radius:6px var(--radius-lg) var(--radius-lg) var(--radius-lg); background:var(--secondary); } .message-bubble p { margin:0; }
903
+ /* Chat React (Fase 5.4): estrutura fixa em classe, não inline (auditoria 18/07, M14). */
904
+ .message-status { display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); margin-block-start:var(--space-2); }
905
+ .message-composer { display:flex; align-items:center; gap:var(--space-3); flex-wrap:nowrap; } .message-composer .input-wrap { flex:1; min-width:0; }
906
+
907
+ /* ========================================================================== */
908
+ /* 07. MÍDIA */
909
+ /* ========================================================================== */
910
+ /* `--media-h` e `--media-ar` são VÁLVULA DE ESCAPE, autorizada pelo Victor em 11/08/2026 no item
911
+ I7 do PLANO-1.0, e no idioma exato das três que existem: `--chart-h` (I4), `--qr-size` (B6) e
912
+ `--datagrid-max-h` (F6). O default é o de antes 360px e 16/9 —, então nada muda para quem não
913
+ passa nada. A causa é medida: numa caixa de demonstração de 826px de largura o `aspect-ratio`
914
+ produz 464px de altura, contra um vão útil de 354; o player não cabe numa composição, e o
915
+ `MediaPlayer` não repassa `style` para esta caixa (as props sobrando vão para o `<video>`, de
916
+ propósito). Quem sabe quanta altura tem é a página, e altura não vira prop (check 23).
917
+ ⚠ `--media-ar:auto` SÓ é seguro junto com `--media-h`. A razão do `aspect-ratio` aqui não é
918
+ estética: é CLS. O exemplo de playlist do `media-chrome` declara o mesmo com o motivo escrito —
919
+ sem proporção fixa, um player com `preload=none` mede zero até a mídia chegar e a página SALTA.
920
+ Tirar a proporção sem fixar a altura devolve o salto. */
921
+ .media-player { position:relative; isolation:isolate; overflow:hidden; min-height:var(--media-h,360px); aspect-ratio:var(--media-ar,16/9); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--media-canvas); color:var(--media-foreground); }
922
+ .media-viewport { position:absolute; inset:0; display:grid; place-items:center; background:var(--media-canvas); }
923
+ .media-fill { width:100%; height:100%; object-fit:contain; } /* vídeo React preenche o viewport (auditoria 18/07, M14) */
924
+ .media-placeholder { display:grid; place-items:center; gap:10px; color:var(--media-muted); text-align:center; }
925
+ .media-placeholder .icon { width:48px; height:48px; color:var(--media-foreground); }
926
+ .media-placeholder strong { color:var(--media-foreground); }
927
+ .media-overlay-title { position:absolute; top:16px; inset-inline:16px; z-index:2; display:flex; align-items:flex-start; justify-content:space-between; gap:16px; color:var(--media-foreground); }
928
+ .media-overlay-title>div>span { display:block; color:var(--media-muted); font-size:var(--text-xs); }
929
+ .media-player .badge { border-color:var(--media-track); background:var(--media-track); color:var(--media-foreground); }
930
+ .media-controls { position:absolute; inset:auto 0 0; z-index:3; display:grid; gap:8px; padding:12px; background:var(--media-scrim); color:var(--media-foreground); }
931
+ .media-seek { --media-played:34%; position:relative; height:20px; display:flex; align-items:center; }
932
+ .media-seek-track { position:absolute; inset-inline:0; height:4px; overflow:hidden; border-radius:var(--radius-full); background:var(--media-track); pointer-events:none; }
933
+ .media-seek-buffered,.media-seek-played { position:absolute; inset-block:0; inset-inline:0 auto; border-radius:inherit; }
934
+ .media-seek-buffered { width:62%; background:var(--media-buffered); }
935
+ .media-seek-played { width:var(--media-played); background:var(--primary); }
936
+ .media-seek input { position:relative; z-index:1; width:100%; height:20px; margin:0; appearance:none; background:transparent; cursor:pointer; }
937
+ .media-seek input::-webkit-slider-runnable-track { height:4px; background:transparent; }
938
+ .media-seek input::-webkit-slider-thumb { width:14px; height:14px; margin-top:-5px; appearance:none; border:2px solid var(--media-foreground); border-radius:50%; background:var(--primary); }
939
+ .media-seek input::-moz-range-track { height:4px; background:transparent; }
940
+ .media-seek input::-moz-range-thumb { width:12px; height:12px; border:2px solid var(--media-foreground); border-radius:50%; background:var(--primary); }
941
+ .media-seek input:focus-visible { outline:2px solid var(--primary); outline-offset:3px; box-shadow:none; }
942
+ .media-control-row,.media-control-group { display:flex; align-items:center; gap:4px; }
943
+ .media-control-row { min-width:0; justify-content:space-between; }
944
+ .media-control { min-width:36px; height:36px; display:inline-grid; place-items:center; padding:0 8px; border:1px solid transparent; border-radius:var(--radius-md); background:transparent; color:var(--media-foreground); cursor:pointer; }
945
+ .media-control:hover,.media-control.active { background:var(--media-track); color:var(--primary); }
946
+ .media-time { padding:0 7px; color:var(--media-muted); font:var(--text-xs) var(--font-code); white-space:nowrap; }
947
+ .media-volume { width:82px; accent-color:var(--primary); }
948
+
949
+ /* ========================================================================== */
950
+ /* 08. ÍCONES */
951
+ /* ========================================================================== */
952
+ .icon-search { max-width:420px; }
953
+ .icon-category { margin-top:26px; } .icon-category h3 { margin:0 0 12px; font-size:var(--text-base); }
954
+ .icon-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:8px; }
955
+ .icon-tile { min-height:86px; display:flex; flex-direction:column; align-items:flex-start; justify-content:space-between; gap:12px; padding:13px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--secondary); color:var(--muted-foreground); cursor:pointer; text-align:start; transition:transform var(--duration-fast) var(--ease-standard),border-color var(--duration-fast),color var(--duration-fast); }
956
+ @media (hover:hover) and (pointer:fine) { .icon-tile:hover { color:var(--foreground); border-color:var(--primary); transform:translateY(-1px); } } .icon-tile:focus-visible { color:var(--foreground); border-color:var(--primary); } .icon-tile span { overflow:hidden; max-width:100%; text-overflow:ellipsis; white-space:nowrap; font:11px var(--font-code); }
957
+ .dialog-backdrop { position:fixed; inset:0; z-index:var(--z-modal); background:var(--overlay); backdrop-filter:blur(5px); }
958
+ .dialog { position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); z-index:var(--z-modal); width:min(560px,calc(100vw - 40px)); max-height:calc(100vh - 40px); overflow:auto; padding:24px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
959
+ .dialog>header,.drawer>header { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:16px; } .dialog-body { display:grid; gap:14px; }
960
+ .drawer-wrap { position:fixed; inset:0; z-index:var(--z-drawer); display:none; background:var(--overlay); } .drawer-wrap.open { display:block; }
961
+ .drawer-backdrop { position:fixed; inset:0; z-index:var(--z-drawer); background:var(--overlay); }
962
+ .drawer { position:fixed; top:0; bottom:0; z-index:var(--z-drawer); width:min(480px,92vw); padding:24px; background:var(--surface-1); box-shadow:var(--shadow-lg); overflow:auto; } .drawer-right { right:0; border-left:1px solid var(--border); } .drawer-left { left:0; border-right:1px solid var(--border); }
963
+ .toast-stack { position:fixed; inset-inline-end:22px; bottom:22px; z-index:var(--z-toast); width:min(360px,calc(100vw - 44px)); display:flex; flex-direction:column; gap:10px; } .toast { display:flex; align-items:center; gap:11px; padding:14px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-md); transition:opacity .28s var(--ease-standard), transform .28s var(--ease-emphasized); } .toast>:first-child { flex:1; min-width:0; } .toast-text { display:flex; flex-direction:column; gap:2px; min-width:0; } .toast[data-starting-style],.toast[data-ending-style] { opacity:0; transform:translateY(12px); }
964
+ /* CommandPaletteShell (Fase 11, achado A13). A superfície já existia aqui sob o nome
965
+ `.command` — e `.command` é chrome da página de docs manual: nenhum componente React a
966
+ emite. Ela passava no check 15 por ACIDENTE, porque o rótulo padrão "Type a command…" põe
967
+ a palavra "command" numa string do pacote React e o gate lê as strings do fonte. Foi para
968
+ apps/docs/docs.css, que é a casa dela; o sistema fica com os dois nomes que o componente
969
+ emite de verdade. Não-modal de propósito, como a gaveta do AppShell (ADR-0003): o foco não
970
+ fica preso, então não declaramos modalidade que não entregamos. */
971
+ .command-overlay { position:fixed; inset:0; z-index:var(--z-modal); display:grid; place-items:start center; padding:var(--space-16) var(--space-5) var(--space-5); background:var(--overlay); backdrop-filter:blur(5px); }
972
+ .command-palette { display:grid; gap:var(--space-2); width:min(680px,100%); max-height:100%; overflow:auto; padding:var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
973
+ /* Camadas flutuantes (Base UI): tooltip, popover, menu. z-modal para funcionarem sobre diálogos; empilhamento mútuo pela ordem do portal no DOM. */
974
+ .tooltip { z-index:var(--z-modal); max-width:260px; padding:6px 10px; border:1px solid var(--border); border-radius:var(--radius-md); background:var(--surface-inset); color:var(--foreground); font-size:var(--text-xs); line-height:var(--leading-normal); box-shadow:var(--shadow-md); }
975
+ .popover { z-index:var(--z-modal); min-width:220px; max-width:min(360px,calc(100vw - 32px)); padding:16px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); display:grid; gap:10px; }
976
+ .menu { z-index:var(--z-modal); min-width:200px; max-width:min(320px,calc(100vw - 32px)); padding:6px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
977
+ .menu-item { display:flex; align-items:center; gap:10px; width:100%; min-height:var(--control-h-md); padding:0 10px; border:0; border-radius:var(--radius-sm); background:transparent; color:var(--foreground); font:inherit; text-align:start; cursor:pointer; }
978
+ .menu-item[data-highlighted],.menu-item:hover:not([data-disabled]) { background:var(--surface-3); outline:none; }
979
+ .menu-item[data-disabled] { opacity:.5; cursor:not-allowed; }
980
+ .menu-sep { height:1px; margin:6px 4px; background:var(--border); border:0; }
981
+ /* Combobox: reaproveita .menu/.menu-item; só dimensionamento e a coluna do check são próprios.
982
+ --anchor-width e --available-height vêm do positioner do Base UI. */
983
+ .combobox-group { position:relative; display:flex; align-items:center; }
984
+ .combobox-group .input { padding-inline-end:calc(var(--control-h-sm) * 2 + 10px); }
985
+ .combobox-actions { position:absolute; inset-inline-end:5px; display:flex; align-items:center; gap:2px; }
986
+ .combobox-popup { inline-size:var(--anchor-width); min-inline-size:0; max-inline-size:none; max-block-size:min(320px,var(--available-height)); overflow-y:auto; overscroll-behavior:contain; }
987
+ .combobox-empty { padding:12px 10px; color:var(--muted-foreground); font-size:var(--text-sm); }
988
+ .combobox-empty:empty { display:none; }
989
+ .combobox-item { display:grid; grid-template-columns:16px minmax(0,1fr); gap:8px; align-items:center; }
990
+ .combobox-check { display:inline-flex; color:var(--primary); }
991
+ /* MultiCombobox (Fase 4) — chips de multi-seleção + grupos. Espaçamento novo tokenizado. */
992
+ .combobox-multi { display:flex; align-items:center; gap:var(--space-2); width:100%; min-width:0; min-height:var(--control-h-md); padding:var(--space-1) var(--space-2); background:var(--field-bg); border:1px solid var(--border-strong); border-radius:var(--radius-control); transition:border-color var(--duration-fast); }
993
+ .combobox-multi:hover { border-color:var(--border); }
994
+ .combobox-multi:focus-within { outline:2px solid var(--focus-strong); outline-offset:2px; border-color:var(--focus-strong); }
995
+ .combobox-multi>.combobox-actions { position:static; inset-inline-end:auto; }
996
+ .combobox-chips { display:flex; flex:1; flex-wrap:wrap; align-items:center; gap:var(--space-2); min-width:0; }
997
+ .combobox-chip { display:inline-flex; align-items:center; gap:var(--space-1); padding-block:var(--space-1); padding-inline:var(--space-3) var(--space-1); background:var(--secondary); color:var(--foreground); border-radius:var(--radius-control); font-size:var(--text-sm); }
998
+ .combobox-chip-remove { display:inline-flex; padding:0; border:0; background:transparent; color:var(--muted-foreground); border-radius:var(--radius-full); cursor:pointer; }
999
+ .combobox-chip-remove:hover { color:var(--foreground); }
1000
+ .combobox-chip-input { flex:1; min-width:6ch; height:var(--control-h-sm); border:0; background:transparent; padding:0 var(--space-1); color:var(--foreground); }
1001
+ .combobox-chip-input::placeholder { color:var(--subtle-foreground); }
1002
+ .combobox-chip-input:focus-visible { outline:none; }
1003
+ .combobox-group-label { padding:var(--space-1) var(--space-2); color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); }
1004
+ /* FileInput / DropZone (Fase 4) — só a UI de entrada; upload real é a Fase 5.
1005
+ A zona é um <label> em volta do <input> nativo: clique/foco/teclado de graça,
1006
+ o arrastar-soltar é reforço. Espaçamento novo tokenizado; superfície = card. */
1007
+ .dropzone { display:flex; flex-direction:column; align-items:center; gap:var(--space-2); padding:var(--space-8) var(--space-6); text-align:center; color:var(--muted-foreground); background:var(--surface-inset); border:1px dashed var(--border-strong); border-radius:var(--radius-card); cursor:pointer; transition:border-color var(--duration-fast),background-color var(--duration-fast); }
1008
+ .dropzone:hover { border-color:var(--border); }
1009
+ .dropzone:focus-within { outline:2px solid var(--focus-strong); outline-offset:2px; border-color:var(--focus-strong); }
1010
+ .dropzone[data-dragging="true"] { border-style:solid; border-color:var(--primary); background:var(--surface-2); }
1011
+ .dropzone strong { color:var(--foreground); }
1012
+ .dropzone-icon { color:var(--primary); }
1013
+ .dropzone-hint { font-size:var(--text-xs); }
1014
+ .file-list { display:flex; flex-direction:column; gap:var(--space-2); margin-top:var(--space-3); padding:0; list-style:none; }
1015
+ .file-item { display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2) var(--space-3); background:var(--surface-2); border:1px solid var(--border); border-radius:var(--radius-md); }
1016
+ .file-progress { flex-basis:var(--space-20); flex-shrink:0; } /* largura fixa da barra de upload (auditoria 18/07, M14) */
1017
+ .file-name { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
1018
+ .file-size { flex:none; color:var(--muted-foreground); font-size:var(--text-xs); font-variant-numeric:tabular-nums; }
1019
+
1020
+ /* TreeView (Fase 4) — árvore headless própria (Base UI 1.6 não tem tree).
1021
+ role=tree/treeitem/group + roving tabindex vêm do React; aqui só a pele.
1022
+ A indentação por nível é padding-inline-start inline (style). Espaço novo
1023
+ tokenizado; a linha não é pill (radius-sm, como o .menu-item). */
1024
+ .tree, .tree-group { margin:0; padding:0; list-style:none; }
1025
+ .tree-item { outline:none; }
1026
+ .tree-node { display:flex; align-items:center; gap:var(--space-2); min-height:var(--control-h-md); padding-block:var(--space-1); padding-inline-end:var(--space-3); border-radius:var(--radius-sm); color:var(--foreground); cursor:pointer; user-select:none; }
1027
+ .tree-node:hover { background:var(--surface-3); }
1028
+ .tree-item:focus-visible > .tree-node { outline:2px solid var(--focus-strong); outline-offset:-2px; }
1029
+ .tree-node[data-selected] { background:color-mix(in srgb,var(--primary) 14%,transparent); }
1030
+ .tree-twist { flex:none; color:var(--muted-foreground); transition:transform var(--duration-fast); }
1031
+ .tree-item[aria-expanded="true"] > .tree-node > .tree-twist { transform:rotate(90deg); }
1032
+ .tree-item[aria-expanded="false"]:dir(rtl) > .tree-node > .tree-twist { transform:rotate(180deg); }
1033
+ .tree-indent { flex:none; width:1rem; }
1034
+ .tree-label { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
1035
+
1036
+ /* NotificationCenter (Fase 4) — compõe o .popover; aqui só o gatilho com count,
1037
+ o cabeçalho e as linhas do feed. Espaço novo tokenizado; a linha usa radius-sm
1038
+ (não é pill, como o .menu-item/.tree-node). O painel NÃO é live region — a
1039
+ chegada é anunciada por um role=status à parte no React. */
1040
+ .notification-trigger { position:relative; display:inline-flex; }
1041
+ .notification-count { position:absolute; inset-block-start:-4px; inset-inline-end:-4px; display:inline-flex; align-items:center; justify-content:center; min-width:18px; height:18px; padding:0 var(--space-1); border-radius:var(--radius-control); background:var(--primary); color:var(--primary-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); line-height:var(--leading-none); pointer-events:none; }
1042
+ .notification-panel { width:min(360px,calc(100vw - 32px)); max-width:none; padding:0; gap:0; display:flex; flex-direction:column; overflow:hidden; }
1043
+ .notification-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); padding:var(--space-3) var(--space-4); border-block-end:1px solid var(--border); }
1044
+ .notification-head strong { font-size:var(--text-sm); }
1045
+ .notification-list { margin:0; padding:var(--space-1); overflow-y:auto; max-height:min(60vh,440px); }
1046
+ .notification-sublist { margin:0; padding:0; list-style:none; }
1047
+ .notification-group-label { margin:0; padding:var(--space-2) var(--space-3) var(--space-1); color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); letter-spacing:var(--tracking-wide); text-transform:uppercase; }
1048
+ .notification-item { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:baseline; column-gap:var(--space-3); row-gap:var(--space-1); width:100%; padding:var(--space-3); border:0; border-radius:var(--radius-sm); background:none; color:var(--foreground); font:inherit; text-align:start; }
1049
+ button.notification-item { cursor:pointer; }
1050
+ button.notification-item:hover { background:var(--surface-3); }
1051
+ .notification-item:focus-visible { outline:2px solid var(--focus-strong); outline-offset:-2px; }
1052
+ .notification-dot { grid-row:1/-1; align-self:center; width:8px; height:8px; border-radius:50%; background:var(--primary); }
1053
+ .notification-item[data-read] .notification-dot { visibility:hidden; }
1054
+ .notification-item-title { grid-column:2; display:flex; align-items:center; gap:var(--space-2); min-width:0; font-size:var(--text-sm); font-weight:var(--weight-medium); }
1055
+ .notification-item[data-read] .notification-item-title { color:var(--muted-foreground); font-weight:var(--weight-regular); }
1056
+ .notification-item-desc { grid-column:2; color:var(--muted-foreground); font-size:var(--text-xs); }
1057
+ .notification-time { grid-column:3; grid-row:1; color:var(--muted-foreground); font-size:var(--text-xs); white-space:nowrap; }
1058
+ .notification-empty { margin:0; padding:var(--space-8) var(--space-4); color:var(--muted-foreground); font-size:var(--text-sm); text-align:center; }
1059
+ /* EmptyState é o membro GRANDE da família "aqui não há nada" — .notification-empty acima e
1060
+ .datagrid-empty abaixo já respondiam a mesma pergunta do mesmo jeito (centralizado, muted),
1061
+ e o componente público era o único dos três sem regra (achado A13). A ordem das partes
1062
+ (imagem → título → corpo → ação) é a do padrão de empty state do Carbon, registrado em
1063
+ REFERENCES.md; a pele é Aurea. O título vinha com o h3 do NAVEGADOR (16,38px). */
1064
+ .empty-state { display:flex; flex-direction:column; align-items:center; gap:var(--space-3); padding:var(--space-10) var(--space-6); color:var(--muted-foreground); text-align:center; }
1065
+ .empty-state > .icon { color:var(--subtle-foreground); }
1066
+ .empty-state p { max-width:46ch; margin:0; }
1067
+ .empty-title { margin:0; color:var(--foreground); font-size:var(--text-base); font-weight:var(--weight-semibold); line-height:var(--leading-tight); }
1068
+
1069
+ /* DataGrid (Fase 4) — TanStack Table v8 headless; aqui só a pele, que reusa
1070
+ .table-wrap/table/th/td. aria-sort fica só no th ordenado; a linha
1071
+ selecionada usa data-selected (o estado acessível é o checkbox da linha).
1072
+ Espaço novo tokenizado. */
1073
+ .datagrid { display:grid; gap:var(--space-3); }
1074
+ .datagrid > .table-wrap:focus-visible { outline:none; box-shadow:0 0 0 3px color-mix(in oklab,var(--focus) 38%,transparent); }
1075
+ .datagrid > .pagination { justify-self:end; }
1076
+ /* Fix aditivo: os 34px dos botões numéricos do .pagination esmagavam os botões
1077
+ de TEXTO (Anterior/Próxima) do Pagination React. Não muda os numéricos. */
1078
+ /* text-transform/letter-spacing: o UA reseta nos <button>; herdar explícito
1079
+ para o cabeçalho ordenável ficar idêntico ao th estático. */
1080
+ .datagrid-sort { display:inline-flex; align-items:center; gap:var(--space-1); padding:0; border:0; background:none; color:inherit; font:inherit; text-transform:inherit; letter-spacing:inherit; cursor:pointer; }
1081
+ .datagrid-sort:focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; border-radius:var(--radius-sm); }
1082
+ .datagrid-sort-icon { flex:none; color:var(--subtle-foreground); }
1083
+ th[aria-sort] .datagrid-sort-icon { color:var(--primary); }
1084
+ .datagrid-selcol { width:1%; white-space:nowrap; }
1085
+
1086
+ /* F3: a linha de filtro é do PRÓPRIO cabeçalho, então herda o recuo lateral das
1087
+ células e nasce alinhada com a coluna — ninguém escreve alinhamento aqui.
1088
+ As três declarações abaixo foram provadas UMA A UMA no navegador, tirando cada
1089
+ uma e medindo de novo: sem `text-transform` o campo digita em MAIÚSCULAS; sem
1090
+ `vertical-align` os dois controles (36px e 40px) começam em linhas diferentes;
1091
+ sem `gap:0` o rótulo só-para-leitor-de-tela cobra 7px de vão.
1092
+ Três outras foram escritas e MORRERAM na mesma prova, por não mudarem nada:
1093
+ `height:auto` (o conteúdo já passa de --row-h, e altura em célula é mínimo),
1094
+ `letter-spacing:normal` (o .input não herda o entreletras do cabeçalho) e
1095
+ `line-height:0` no rótulo (o <label> vazio já não ocupa altura). */
1096
+ .datagrid-filters th { padding-block:var(--space-2); text-transform:none; vertical-align:top; }
1097
+ .datagrid-filters .field { gap:0; }
1098
+
1099
+ /* F6: cabeçalho fixo. A `.table-wrap` já é o contêiner de rolagem (overflow:auto),
1100
+ e é ELE que ancora o sticky — não a janela. Por isso a altura precisa de teto:
1101
+ sem teto a caixa nunca rola por dentro e o cabeçalho nunca teria de grudar.
1102
+ `--datagrid-max-h` é a válvula de escape, no mesmo idioma do `--qr-size`: quem
1103
+ sabe quanta tela tem é o consumidor, e altura não vira prop numérica (check 23).
1104
+ A linha de FILTRO gruda logo abaixo do cabeçalho, e o deslocamento é `--row-h`
1105
+ porque é essa a altura da célula de cabeçalho — medido, não deduzido. */
1106
+ .datagrid-sticky .table-wrap { max-block-size:var(--datagrid-max-h,60vh); }
1107
+ /* `z-index:1` fica DECLARADO COMO PRECAUÇÃO, não como fato provado: não consegui
1108
+ medi-lo (elementFromPoint usa coordenada de janela, e a grade está a 7000px de
1109
+ rolagem na página de prova). É prática corrente para cabeçalho fixo — sem ele o
1110
+ fundo da linha do corpo pode passar por cima —, e removê-lo por não ter gate
1111
+ seria trocar risco real por pureza de método. As outras três regras daqui foram
1112
+ provadas. */
1113
+ .datagrid-sticky thead th { position:sticky; inset-block-start:0; z-index:1; }
1114
+ .datagrid-sticky .datagrid-filters th { inset-block-start:var(--row-h); }
1115
+
1116
+ /* F7: a alça de redimensionar. Fina, colada no fim da célula e da altura toda —
1117
+ alvo estreito de propósito, porque largo demais rouba o clique do cabeçalho
1118
+ ordenável que está ao lado. O `th` precisa de `position:relative` para ancorá-la. */
1119
+ .datagrid thead th:has(> .datagrid-resizer) { position:relative; }
1120
+ .datagrid-resizer { position:absolute; inset-block:0; inset-inline-end:0; inline-size:var(--space-1); padding:0; border:0; background:none; cursor:col-resize; touch-action:none; }
1121
+ .datagrid-resizer:hover, .datagrid-resizer:focus-visible { background:var(--primary); }
1122
+ .datagrid-resizer:focus-visible { outline:2px solid var(--focus-strong); outline-offset:1px; }
1123
+
1124
+ /* F8: o esqueleto dentro da célula. Sem altura própria ele mede zero — a `.skeleton`
1125
+ é uma <div> vazia — e a linha de carregamento sairia rasa demais para parecer uma
1126
+ linha. `1em` acompanha a tipografia em vez de fixar pixel. */
1127
+ .datagrid-skeleton { block-size:1em; }
1128
+
1129
+ /* F9: lista e detalhe lado a lado.
1130
+ LIMITE DECLARADO: a decisão é por largura de JANELA, não da grade. O `@container`
1131
+ seria mais certo — quem sabe se o painel cabe é a grade — e foi tentado: exige
1132
+ `container-type:inline-size` num ancestral, e pôr isso na `.datagrid` dá a ela
1133
+ contenção de tamanho, o que COLAPSA a grade dentro de um pai que encolhe para
1134
+ caber (float, inline-block, item de flex com largura automática). Trocar um
1135
+ defeito de layout raro por um defeito de layout comum não vale. Consequência
1136
+ honesta: grade estreita dentro de janela larga divide mesmo assim — o escape é
1137
+ `--datagrid-detail-w`. */
1138
+ .datagrid-split { display:grid; gap:var(--space-3); align-items:start; }
1139
+ @media (min-width:768px) {
1140
+ .datagrid-split { grid-template-columns:1fr var(--datagrid-detail-w,20rem); }
1141
+ }
1142
+ .datagrid-detail { border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); padding:var(--space-4); }
1143
+ .datagrid-detail-head { display:flex; justify-content:flex-end; }
1144
+
1145
+ /* G2: a ação de conjunto fica à direita, acima da fila — perto do que ela comanda e
1146
+ fora do caminho de leitura da lista. */
1147
+ .file-queue-actions { display:flex; justify-content:flex-end; }
1148
+
1149
+ /* G3/G4: a miniatura ocupa o mesmo espaço do ícone que ela substitui — a linha da
1150
+ fila não muda de altura por ter prévia. `object-fit:cover` porque foto não é
1151
+ quadrada e deformar a prévia mente sobre o arquivo. */
1152
+ .file-thumb { flex:none; inline-size:var(--space-5); block-size:var(--space-5); border-radius:var(--radius-sm); object-fit:cover; }
1153
+ .file-checksum { color:var(--muted-foreground); font-family:var(--font-code); font-size:var(--text-xs); }
1154
+
1155
+ /* G5/G6: a escolha do conflito e o recibo. Os dois são superfície de card porque são
1156
+ blocos de decisão e de registro, não linhas da fila. */
1157
+ .file-conflict { display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--surface-2); }
1158
+ .file-receipt { padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
1159
+ .file-receipt-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-2); }
1160
+ .file-receipt ul { margin:0; padding-inline-start:var(--space-5); color:var(--muted-foreground); font-size:var(--text-sm); }
1161
+
1162
+ /* Parte H — a camada operacional. As três primeiras peças são COMPOSIÇÃO: `.card` já
1163
+ dá a superfície flutuante, `.status` já dá o estado, `.data-list` já dá o par
1164
+ termo/valor. O que falta é só o arranjo — e arranjo é o que não vem de graça. */
1165
+ .agent-card { display:grid; gap:var(--space-3); }
1166
+ .agent-card-head { display:flex; align-items:center; gap:var(--space-3); }
1167
+ .agent-card-id { display:grid; flex:1; min-inline-size:0; }
1168
+ .agent-card-desc { margin:0; color:var(--muted-foreground); }
1169
+ .agent-caps { display:flex; flex-wrap:wrap; gap:var(--space-2); margin:0; padding:0; list-style:none; }
1170
+ .agent-cap { display:inline-flex; align-items:center; gap:var(--space-1); padding:var(--space-1) var(--space-3); border:1px solid var(--border); border-radius:var(--radius-control); font-size:var(--text-sm); }
1171
+ .agent-card-actions { display:flex; justify-content:flex-end; gap:var(--space-2); }
1172
+ .agent-inspector { display:grid; gap:var(--space-4); }
1173
+ .agent-inspector-head { display:flex; align-items:center; justify-content:space-between; }
1174
+ .agent-inspector-section { display:grid; gap:var(--space-2); }
1175
+ .agent-inspector-label { margin:0; color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; font-weight:var(--weight-bold); }
1176
+
1177
+ /* H.b — a execução. O passo dobrável é `<details>` nativo, então a marca de aberto/
1178
+ fechado já é do navegador; o que se acrescenta é o arranjo e o traço de estado. */
1179
+ .invocation { display:grid; gap:var(--space-4); }
1180
+ .invocation-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); }
1181
+ .invocation-io { display:grid; gap:var(--space-2); }
1182
+ .invocation-label { margin:0; color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; font-weight:var(--weight-bold); }
1183
+ .invocation-steps { display:grid; gap:var(--space-2); margin:0; padding:0; list-style:none; }
1184
+ /* O estado do passo entra por BORDA lateral, não por cor de fundo: fundo colorido
1185
+ compete com o texto do passo, e a borda diz o mesmo sem disputar leitura. */
1186
+ .invocation-step { padding-inline-start:var(--space-3); border-inline-start:2px solid var(--border); }
1187
+ .invocation-step[data-state="running"] { border-inline-start-color:var(--primary); }
1188
+ .invocation-step[data-state="error"] { border-inline-start-color:var(--danger-400); }
1189
+ .invocation-step-body { padding-block-start:var(--space-2); }
1190
+
1191
+ .task-queue { display:grid; gap:var(--space-3); margin:0; padding:0; list-style:none; }
1192
+ .task-item { display:grid; gap:var(--space-2); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
1193
+ .task-line { display:flex; align-items:center; gap:var(--space-2); }
1194
+ .task-title { flex:1; min-inline-size:0; font-weight:var(--weight-medium); }
1195
+ .task-desc { margin:0; color:var(--muted-foreground); font-size:var(--text-sm); }
1196
+
1197
+ /* H.c — governança da ação. */
1198
+ .approval { display:grid; gap:var(--space-3); }
1199
+ .approval-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); }
1200
+ .approval-desc, .approval-reason { margin:0; }
1201
+ .approval-reason { color:var(--muted-foreground); font-size:var(--text-sm); }
1202
+ /* Negar vem ANTES de aprovar na ordem de leitura e de tabulação: a ação destrutiva
1203
+ não deve ser a primeira a receber foco nem a mais fácil de acertar sem ler. */
1204
+ .approval-actions { display:flex; justify-content:flex-end; gap:var(--space-2); }
1205
+ .tool-permission-list { display:grid; gap:var(--space-3); margin:0; padding:0; list-style:none; }
1206
+ .tool-permission-item { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:var(--space-3); }
1207
+ .tool-permission-id { display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--space-2); min-inline-size:0; }
1208
+ .tool-permission-scope { padding:0 var(--space-1); border-radius:var(--radius-sm); background:var(--surface-2); font-family:var(--font-code); font-size:var(--text-xs); }
1209
+
1210
+ /* H.d — a observação. */
1211
+ .event-stream { display:grid; gap:var(--space-2); }
1212
+ /* O feed é do GRUPO: `role="feed"` só aceita `article` como filho, então o rótulo do grupo
1213
+ mora FORA dele. Ver o comentário do EventStream — a regra é do ARIA, não de estilo. */
1214
+ .event-group-feed { display:grid; gap:var(--space-2); }
1215
+ .event-group { margin:var(--space-3) 0 0; color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; font-weight:var(--weight-bold); }
1216
+ .event-item { display:flex; align-items:baseline; gap:var(--space-3); }
1217
+ .event-body { display:grid; flex:1; min-inline-size:0; }
1218
+ /* O ponto do evento é o MESMO tamanho e a mesma escala do ponto do Status: dois
1219
+ indicadores de gravidade com tamanhos diferentes na mesma tela leem como coisas
1220
+ diferentes, e não são. */
1221
+ .event-dot { flex:none; inline-size:var(--space-2); block-size:var(--space-2); border-radius:var(--radius-full); background:var(--muted-foreground); }
1222
+ .event-item[data-severity="success"] .event-dot { background:var(--success-400); }
1223
+ .event-item[data-severity="warning"] .event-dot { background:var(--warning-400); }
1224
+ .event-item[data-severity="danger"] .event-dot { background:var(--danger-400); }
1225
+ .event-time { flex:none; color:var(--muted-foreground); font-family:var(--font-code); font-size:var(--text-xs); }
1226
+
1227
+ .trace { display:grid; gap:var(--space-1); margin:0; padding:0; list-style:none; }
1228
+ /* A profundidade do span entra como RECUO, e por variável: a cascata precisa mostrar
1229
+ quem chamou quem, e recuar por nível é o que a referência faz sem precisar desenhar
1230
+ linhas de ligação. */
1231
+ /* O RÓTULO é quem cede, e a barra tem piso. A primeira versão era
1232
+ `minmax(6rem,14rem) 1fr`, e num painel estreito o rótulo comia tudo: medido no catálogo em
1233
+ 09/08/2026, as colunas resolviam `224px 24px` e a cascata inteira desenhava em 24px — barras
1234
+ de 24, 5, 6 e **0** pixels. O `1fr` só recebe a sobra, e não havia sobra. Com o mínimo do
1235
+ lado da BARRA e o rótulo podendo encolher até zero, quem trunca é o texto, que já tem
1236
+ reticência para isso — e o dado, que é a barra, nunca desaparece. */
1237
+ .trace-span { display:grid; grid-template-columns:minmax(0,14rem) minmax(8rem,1fr); align-items:center; gap:var(--space-3); padding-inline-start:calc(var(--trace-depth,0) * var(--space-3)); }
1238
+ .trace-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:var(--text-sm); }
1239
+ .trace-track { position:relative; block-size:var(--space-3); border-radius:var(--radius-full); background:var(--surface-2); }
1240
+ .trace-bar { position:absolute; inset-block:0; border-radius:var(--radius-full); background:var(--primary); }
1241
+ .trace-span[data-error] .trace-bar { background:var(--danger-400); }
1242
+
1243
+ .health-matrix { display:grid; grid-template-columns:repeat(auto-fill,minmax(12rem,1fr)); gap:var(--space-3); margin:0; padding:0; list-style:none; }
1244
+ .health-cell { display:grid; gap:var(--space-1); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
1245
+ .health-name { font-weight:var(--weight-medium); }
1246
+
1247
+ /* H.e — custo e memória.
1248
+ O número total é o `TotalMetric` da referência, e ele é UMA linha de arranjo: a escala
1249
+ tipográfica da Aurea vai só até `--text-base`, e o `text-3xl` de lá é a identidade DELA.
1250
+ Aqui o destaque vem do peso e do contraste com o rótulo em `--text-xs`, não de um degrau
1251
+ de fonte que a escala não tem. Compartilhado pelo ModelUsage e pelo CostMeter porque é a
1252
+ mesma coisa nos dois — duas classes diriam que são duas. */
1253
+ .metric-total { display:flex; align-items:baseline; gap:var(--space-2); }
1254
+ .metric-total strong { font-size:var(--text-base); font-weight:var(--weight-bold); font-variant-numeric:tabular-nums; }
1255
+
1256
+ .model-usage { display:grid; gap:var(--space-3); }
1257
+ .model-usage-list { display:grid; gap:var(--space-2); margin:0; padding:0; list-style:none; }
1258
+ /* Mesma regra do `.trace-span`, e pelo mesmo defeito medido no mesmo dia: o mínimo fica do
1259
+ lado da BARRA, e o nome do modelo encolhe com reticência. Barra sem piso desaparece antes
1260
+ do texto, e é ela que carrega o dado. */
1261
+ .model-usage-row { display:grid; grid-template-columns:minmax(0,12rem) minmax(8rem,1fr) auto; align-items:center; gap:var(--space-3); }
1262
+ .model-usage-name { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:var(--text-sm); }
1263
+ /* A trilha mede o MESMO que a do rastro: são os dois um `meter`, e dois medidores de
1264
+ alturas diferentes na mesma aplicação leem como coisas diferentes — não são. */
1265
+ .model-usage-track { position:relative; block-size:var(--space-3); border-radius:var(--radius-full); background:var(--surface-2); }
1266
+ .model-usage-bar { position:absolute; inset-block:0; inset-inline-start:0; border-radius:var(--radius-full); background:var(--primary); }
1267
+ .model-usage-value { font-size:var(--text-sm); font-variant-numeric:tabular-nums; }
1268
+
1269
+ .cost-meter { display:grid; gap:var(--space-3); }
1270
+ .cost-meter-track { position:relative; block-size:var(--space-3); border-radius:var(--radius-full); background:var(--surface-2); }
1271
+ .cost-meter-bar { position:absolute; inset-block:0; inset-inline-start:0; border-radius:var(--radius-full); background:var(--primary); }
1272
+ /* Estourar o teto muda a BARRA, e não o fundo do cartão: fundo colorido num cartão inteiro
1273
+ é o que a Aurea não faz, e a barra é justamente a peça que fala de quantidade.
1274
+ O limite BRANDO não tem cor, e isso foi MEDIDO, não escolhido por gosto: no tema escuro
1275
+ `--warning-400` É `var(--brand-yellow)`, o mesmo valor de `--primary`. Uma barra "de
1276
+ aviso" ali sairia idêntica à barra normal — degrau invisível justo em metade dos temas.
1277
+ O aviso do limite brando sai por PALAVRA, no Alert, que é o mesmo critério dos cinco
1278
+ estados do HealthMatrix. A cor fica para o limite rígido, onde o vermelho separa nos dois. */
1279
+ .cost-meter[data-state="over"] .cost-meter-bar { background:var(--danger-400); }
1280
+
1281
+ .memory-ledger { display:grid; gap:var(--space-2); margin:0; padding:0; list-style:none; }
1282
+ .memory-entry { display:grid; gap:var(--space-1); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
1283
+ .memory-line { display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--space-2); }
1284
+ .memory-content { flex:1; min-inline-size:0; }
1285
+ .memory-time { flex:none; color:var(--muted-foreground); font-family:var(--font-code); font-size:var(--text-xs); }
1286
+ /* Esquecida continua NA LISTA, riscada: o livro-razão registra o lançamento, não o estado
1287
+ final. Sumir com a linha apaga exatamente o que se procura quando o agente para de
1288
+ saber uma coisa. O risco não é a única marca — o selo ao lado diz "Forgotten" em palavra. */
1289
+ .memory-entry[data-operation="forgotten"] .memory-content { color:var(--muted-foreground); text-decoration:line-through; }
1290
+ .memory-provenance { font-size:var(--text-sm); }
1291
+ .memory-provenance summary { color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; cursor:pointer; }
1292
+ .memory-source { margin:var(--space-2) 0 0; color:var(--muted-foreground); font-size:var(--text-sm); }
1293
+
1294
+ /* H.f — a relação. */
1295
+ .agent-messages { display:grid; gap:var(--space-2); margin:0; padding:0; list-style:none; }
1296
+ .agent-message { display:grid; gap:var(--space-1); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
1297
+ /* O recado que FALHOU muda a borda, não o fundo: é o mesmo tratamento do passo do
1298
+ `InvocationPanel`, e pelo mesmo motivo — fundo colorido disputa leitura com o texto. */
1299
+ .agent-message[data-kind="error"] { border-color:var(--danger-400); }
1300
+ .agent-message-line { display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2); }
1301
+ .agent-message-route { display:inline-flex; align-items:center; gap:var(--space-1); flex:1; min-inline-size:0; }
1302
+ .agent-message-arrow { flex:none; color:var(--muted-foreground); }
1303
+ .agent-message-time { flex:none; color:var(--muted-foreground); font-family:var(--font-code); font-size:var(--text-xs); }
1304
+ .agent-message-body { margin:0; }
1305
+ .agent-message-why { font-size:var(--text-sm); }
1306
+ .agent-message-why summary { color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; font-weight:var(--weight-bold); cursor:pointer; }
1307
+ .agent-message-reason { margin:var(--space-2) 0 0; color:var(--muted-foreground); font-size:var(--text-sm); }
1308
+
1309
+ .automation { display:grid; gap:var(--space-3); }
1310
+ .automation-head { display:flex; align-items:flex-start; justify-content:space-between; gap:var(--space-3); }
1311
+ .automation-id { display:grid; flex:1; min-inline-size:0; }
1312
+ /* Regra DESLIGADA não some e não muda de cor — ela recua. O interruptor ao lado já diz o
1313
+ estado em palavra e em teclado; apagar o cartão esconderia justamente a regra que alguém
1314
+ precisa achar para religar. */
1315
+ .automation:not([data-enabled]) .automation-id { color:var(--muted-foreground); }
1316
+ .automation-foot { display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-3); }
1317
+
1318
+ /* H14 — o grafo. A pele do MOTOR entra por variável, e nenhuma classe dele aparece aqui:
1319
+ o `@xyflow/react` publica tudo como `var(--xy-algo, --xy-algo-default)`, e sobrescrever a
1320
+ variável na nossa raiz vale para toda a árvore dele (propriedade customizada herda). Escrever
1321
+ `.react-flow__*` no core resolveria igual e criaria uma classe que componente nenhum daqui
1322
+ produz — o check 15 existe exatamente para barrar isso. O consumidor importa o `base.css`
1323
+ do motor (estrutura); o `style.css` dele, que é a identidade deles, nunca entra. */
1324
+ /* `inline-size:100%` não é enfeite, é a mesma lição do Chart: painel de demo é flex, e uma
1325
+ caixa de largura automática entra como item que encolhe — medido no catálogo, a tela do grafo
1326
+ deu 71px e os nós saíram todos para fora dela. A tela ocupa o que lhe derem. */
1327
+ .dependency-graph { inline-size:100%; block-size:28rem; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); overflow:hidden;
1328
+ --xy-background-color:transparent;
1329
+ --xy-background-pattern-color:var(--border);
1330
+ --xy-edge-stroke:var(--muted-foreground);
1331
+ --xy-edge-stroke-selected:var(--primary);
1332
+ --xy-edge-label-color:var(--muted-foreground);
1333
+ --xy-edge-label-background-color:var(--card);
1334
+ --xy-connectionline-stroke:var(--primary);
1335
+ --xy-attribution-background-color:transparent; }
1336
+ /* A atribuição do motor (que a licença dele manda manter, e mantemos) traz `color:#999`
1337
+ CRAVADO no `init.css` — o único ponto em que a folha "estrutural" carrega cor. Reprovou
1338
+ contraste no axe, tema claro. A regra é de ELEMENTO e vem ESCOPADA à nossa classe, que é a
1339
+ lição que o `table { min-width:720px }` do Lote 4 deixou: regra de elemento no core não tem
1340
+ gate, então escreva-a presa a alguma coisa nossa ou ela é global.
1341
+ E leva `[href]` para GANHAR de `.react-flow__attribution a`, que tem a MESMA especificidade
1342
+ e vem depois na folha do consumidor. Subir por atributo evita citar a classe deles aqui —
1343
+ classe de terceiro no nosso core é o que o check 15 barra, e com razão. */
1344
+ .dependency-graph a[href] { color:var(--muted-foreground); }
1345
+ /* O nó é uma superfície flutuante como qualquer outra da Aurea — mesmo raio de cartão, mesma
1346
+ borda, mesmo fundo. É aqui que ele deixa de parecer React Flow. */
1347
+ .graph-node { position:relative; inline-size:180px; }
1348
+ .graph-node-body { display:grid; gap:var(--space-05); inline-size:100%; padding:var(--space-2) var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--foreground); font-family:inherit; font-size:var(--text-sm); text-align:start; }
1349
+ button.graph-node-body { cursor:pointer; }
1350
+ .graph-node[data-selected] .graph-node-body { border-color:var(--primary); }
1351
+ .graph-node-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight:var(--weight-medium); }
1352
+ /* `kind` é TEXTO, e não cor: a referência pinta dez tipos de nó em dez cores, e é a quarta vez
1353
+ que esta parte recusa a mesma coisa. */
1354
+ .graph-node-kind { color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; }
1355
+ .graph-handle { inline-size:var(--space-2); block-size:var(--space-2); border:1px solid var(--border); background:var(--surface-2); }
1356
+ .datagrid tbody tr[data-selected] { background:color-mix(in srgb,var(--primary) 14%,transparent); }
1357
+ .datagrid-empty { padding:var(--space-6); color:var(--muted-foreground); text-align:center; }
1358
+
1359
+ /* ========================================================================== */
1360
+ /* 10. RESPONSIVIDADE */
1361
+ /* ========================================================================== */
1362
+ /* Aperto de tela baixa: é densidade do SHELL DOS DOCS (.doc-sidebar), não da .doc-nav em
1363
+ si — sem o escopo ela zerava o gap de qualquer nav, inclusive a do catálogo. */
1364
+ /* Legado do catálogo escrito à mão: `display:block` desmancha o grid e empilha a lateral.
1365
+ Agora vale SÓ para o shell que não tem gaveta — o `apps/docs/index.html`, que traz a
1366
+ própria `.doc-sidebar` e o próprio JS de abrir. O shell do sistema segue em grid de uma
1367
+ coluna com a gaveta acima. Sai junto com aquela página (achado M13). */
1368
+ @media (max-width:640px) { :root { --card-pad:16px; --section-gap:16px; } .doc-actions .select { flex:1; width:auto!important; min-width:0; } .doc-actions>.btn-primary { width:var(--control-h-md); padding:0; font-size:0; } .doc-actions>.btn-primary .mono { display:none; } .tabs,.pagination,.segmented { max-width:100%; overflow-x:auto; } .step:nth-child(3):before { display:none; } .alert { grid-template-columns:auto minmax(0,1fr); } .alert>:last-child { grid-column:2; justify-self:start; } .log-line { grid-template-columns:70px 58px minmax(0,1fr); gap:6px; } .queue-item>.badge { grid-column:2; justify-self:start; } .event-row>.badge { grid-column:3; justify-self:start; } .message { grid-template-columns:1fr; } .media-player { min-height:330px; aspect-ratio:auto; } .media-overlay-title { top:12px; left:12px; right:12px; } .media-controls { padding:10px; } .media-control-row { align-items:flex-start; flex-direction:column; gap:8px; } .media-control-row>.media-control-group { width:100%; justify-content:space-between; } .media-optional,.media-volume { display:none; } .media-time { padding:0 4px; font-size:11px; } .toast-stack { inset-inline:12px; bottom:12px; } .toast { width:100%; min-width:0; grid-template-columns:minmax(0,1fr) auto; } .drawer { width:100%; padding:20px; } .dialog { padding:20px; } }
1369
+ @media (prefers-reduced-motion:reduce) { *,*:before,*:after { scroll-behavior:auto!important; animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important; } }
1370
+
1371
+ /* Padrões compostos Aurea 1.7: blocos de operação, não widgets de domínio. */
1372
+ /* Alvo de toque. `.tabs`/`.segmented` no lugar de `.tab`/`.segmented button`: agora a altura
1373
+ mora na cápsula, então é ela que precisa crescer — o filho estica atrás. */
1374
+ @media (pointer:coarse) { .btn:not(.btn-link),.tabs,.segmented,.pagination button,.schedule-grid button,.media-control { min-height:var(--target-min); } .btn-icon,.media-control { min-width:var(--target-min); } }
1375
+ @media (forced-colors:active) {
1376
+ :focus-visible { outline:2px solid CanvasText; outline-offset:2px; box-shadow:none; }
1377
+ .badge,.pattern-gallery button[aria-pressed="true"],.device-grid button[aria-pressed="true"] { border:1px solid CanvasText; }
1378
+ }
1379
+
1380
+
1381
+ /* Aurea v1.7 stabilization patch — visual design unchanged. */
1382
+ .badge-running{background:var(--success-bg);color:var(--success-400);border-color:color-mix(in oklab,var(--success-400) 32%,transparent)}
1383
+ .table-region{overflow:auto;outline:none;border-radius:var(--radius-lg)}
1384
+ .table-region:focus-visible{box-shadow:0 0 0 3px color-mix(in oklab,var(--focus) 38%,transparent)}
1385
+ /* CONTAINER QUERY, e não breakpoint de viewport — Parte D, item D3 (08/08/2026).
1386
+ Era `@media (max-width:400px)`, o último dos seis legados do achado M13. Trocar por um degrau
1387
+ da escala (639px) teria resolvido o gate e mantido o defeito: a largura que aperta os
1388
+ controles é a do PLAYER, não a da janela. Um player numa lateral de 400px dentro de uma tela
1389
+ de 1920px tem exatamente o mesmo aperto, e o breakpoint de viewport nunca o via.
1390
+ `.media-player` já era `position:relative` + `overflow:hidden`, então a contenção não muda o
1391
+ posicionamento do que está dentro — medido antes de escrever. */
1392
+ .media-player { container-type:inline-size; }
1393
+ @container (max-width:400px) {
1394
+ .media-controls { flex-wrap:wrap; overflow:visible!important; gap:8px; }
1395
+ .media-control { min-width:var(--target-min); min-height:var(--target-min); }
1396
+ }
1397
+ @media(pointer:coarse){
1398
+ .media-control,.btn-icon{min-width:var(--target-min);min-height:var(--target-min)}
1399
+ }
1400
+
1401
+ /* ========================================================================== */
1402
+ /* 12. RTL */
1403
+ /* ========================================================================== */
1404
+ /* O eixo inline do CSS acima já é lógico e espelha sozinho. Aqui ficam só os
1405
+ casos sem forma lógica: background-position e translateX.
1406
+ Deliberadamente NÃO espelham:
1407
+ - o check do .checkbox e o .spinner são glifos desenhados com borda;
1408
+ - .map-* e .builder-* são geografia/arte com rotate() fixo — espelhar os
1409
+ nós sem as arestas quebraria o desenho;
1410
+ - .drawer-left/.drawer-right prometem lado físico no nome (prop side). */
1411
+ :dir(rtl) .select { background-position:12px 50%; }
1412
+ :dir(rtl) .switch input:checked + .switch-track:after { transform:translateX(-18px); }
952
1413
  }