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