@aurea-uds/core 0.1.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 ADDED
@@ -0,0 +1,952 @@
1
+ @layer aurea{
2
+ /* Aurea tokens — generated from aurea.tokens.json (DTCG 2025.10). Do not edit. */
3
+ :root{
4
+ --brand-yellow:oklch(0.795 0.184 86.047);
5
+ --brand-yellow-foreground:oklch(0.421 0.095 57.708);
6
+ --font-ui:"IBM Plex Sans","Segoe UI",sans-serif;
7
+ --font-editorial:"IBM Plex Serif",Georgia,serif;
8
+ --font-code:"IBM Plex Mono",SFMono-Regular,Consolas,monospace;
9
+ --text-xs:0.75rem;
10
+ --text-sm:0.8125rem;
11
+ --text-md:0.875rem;
12
+ --text-base:1rem;
13
+ --text-lg:1.125rem;
14
+ --text-xl:1.25rem;
15
+ --text-2xl:1.5rem;
16
+ --text-3xl:1.875rem;
17
+ --text-4xl:2.25rem;
18
+ --text-5xl:3rem;
19
+ --leading-tight:1.2;
20
+ --leading-normal:1.5;
21
+ --leading-relaxed:1.7;
22
+ --space-0:0;
23
+ --space-05:0.125rem;
24
+ --space-1:0.25rem;
25
+ --space-2:0.5rem;
26
+ --space-3:0.75rem;
27
+ --space-4:1rem;
28
+ --space-5:1.25rem;
29
+ --space-6:1.5rem;
30
+ --space-7:1.75rem;
31
+ --space-8:2rem;
32
+ --space-10:2.5rem;
33
+ --space-12:3rem;
34
+ --space-16:4rem;
35
+ --space-20:5rem;
36
+ --space-24:6rem;
37
+ --radius-xs:6px;
38
+ --radius-sm:8px;
39
+ --radius-md:10px;
40
+ --radius-lg:16px;
41
+ --radius-card:22px;
42
+ --radius-control:999px;
43
+ --radius-full:var(--radius-control);
44
+ --border-width:1px;
45
+ --shadow-md:0 16px 42px rgba(0,0,0,0.28);
46
+ --shadow-lg:0 28px 80px rgba(0,0,0,0.42);
47
+ --duration-fast:120ms;
48
+ --duration-base:180ms;
49
+ --duration-slow:280ms;
50
+ --ease-standard:cubic-bezier(0.2,0,0,1);
51
+ --ease-emphasized:cubic-bezier(0.2,0.8,0.2,1);
52
+ --z-base:0;
53
+ --z-sticky:20;
54
+ --z-dropdown:40;
55
+ --z-drawer:60;
56
+ --z-modal:80;
57
+ --z-toast:100;
58
+ --sidebar-width:16.5rem;
59
+ --topbar-height:4rem;
60
+ --content-max:102.5rem;
61
+ --breakpoint-sm:640px;
62
+ --breakpoint-md:768px;
63
+ --breakpoint-lg:1024px;
64
+ --breakpoint-xl:1280px;
65
+ --breakpoint-2xl:1536px;
66
+ --control-h-xs:1.625rem;
67
+ --control-h-sm:1.875rem;
68
+ --control-h-md:2.25rem;
69
+ --control-h-lg:2.625rem;
70
+ --control-h-xl:3.125rem;
71
+ --row-h:3rem;
72
+ --card-pad:1.25rem;
73
+ --section-gap:1.5rem;
74
+ --media-canvas:oklch(0.085 0 0);
75
+ --media-foreground:oklch(0.985 0 0);
76
+ --media-muted:oklch(0.74 0 0);
77
+ --media-scrim:oklch(0 0 0 / 0.82);
78
+ --media-track:oklch(1 0 0 / 0.18);
79
+ --media-buffered:oklch(1 0 0 / 0.42);
80
+ --media-caption-bg:oklch(0 0 0 / 0.82);
81
+ --media-control-bg:oklch(0 0 0 / 0.52);
82
+ --media-control-hover:oklch(1 0 0 / 0.14);
83
+ --text-muted:var(--muted-foreground);
84
+ --success-400:#42d392;
85
+ --success-500:#22c875;
86
+ --success-700:#147a4d;
87
+ --warning-400:var(--brand-yellow);
88
+ --warning-500:#d49c00;
89
+ --warning-800:#6d4d00;
90
+ --danger-400:#ff7180;
91
+ --danger-500:#e94b58;
92
+ --danger-800:#7f2029;
93
+ --info-400:#8ec5ff;
94
+ --info-500:#2b7fff;
95
+ --info-800:#1447e6;
96
+ --oracle-50:oklch(0.96 0.025 251.813);
97
+ --oracle-100:oklch(0.9 0.06 251.813);
98
+ --oracle-200:oklch(0.85 0.09 251.813);
99
+ --oracle-300:var(--chart-1);
100
+ --oracle-400:var(--chart-2);
101
+ --oracle-500:var(--chart-3);
102
+ --oracle-600:var(--chart-4);
103
+ --oracle-700:var(--chart-5);
104
+ --oracle-800:oklch(0.38 0.2 265.638);
105
+ --oracle-900:oklch(0.31 0.16 265.638);
106
+ --oracle-950:oklch(0.23 0.11 265.638);
107
+ --weight-regular:400;
108
+ --weight-medium:500;
109
+ --weight-semibold:600;
110
+ --weight-bold:700;
111
+ --icon-sm:1rem;
112
+ --icon-md:1.25rem;
113
+ --icon-lg:1.5rem;
114
+ --icon-xl:2rem;
115
+ --tracking-tight:-0.01em;
116
+ --tracking-normal:0;
117
+ --leading-none:1;
118
+ --target-min:2.75rem;
119
+ --tracking-wide:0.04em;
120
+ --tracking-wider:0.06em;
121
+ --tracking-widest:0.12em;
122
+ }
123
+ [data-theme="dark"]{
124
+ --background:oklch(0.145 0 0);
125
+ --foreground:oklch(0.985 0 0);
126
+ --card:oklch(0.205 0 0);
127
+ --card-foreground:oklch(0.985 0 0);
128
+ --popover:oklch(0.205 0 0);
129
+ --popover-foreground:oklch(0.985 0 0);
130
+ --primary:var(--brand-yellow);
131
+ --primary-foreground:var(--brand-yellow-foreground);
132
+ --secondary:oklch(0.274 0.006 286.033);
133
+ --secondary-foreground:oklch(0.985 0 0);
134
+ --muted:oklch(0.269 0 0);
135
+ --muted-foreground:oklch(0.708 0 0);
136
+ --accent:oklch(0.269 0 0);
137
+ --accent-foreground:oklch(0.985 0 0);
138
+ --destructive:oklch(0.704 0.191 22.216);
139
+ --destructive-foreground:oklch(0.2 0.02 22);
140
+ --border:oklch(1 0 0 / 0.1);
141
+ --input:oklch(1 0 0 / 0.15);
142
+ --ring:oklch(0.556 0 0);
143
+ --chart-1:oklch(0.809 0.105 251.813);
144
+ --chart-2:oklch(0.623 0.214 259.815);
145
+ --chart-3:oklch(0.546 0.245 262.881);
146
+ --chart-4:oklch(0.488 0.243 264.376);
147
+ --chart-5:oklch(0.424 0.199 265.638);
148
+ --sidebar:oklch(0.205 0 0);
149
+ --sidebar-foreground:oklch(0.985 0 0);
150
+ --sidebar-primary:var(--brand-yellow);
151
+ --sidebar-primary-foreground:var(--brand-yellow-foreground);
152
+ --sidebar-accent:oklch(0.269 0 0);
153
+ --sidebar-accent-foreground:oklch(0.985 0 0);
154
+ --sidebar-border:oklch(1 0 0 / 0.1);
155
+ --sidebar-ring:oklch(0.556 0 0);
156
+ --surface-1:var(--card);
157
+ --surface-2:var(--secondary);
158
+ --surface-3:var(--muted);
159
+ --surface-inset:oklch(0.145 0 0);
160
+ --surface-hover:oklch(0.3 0 0);
161
+ --foreground-strong:var(--foreground);
162
+ --subtle-foreground:oklch(0.76 0 0);
163
+ --border-strong:oklch(0.58 0 0);
164
+ --field-bg:var(--muted);
165
+ --overlay:oklch(0 0 0 / 0.74);
166
+ --primary-hover:oklch(0.835 0.19 89);
167
+ --primary-active:oklch(0.74 0.17 84);
168
+ --secondary-hover:oklch(0.31 0.006 286.033);
169
+ --focus:var(--primary);
170
+ --focus-strong:var(--primary);
171
+ --primary-emphasis:var(--primary);
172
+ --primary-outline:var(--primary);
173
+ --control-selected:var(--primary);
174
+ --control-selected-foreground:var(--primary-foreground);
175
+ --selection:oklch(0.795 0.184 86.047 / 0.22);
176
+ --success-bg:oklch(0.42 0.11 150 / 0.36);
177
+ --warning-bg:oklch(0.34 0.08 86 / 0.64);
178
+ --danger-bg:oklch(0.32 0.1 22 / 0.62);
179
+ --info-bg:oklch(0.29 0.08 260 / 0.62);
180
+ }
181
+ [data-theme="light"]{
182
+ --background:oklch(0.965 0 0);
183
+ --foreground:oklch(0.145 0 0);
184
+ --card:oklch(1 0 0);
185
+ --card-foreground:oklch(0.145 0 0);
186
+ --popover:oklch(1 0 0);
187
+ --popover-foreground:oklch(0.145 0 0);
188
+ --primary:var(--brand-yellow);
189
+ --primary-foreground:var(--brand-yellow-foreground);
190
+ --secondary:oklch(0.94 0 0);
191
+ --secondary-foreground:oklch(0.21 0.006 285.885);
192
+ --muted:var(--secondary);
193
+ --muted-foreground:oklch(0.43 0 0);
194
+ --accent:var(--secondary);
195
+ --accent-foreground:oklch(0.205 0 0);
196
+ --destructive:oklch(0.577 0.245 27.325);
197
+ --destructive-foreground:oklch(0.985 0 0);
198
+ --border:oklch(0.89 0 0);
199
+ --input:oklch(0.922 0 0);
200
+ --ring:oklch(0.708 0 0);
201
+ --chart-1:oklch(0.809 0.105 251.813);
202
+ --chart-2:oklch(0.623 0.214 259.815);
203
+ --chart-3:oklch(0.546 0.245 262.881);
204
+ --chart-4:oklch(0.488 0.243 264.376);
205
+ --chart-5:oklch(0.424 0.199 265.638);
206
+ --sidebar:var(--card);
207
+ --sidebar-foreground:oklch(0.145 0 0);
208
+ --sidebar-primary:var(--brand-yellow);
209
+ --sidebar-primary-foreground:var(--brand-yellow-foreground);
210
+ --sidebar-accent:var(--secondary);
211
+ --sidebar-accent-foreground:oklch(0.205 0 0);
212
+ --sidebar-border:oklch(0.89 0 0);
213
+ --sidebar-ring:oklch(0.708 0 0);
214
+ --surface-1:var(--card);
215
+ --surface-2:var(--secondary);
216
+ --surface-3:var(--muted);
217
+ --surface-inset:oklch(0.955 0 0);
218
+ --surface-hover:oklch(0.925 0 0);
219
+ --foreground-strong:var(--foreground);
220
+ --subtle-foreground:oklch(0.43 0 0);
221
+ --border-strong:oklch(0.56 0 0);
222
+ --field-bg:var(--muted);
223
+ --overlay:oklch(0.145 0 0 / 0.4);
224
+ --primary-hover:oklch(0.835 0.19 89);
225
+ --primary-active:oklch(0.74 0.17 84);
226
+ --secondary-hover:var(--surface-hover);
227
+ --focus:var(--primary);
228
+ --focus-strong:var(--brand-yellow-foreground);
229
+ --primary-emphasis:var(--brand-yellow-foreground);
230
+ --primary-outline:var(--brand-yellow-foreground);
231
+ --control-selected:var(--brand-yellow-foreground);
232
+ --control-selected-foreground:oklch(0.985 0 0);
233
+ --success-400:#0f6b3e;
234
+ --warning-400:#6d4d00;
235
+ --danger-400:#9f2330;
236
+ --info-400:#164f9d;
237
+ --oracle-300:var(--chart-4);
238
+ --oracle-400:var(--chart-4);
239
+ --selection:oklch(0.795 0.184 86.047 / 0.18);
240
+ --success-bg:oklch(0.94 0.05 150);
241
+ --warning-bg:oklch(0.95 0.06 86);
242
+ --danger-bg:oklch(0.95 0.05 22);
243
+ --info-bg:oklch(0.95 0.045 260);
244
+ }
245
+ [data-density="compact"]{
246
+ --control-h-xs:1.5rem;
247
+ --control-h-sm:1.75rem;
248
+ --control-h-md:2rem;
249
+ --control-h-lg:2.375rem;
250
+ --control-h-xl:2.75rem;
251
+ --row-h:2.625rem;
252
+ --card-pad:1rem;
253
+ --section-gap:1.125rem;
254
+ }
255
+ [data-density="comfortable"]{
256
+ --control-h-xs:1.625rem;
257
+ --control-h-sm:1.875rem;
258
+ --control-h-md:2.25rem;
259
+ --control-h-lg:2.625rem;
260
+ --control-h-xl:3.125rem;
261
+ --row-h:3rem;
262
+ --card-pad:1.25rem;
263
+ --section-gap:1.5rem;
264
+ }
265
+ [data-density="spacious"]{
266
+ --control-h-xs:1.75rem;
267
+ --control-h-sm:2.125rem;
268
+ --control-h-md:2.5rem;
269
+ --control-h-lg:3rem;
270
+ --control-h-xl:3.5rem;
271
+ --row-h:3.375rem;
272
+ --card-pad:1.5rem;
273
+ --section-gap:2rem;
274
+ }
275
+ /* Aurea core — component styles. Built with @aurea-uds/tokens into dist/aurea.css. Load @aurea-uds/fonts/css for IBM Plex. */
276
+ /* ========================================================================== */
277
+ /* 02. RESET, ACESSIBILIDADE E BASE */
278
+ /* ========================================================================== */
279
+ * { box-sizing:border-box; }
280
+ html { scroll-behavior:smooth; }
281
+ body { margin:0; background:var(--background); color:var(--foreground); font-family:var(--font-ui); font-size:var(--text-md); line-height:var(--leading-normal); }
282
+ ::selection { background:var(--selection); }
283
+ button,input,textarea,select { font:inherit; }
284
+ button,a,[role="button"],input,textarea,select { outline:none; }
285
+ :focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; box-shadow:none; border-color:var(--focus-strong)!important; }
286
+ button { color:inherit; }
287
+ a { color:var(--primary); text-underline-offset:3px; }
288
+ img,svg { display:block; }
289
+ /* O `display:none` que o browser dá pro atributo `hidden` vem da folha do UA e PERDE de
290
+ qualquer `display:` de classe nossa — o painel some do fluxo de acessibilidade mas
291
+ continua na tela. Já mordeu o popover do MediaPlayer e as abas do catálogo. Aqui a
292
+ regra fecha a classe inteira do bug, em vez de um remendo por componente.
293
+ `until-found` fica de fora: ali o `hidden` é revelável e o browser cuida. */
294
+ [hidden]:not([hidden="until-found"]) { display:none!important; }
295
+ .sr-only { position:absolute!important; width:1px!important; height:1px!important; padding:0!important; margin:-1px!important; overflow:hidden!important; clip:rect(0,0,0,0)!important; white-space:nowrap!important; border:0!important; }
296
+ .icon { width:var(--icon-md); height:var(--icon-md); fill:currentColor; flex:none; }
297
+ .icon-sm { width:var(--icon-sm); height:var(--icon-sm); } .icon-lg { width:var(--icon-lg); height:var(--icon-lg); } .icon-xl { width:var(--icon-xl); height:var(--icon-xl); }
298
+ .muted { color:var(--muted-foreground); }
299
+ .stack { display:flex; flex-direction:column; gap:var(--space-4); }
300
+ .cluster { display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); }
301
+ /* Grid: o terceiro primitivo de layout, com o mesmo gap do .stack. A pele nasceu na Fase 11
302
+ (achado A13): o componente existia desde sempre e saía `display:block` — empilhava. Ninguém
303
+ via porque o chrome do catálogo REDEFINIA .grid, então ele parecia pronto na nossa
304
+ documentação e chegava sem nada no consumidor. `min(...,100%)` porque `minmax(15rem,1fr)`
305
+ estoura o contêiner quando o texto vai a 200% e a coluna fica maior que a caixa (lição do
306
+ M12); --grid-min é o botão de ajuste, no idioma de --qr-module e --demo-height. */
307
+ .grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(min(var(--grid-min,15rem),100%),1fr)); gap:var(--space-4); }
308
+
309
+ /* ========================================================================== */
310
+ /* 03. APP SHELL E DOCUMENTAÇÃO */
311
+ /* ========================================================================== */
312
+ /* Regiões do AppShell — SUPERFÍCIES FLUTUANTES (decisão do Victor, 23/07/2026): topo,
313
+ lateral e conteúdo são caixas soltas de raio --radius-card, separadas por --space-4 e
314
+ descoladas da borda da janela pelo padding do shell. NADA de barra colada com linha
315
+ divisória: caixa retangular genérica é proibida (CLAUDE.md).
316
+ Arranjo escolhido: o TOPO atravessa toda a largura (grid-column 1/-1) e leva a marca;
317
+ a lateral começa abaixo dele. Ambos grudam na rolagem por `sticky` — o conteúdo passa
318
+ por baixo e as duas caixas seguem visíveis.
319
+ ATENÇÃO: `.app-shell` NÃO se toca. O catálogo escrito à mão (apps/docs) usa essa MESMA
320
+ classe como raiz, com filhos próprios (.doc-sidebar/.main-wrap) — mexer nela desloca as
321
+ 26 telas de referência inteiras (aconteceu; o portão visual pegou). Por isso a folga
322
+ entre as caixas sai da MARGEM de cada filho, não de gap/padding no shell. As linhas
323
+ extras do grid nascem sozinhas por auto-placement. */
324
+ .app-shell { min-height:100vh; display:grid; grid-template-columns:var(--sidebar-width) minmax(0,1fr); background:var(--background); }
325
+ /* A base do topo carrega só o ARRANJO; cada variante traz a própria superfície.
326
+ floating = caixa solta (a que já estava) · flush = faixa encostada nas bordas com fio
327
+ embaixo, para o topo virar moldura da janela · pill = cápsula centrada, de cabeçalho
328
+ de site. Só floating e flush entram no grid do AppShell; a pill é peça avulsa (numa
329
+ pílula não cabem busca + densidade + tema + comando). */
330
+ .topbar { grid-column:1/-1; position:sticky; z-index:var(--z-sticky); display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); min-height:var(--topbar-height); }
331
+ .topbar-floating { top:var(--space-4); margin:var(--space-4) var(--space-4) 0; padding:var(--space-2) var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); }
332
+ /* flush: encostada nas bordas. SEM fio embaixo (pedido do Victor — a referência não tem)
333
+ e sem superfície própria: ela se funde ao fundo e cola no conteúdo, que começa logo
334
+ abaixo. É o topo do nosso catálogo. */
335
+ .topbar-flush { top:0; padding:var(--space-2) var(--space-5); background:var(--background); }
336
+ .topbar-pill { top:var(--space-4); width:max-content; max-width:calc(100% - var(--space-8)); margin:var(--space-4) auto 0; justify-content:flex-start; gap:var(--space-2); padding:var(--space-2); border:1px solid var(--border); border-radius:var(--radius-control); background:var(--card); color:var(--card-foreground); }
337
+ /* .brand nasceu como cabeçalho VERTICAL da lateral (borda embaixo + respiro); no topo
338
+ ele deita, então some com a borda e a margem — o lockup em si continua o mesmo. */
339
+ .topbar .brand { min-height:auto; margin:0; padding:0; border:0; }
340
+ /* altura fixa = janela menos topo e as 3 folgas; assim a caixa parece cheia mesmo com
341
+ poucos itens (é o que as referências mostram) e sobra o que rolar por dentro. */
342
+ .sidebar { --shell-top:calc(var(--topbar-height) + var(--space-4)*2); position:sticky; top:var(--shell-top); align-self:start; height:calc(100vh - var(--shell-top) - var(--space-4)); margin:var(--space-4); display:flex; flex-direction:column; gap:var(--space-1); padding:var(--space-4); overflow:auto; scrollbar-width:none; border:1px solid var(--sidebar-border); border-radius:var(--radius-card); background:var(--sidebar); color:var(--sidebar-foreground); }
343
+ /* a lateral ROLA mas nunca mostra a barra (pedido do Victor): a barra nativa risca a
344
+ superfície flutuante e não segue o raio de 22px. `scrollbar-width` cobre Firefox e
345
+ Chrome 121+; o pseudo cobre Safari e Chrome antigo. */
346
+ .sidebar::-webkit-scrollbar { width:0; height:0; }
347
+ /* com o topo `flush` a barra COLA no conteúdo (pedido do Victor): a folga acima da lateral
348
+ e do conteúdo é --space-1 (4px), e o --shell-top acompanha. */
349
+ /* flush cola no conteúdo com --space-05 (2px) — o degrau mais apertado da escala, criado
350
+ pra este caso (o Victor aprovou os 2px; tokenizado, não cru). */
351
+ .app-shell-flush .sidebar { --shell-top:calc(var(--topbar-height) + var(--space-05)); margin-block-start:var(--space-05); }
352
+ .app-shell-flush .content { margin-block-start:var(--space-05); }
353
+ .content { min-width:0; margin:var(--space-4) var(--space-4) var(--space-4) 0; padding:var(--space-6); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); }
354
+ /* O topo é presentational — não conhece o conteúdo — então garante o mínimo universal:
355
+ os filhos encolhem em vez de forçar rolagem lateral. O COLAPSO de conteúdo (esconder a
356
+ busca, trocar a nav por menu) é do consumidor, com os utilitários responsivos abaixo. */
357
+ .topbar > * { min-width:0; }
358
+ /* QUEBRA POR CONTEÚDO. O `flex-wrap` vivia só dentro do @media abaixo, ou seja, a barra só
359
+ quebrava quando a JANELA era estreita. Texto a 200% (WCAG 1.4.4) aumenta o conteúdo sem
360
+ mexer no viewport: o @media não disparava e a nav passava 27px da borda direita, em todas
361
+ as páginas. Quebrar por conteúdo cobre os dois casos e não muda nada quando cabe. */
362
+ .topbar,.topbar > nav { flex-wrap:wrap; }
363
+ /* Colapso do shell abaixo de md (767 = md-1, escala): a lateral deixa de flutuar e de ter
364
+ altura de janela — empilha acima do conteúdo, que passa a ocupar a largura toda. Sem
365
+ estado (o off-canvas com botão é refino da Fase 3); o essencial é nunca rolar de lado. */
366
+ /* Abaixo de md o topo QUEBRA em duas linhas em vez de esconder a navegação: perder o acesso
367
+ a uma área inteira não é colapso responsivo, é bug. A nav vai pra segunda linha, centrada
368
+ e na largura toda; badge e selo (secundários) seguem escondidos pelos utilitários. */
369
+ @media (max-width:767px) { .app-shell { grid-template-columns:minmax(0,1fr); } .sidebar { position:static; height:auto; } .content { margin-inline-start:var(--space-4); }
370
+ .topbar { flex-wrap:wrap; row-gap:var(--space-2); }
371
+ .topbar > nav { order:3; flex-basis:100%; justify-content:center; }
372
+ /* e a nav quebra entre os PRÓPRIOS itens: mandar a barra pra segunda linha não bastava,
373
+ as cinco áreas somam 371px e estouravam a 320px (auditoria 26/07, A1). */
374
+ .topbar > nav { flex-wrap:wrap; row-gap:var(--space-1); } }
375
+
376
+ /* ── GAVETA DE NAVEGAÇÃO (achado A8) ───────────────────────────────────────────────
377
+ Empilhar a lateral acima do conteúdo passava, no gate de rolagem lateral, e era
378
+ inutilizável: medido em 375px, o `h1` da página começava 4,6 telas abaixo, atrás dos 65
379
+ itens da navegação. Abaixo de md ela sai do fluxo e vira gaveta.
380
+ SEM UMA LINHA DE JS: o <aside> é um popover nativo e o botão do topo é o invoker. O
381
+ navegador entrega Escape, clique fora para fechar, `aria-expanded` no disparador, a volta
382
+ do foco ao fechar e o ::backdrop. Baseline desde 04/2025; verificado em 30/07/2026.
383
+ Não-modal de propósito (o foco não fica preso), como o CommandPaletteShell: declarar
384
+ modalidade sem entregá-la é pior que não declarar. */
385
+ .btn.nav-toggle { display:none; }
386
+ /* O UA aplica um bloco inteiro a `[popover]` mesmo FECHADO — `width:fit-content`, `inset:0`,
387
+ `margin:auto`, borda e padding próprios. A `.sidebar` já sobrescreve quase tudo; sobravam
388
+ largura e insets, e isso encolheu a lateral do DESKTOP de 232px para 225px. Quem pegou foi o
389
+ gate de pixel, não o olho: 4.573 pixels de diferença numa captura de 0,03 de proporção. */
390
+ .sidebar[popover]:not(:popover-open) { width:auto; inset-inline:auto; inset-block-end:auto; }
391
+ /* Quem esconde a gaveta fechada é ESTA regra, não o `display:none` do UA para `[popover]`:
392
+ regra de autor vence regra de UA por ORIGEM, e a `.sidebar` já declara `display:flex`.
393
+ Medido: sem esta linha a lateral seguia empilhada em 375px, com o botão já visível.
394
+ O @supports é a rede: em navegador sem popover a regra inteira não aplica, a lateral volta a
395
+ empilhar (o comportamento de antes, feio e usável) em vez de sumir sem botão que a abra. */
396
+ @supports selector(:popover-open) {
397
+ @media (max-width:767px) {
398
+ .sidebar[popover]:not(:popover-open) { display:none; }
399
+ .btn.nav-toggle { display:inline-flex; }
400
+ /* altura EXPLÍCITA, não `auto`: medido em 30/07/2026, com `top` e `bottom` postos e
401
+ `height:auto` o Chromium resolveu pela altura do CONTEÚDO (3.498px de lateral rolando a
402
+ página inteira), não pelas âncoras. `dvh` porque a barra do navegador móvel some e volta. */
403
+ .sidebar[popover]:popover-open { position:fixed; inset-block:var(--space-4); inset-inline-start:var(--space-4); width:min(20rem,calc(100vw - var(--space-8))); height:calc(100dvh - var(--space-4) * 2); margin:0; }
404
+ .sidebar[popover]::backdrop { background:var(--overlay); }
405
+ }
406
+ }
407
+
408
+ /* ── Visibilidade responsiva — o mecanismo PADRÃO de colapsar uma barra ─────────────
409
+ Regra do projeto (enraizada): todo layout horizontal que aperta esconde o secundário
410
+ por breakpoint da escala, nunca rola de lado. Estas 4 classes são esse mecanismo; o
411
+ gate (check 4b) trava os breakpoints. Mobile-first: `below` some acima do ponto. */
412
+ @media (max-width:1023px) { .hide-below-lg { display:none !important; } }
413
+ @media (max-width:767px) { .hide-below-md { display:none !important; } }
414
+ .brand { min-height:var(--topbar-height); display:flex; align-items:center; gap:12px; padding:2px 4px 16px; margin:0 0 16px; border:0; border-bottom:1px solid var(--sidebar-border); border-radius:0; background:transparent; box-shadow:none; }
415
+ .brand strong { display:block; letter-spacing:var(--tracking-tight); } .brand small { display:block; color:var(--muted-foreground); font-size:11px; }
416
+ /* minmax(0,1fr): a trilha `auto` do grid crescia até o nome mais longo e os itens
417
+ VAZAVAM pela direita da caixa (colavam na borda de um lado e sobrava do outro). */
418
+ .doc-nav { padding:0 0 8px; display:grid; grid-template-columns:minmax(0,1fr); gap:var(--space-1); }
419
+ .btn.mobile-nav-toggle,.btn.mobile-nav-close { display:none; }
420
+ /* .nav-group-label é o rótulo de categoria SEM ser título de documento: mesma aparência do
421
+ h4 histórico, mas o grupo se nomeia por aria-labelledby e não entra na lista de títulos
422
+ do leitor de tela (senão a lateral vem antes do h1 da página). É o jeito preferido. */
423
+ .doc-nav h4,.doc-nav .nav-group-label { margin:16px 8px 8px; color:var(--subtle-foreground); font-size:11px; letter-spacing:var(--tracking-widest); text-transform:uppercase; }
424
+ .doc-nav .nav-group { display:grid; gap:var(--space-1); }
425
+ .doc-nav a { display:flex; align-items:center; gap:10px; min-height:40px; margin:0; padding:9px 12px; border:1px solid transparent; border-radius:var(--radius-lg); background:transparent; color:var(--muted-foreground); text-decoration:none; box-shadow:none; }
426
+ .doc-nav a:hover { color:var(--sidebar-accent-foreground); background:var(--sidebar-accent); border-color:var(--sidebar-border); }
427
+ .doc-nav a.active { border-color:var(--sidebar-border); }
428
+ /* o glifo da família fica na frente do rótulo e nunca encolhe o texto */
429
+ .doc-nav a .icon { flex:none; opacity:.8; }
430
+ .doc-nav a.active .icon { opacity:1; }
431
+ .eyebrow { display:inline-flex; align-items:center; gap:8px; color:var(--primary); font-size:12px; font-weight:var(--weight-bold); letter-spacing:var(--tracking-widest); text-transform:uppercase; }
432
+ /* No claro o amarelo puro como texto não alcança 4.5:1 (axe). Aprofundar com --foreground
433
+ mantém o matiz da marca e passa o AA; no escuro segue o amarelo cheio. */
434
+ [data-theme="light"] .eyebrow { color:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
435
+ .demo { min-width:0; padding:var(--card-pad); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); box-shadow:none; }
436
+
437
+ /* ========================================================================== */
438
+ /* 04. COMPONENTES PRIMITIVOS */
439
+ /* ========================================================================== */
440
+ .btn { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:transparent; display:inline-flex; align-items:center; justify-content:center; gap:8px; height:var(--control-h-md); padding:0 15px; border:1px solid var(--btn-border); border-radius:var(--radius-control); background:var(--btn-bg); color:var(--btn-fg); font-size:var(--text-sm); font-weight:var(--weight-medium); line-height:var(--leading-none); text-decoration:none; white-space:nowrap; cursor:pointer; transition:transform var(--duration-fast) var(--ease-standard),background var(--duration-fast),border-color var(--duration-fast),color var(--duration-fast),opacity var(--duration-fast); }
441
+ .btn:hover:not(:disabled) { transform:none; } .btn:active:not(:disabled) { transform:scale(.97); opacity:.9; }
442
+ .btn:disabled { opacity:.45; cursor:not-allowed; }
443
+ /* Cinco alturas, todas de token de densidade (autorizado pelo Victor em 24/07/2026). O raio
444
+ é pill em TODAS — o tamanho muda altura e respiro, nunca a forma. Em ponteiro grosso o
445
+ mínimo de 44px lá 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. Só 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 só ────────────────────────────────────────────────────
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 dá 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 já 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 dá 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 já existia aqui sob o nome
774
+ `.command` — e `.command` é chrome da página de docs manual: nenhum componente React a
775
+ emite. Ela passava no check 15 por ACIDENTE, porque o rótulo padrão "Type a command…" põe
776
+ a palavra "command" numa string do pacote React e o gate lê as strings do fonte. Foi para
777
+ apps/docs/docs.css, que é a casa dela; o sistema fica com os dois nomes que o componente
778
+ emite de verdade. Não-modal de propósito, como a gaveta do AppShell (ADR-0003): o foco não
779
+ fica preso, então não declaramos modalidade que não entregamos. */
780
+ .command-overlay { position:fixed; inset:0; z-index:var(--z-modal); display:grid; place-items:start center; padding:var(--space-16) var(--space-5) var(--space-5); background:var(--overlay); backdrop-filter:blur(5px); }
781
+ .command-palette { display:grid; gap:var(--space-2); width:min(680px,100%); max-height:100%; overflow:auto; padding:var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
782
+ /* Camadas flutuantes (Base UI): tooltip, popover, menu. z-modal para funcionarem sobre diálogos; empilhamento mútuo pela ordem do portal no DOM. */
783
+ .tooltip { z-index:var(--z-modal); max-width:260px; padding:6px 10px; border:1px solid var(--border); border-radius:var(--radius-md); background:var(--surface-inset); color:var(--foreground); font-size:var(--text-xs); line-height:var(--leading-normal); box-shadow:var(--shadow-md); }
784
+ .popover { z-index:var(--z-modal); min-width:220px; max-width:min(360px,calc(100vw - 32px)); padding:16px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); display:grid; gap:10px; }
785
+ .menu { z-index:var(--z-modal); min-width:200px; max-width:min(320px,calc(100vw - 32px)); padding:6px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
786
+ .menu-item { display:flex; align-items:center; gap:10px; width:100%; min-height:var(--control-h-md); padding:0 10px; border:0; border-radius:var(--radius-sm); background:transparent; color:var(--foreground); font:inherit; text-align:start; cursor:pointer; }
787
+ .menu-item[data-highlighted],.menu-item:hover:not([data-disabled]) { background:var(--surface-3); outline:none; }
788
+ .menu-item[data-disabled] { opacity:.5; cursor:not-allowed; }
789
+ .menu-sep { height:1px; margin:6px 4px; background:var(--border); border:0; }
790
+ /* Combobox: reaproveita .menu/.menu-item; 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) — só a UI de entrada; upload real é a Fase 5.
814
+ A zona é um <label> em volta do <input> nativo: clique/foco/teclado de graça,
815
+ o arrastar-soltar é reforço. Espaçamento novo tokenizado; superfície = card. */
816
+ .dropzone { display:flex; flex-direction:column; align-items:center; gap:var(--space-2); padding:var(--space-8) var(--space-6); text-align:center; color:var(--muted-foreground); background:var(--surface-inset); border:1px dashed var(--border-strong); border-radius:var(--radius-card); cursor:pointer; transition:border-color var(--duration-fast),background-color var(--duration-fast); }
817
+ .dropzone:hover { border-color:var(--border); }
818
+ .dropzone:focus-within { outline:2px solid var(--focus-strong); outline-offset:2px; border-color:var(--focus-strong); }
819
+ .dropzone[data-dragging="true"] { border-style:solid; border-color:var(--primary); background:var(--surface-2); }
820
+ .dropzone strong { color:var(--foreground); }
821
+ .dropzone-icon { color:var(--primary); }
822
+ .dropzone-hint { font-size:var(--text-xs); }
823
+ .file-list { display:flex; flex-direction:column; gap:var(--space-2); margin-top:var(--space-3); padding:0; list-style:none; }
824
+ .file-item { display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2) var(--space-3); background:var(--surface-2); border:1px solid var(--border); border-radius:var(--radius-md); }
825
+ .file-progress { flex-basis:var(--space-20); flex-shrink:0; } /* largura fixa da barra de upload (auditoria 18/07, M14) */
826
+ .file-name { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
827
+ .file-size { flex:none; color:var(--muted-foreground); font-size:var(--text-xs); font-variant-numeric:tabular-nums; }
828
+
829
+ /* TreeView (Fase 4) — árvore headless própria (Base UI 1.6 não tem tree).
830
+ role=tree/treeitem/group + roving tabindex vêm do React; aqui só a pele.
831
+ A indentação por nível é padding-inline-start inline (style). Espaço novo
832
+ tokenizado; a linha não é pill (radius-sm, como o .menu-item). */
833
+ .tree, .tree-group { margin:0; padding:0; list-style:none; }
834
+ .tree-item { outline:none; }
835
+ .tree-node { display:flex; align-items:center; gap:var(--space-2); min-height:var(--control-h-md); padding-block:var(--space-1); padding-inline-end:var(--space-3); border-radius:var(--radius-sm); color:var(--foreground); cursor:pointer; user-select:none; }
836
+ .tree-node:hover { background:var(--surface-3); }
837
+ .tree-item:focus-visible > .tree-node { outline:2px solid var(--focus-strong); outline-offset:-2px; }
838
+ .tree-node[data-selected] { background:color-mix(in srgb,var(--primary) 14%,transparent); }
839
+ .tree-twist { flex:none; color:var(--muted-foreground); transition:transform var(--duration-fast); }
840
+ .tree-item[aria-expanded="true"] > .tree-node > .tree-twist { transform:rotate(90deg); }
841
+ .tree-item[aria-expanded="false"]:dir(rtl) > .tree-node > .tree-twist { transform:rotate(180deg); }
842
+ .tree-indent { flex:none; width:1rem; }
843
+ .tree-label { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
844
+
845
+ /* NotificationCenter (Fase 4) — compõe o .popover; aqui só o gatilho com count,
846
+ o cabeçalho e as linhas do feed. Espaço novo tokenizado; a linha usa radius-sm
847
+ (não é pill, como o .menu-item/.tree-node). O painel NÃO é live region — a
848
+ chegada é anunciada por um role=status à parte no React. */
849
+ .notification-trigger { position:relative; display:inline-flex; }
850
+ .notification-count { position:absolute; inset-block-start:-4px; inset-inline-end:-4px; display:inline-flex; align-items:center; justify-content:center; min-width:18px; height:18px; padding:0 var(--space-1); border-radius:var(--radius-control); background:var(--primary); color:var(--primary-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); line-height:var(--leading-none); pointer-events:none; }
851
+ .notification-panel { width:min(360px,calc(100vw - 32px)); max-width:none; padding:0; gap:0; display:flex; flex-direction:column; overflow:hidden; }
852
+ .notification-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); padding:var(--space-3) var(--space-4); border-block-end:1px solid var(--border); }
853
+ .notification-head strong { font-size:var(--text-sm); }
854
+ .notification-list { margin:0; padding:var(--space-1); overflow-y:auto; max-height:min(60vh,440px); }
855
+ .notification-sublist { margin:0; padding:0; list-style:none; }
856
+ .notification-group-label { margin:0; padding:var(--space-2) var(--space-3) var(--space-1); color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); letter-spacing:var(--tracking-wide); text-transform:uppercase; }
857
+ .notification-item { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:baseline; column-gap:var(--space-3); row-gap:var(--space-1); width:100%; padding:var(--space-3); border:0; border-radius:var(--radius-sm); background:none; color:var(--foreground); font:inherit; text-align:start; }
858
+ button.notification-item { cursor:pointer; }
859
+ button.notification-item:hover { background:var(--surface-3); }
860
+ .notification-item:focus-visible { outline:2px solid var(--focus-strong); outline-offset:-2px; }
861
+ .notification-dot { grid-row:1/-1; align-self:center; width:8px; height:8px; border-radius:50%; background:var(--primary); }
862
+ .notification-item[data-read] .notification-dot { visibility:hidden; }
863
+ .notification-item-title { grid-column:2; display:flex; align-items:center; gap:var(--space-2); min-width:0; font-size:var(--text-sm); font-weight:var(--weight-medium); }
864
+ .notification-item[data-read] .notification-item-title { color:var(--muted-foreground); font-weight:var(--weight-regular); }
865
+ .notification-item-desc { grid-column:2; color:var(--muted-foreground); font-size:var(--text-xs); }
866
+ .notification-time { grid-column:3; grid-row:1; color:var(--muted-foreground); font-size:var(--text-xs); white-space:nowrap; }
867
+ .notification-empty { margin:0; padding:var(--space-8) var(--space-4); color:var(--muted-foreground); font-size:var(--text-sm); text-align:center; }
868
+ /* EmptyState é o membro GRANDE da família "aqui não há nada" — .notification-empty acima e
869
+ .datagrid-empty abaixo já respondiam a mesma pergunta do mesmo jeito (centralizado, muted),
870
+ e o componente público era o único dos três sem regra (achado A13). A ordem das partes
871
+ (imagem → título → corpo → ação) é a do padrão de empty state do Carbon, registrado em
872
+ REFERENCES.md; a pele é Aurea. O título vinha com o h3 do NAVEGADOR (16,38px). */
873
+ .empty-state { display:flex; flex-direction:column; align-items:center; gap:var(--space-3); padding:var(--space-10) var(--space-6); color:var(--muted-foreground); text-align:center; }
874
+ .empty-state > .icon { color:var(--subtle-foreground); }
875
+ .empty-state p { max-width:46ch; margin:0; }
876
+ .empty-title { margin:0; color:var(--foreground); font-size:var(--text-base); font-weight:var(--weight-semibold); line-height:var(--leading-tight); }
877
+
878
+ /* DataGrid (Fase 4) — TanStack Table v8 headless; aqui só a pele, que reusa
879
+ .table-wrap/table/th/td. aria-sort fica só no th ordenado; a linha
880
+ selecionada usa data-selected (o estado acessível é o checkbox da linha).
881
+ Espaço novo tokenizado. */
882
+ .datagrid { display:grid; gap:var(--space-3); }
883
+ .datagrid > .table-wrap:focus-visible { outline:none; box-shadow:0 0 0 3px color-mix(in oklab,var(--focus) 38%,transparent); }
884
+ .datagrid > .pagination { justify-self:end; }
885
+ /* Fix aditivo: os 34px dos botões numéricos do .pagination esmagavam os botões
886
+ de TEXTO (Anterior/Próxima) do Pagination React. Não muda os numéricos. */
887
+ /* text-transform/letter-spacing: o UA reseta nos <button>; herdar explícito
888
+ para o cabeçalho ordenável ficar idêntico ao th estático. */
889
+ .datagrid-sort { display:inline-flex; align-items:center; gap:var(--space-1); padding:0; border:0; background:none; color:inherit; font:inherit; text-transform:inherit; letter-spacing:inherit; cursor:pointer; }
890
+ .datagrid-sort:focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; border-radius:var(--radius-sm); }
891
+ .datagrid-sort-icon { flex:none; color:var(--subtle-foreground); }
892
+ th[aria-sort] .datagrid-sort-icon { color:var(--primary); }
893
+ .datagrid-selcol { width:1%; white-space:nowrap; }
894
+ .datagrid tbody tr[data-selected] { background:color-mix(in srgb,var(--primary) 14%,transparent); }
895
+ .datagrid-empty { padding:var(--space-6); color:var(--muted-foreground); text-align:center; }
896
+
897
+ /* ========================================================================== */
898
+ /* 10. RESPONSIVIDADE */
899
+ /* ========================================================================== */
900
+ @media (max-width:1366px) and (min-width:821px) { :root { --sidebar-width:216px; } }
901
+ /* Aperto de tela baixa: é densidade do SHELL DOS DOCS (.doc-sidebar), não da .doc-nav em
902
+ si — sem o escopo ela zerava o gap de qualquer nav, inclusive a do catálogo. */
903
+ @media (max-height:800px) and (min-width:821px) { .doc-sidebar .brand { min-height:var(--target-min); padding:0 2px 8px; margin-bottom:6px; } .doc-sidebar .brand strong { font-size:var(--text-sm); } .doc-sidebar .brand small { font-size:10px; } .doc-sidebar .doc-nav { gap:0; padding-bottom:0; } .doc-sidebar .doc-nav h4 { margin:8px 4px 4px; font-size:10px; } .doc-sidebar .doc-nav a { min-height:30px; padding:5px 8px; font-size:var(--text-xs); } }
904
+ /* Legado do catálogo escrito à mão: `display:block` desmancha o grid e empilha a lateral.
905
+ Agora vale SÓ para o shell que não tem gaveta — o `apps/docs/index.html`, que traz a
906
+ própria `.doc-sidebar` e o próprio JS de abrir. O shell do sistema segue em grid de uma
907
+ coluna com a gaveta acima. Sai junto com aquela página (achado M13). */
908
+ @media (max-width:820px) { .app-shell:not(:has(> .sidebar[popover])) { display:block; } }
909
+ @media (max-width:820px) { .btn.mobile-nav-toggle { display:inline-flex; } .btn.mobile-nav-close { display:inline-flex; margin-inline-start:auto; } .mobile-nav-backdrop.open { display:block; } }
910
+ @media (max-width:640px) { :root { --card-pad:16px; --section-gap:16px; } .doc-actions .select { flex:1; width:auto!important; min-width:0; } .doc-actions>.btn-primary { width:var(--control-h-md); padding:0; font-size:0; } .doc-actions>.btn-primary .mono { display:none; } .tabs,.pagination,.segmented { max-width:100%; overflow-x:auto; } .step:nth-child(3):before { display:none; } .alert { grid-template-columns:auto minmax(0,1fr); } .alert>:last-child { grid-column:2; justify-self:start; } .log-line { grid-template-columns:70px 58px minmax(0,1fr); gap:6px; } .queue-item>.badge { grid-column:2; justify-self:start; } .event-row>.badge { grid-column:3; justify-self:start; } .message { grid-template-columns:1fr; } .media-player { min-height:330px; aspect-ratio:auto; } .media-overlay-title { top:12px; left:12px; right:12px; } .media-controls { padding:10px; } .media-control-row { align-items:flex-start; flex-direction:column; gap:8px; } .media-control-row>.media-control-group { width:100%; justify-content:space-between; } .media-optional,.media-volume { display:none; } .media-time { padding:0 4px; font-size:11px; } .toast-stack { inset-inline:12px; bottom:12px; } .toast { width:100%; min-width:0; grid-template-columns:minmax(0,1fr) auto; } .drawer { width:100%; padding:20px; } .dialog { padding:20px; } }
911
+ @media (prefers-reduced-motion:reduce) { *,*:before,*:after { scroll-behavior:auto!important; animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important; } }
912
+
913
+ /* Padrões compostos Aurea 1.7: blocos de operação, não widgets de domínio. */
914
+ .operational-grid .demo{min-height:0}@media(max-width:640px){.operational-grid .demo{grid-column:span 12}}
915
+ .universal-grid .demo { min-height:0; }
916
+ .pattern-toolbar .input-wrap { flex:1 1 180px; }
917
+ .media-library-row.active { border-color:var(--primary); }
918
+ /* Alvo de toque. `.tabs`/`.segmented` no lugar de `.tab`/`.segmented button`: agora a altura
919
+ mora na cápsula, então é ela que precisa crescer — o filho estica atrás. */
920
+ @media (pointer:coarse) { .btn:not(.btn-link),.tabs,.segmented,.pagination button,.schedule-grid button,.media-control { min-height:var(--target-min); } .btn-icon,.media-control { min-width:var(--target-min); } }
921
+ @media (forced-colors:active) {
922
+ :focus-visible { outline:2px solid CanvasText; outline-offset:2px; box-shadow:none; }
923
+ .badge,.pattern-gallery button[aria-pressed="true"],.device-grid button[aria-pressed="true"] { border:1px solid CanvasText; }
924
+ }
925
+
926
+
927
+ /* Aurea v1.7 stabilization patch — visual design unchanged. */
928
+ .badge-running{background:var(--success-bg);color:var(--success-400);border-color:color-mix(in oklab,var(--success-400) 32%,transparent)}
929
+ .table-region{overflow:auto;outline:none;border-radius:var(--radius-lg)}
930
+ .table-region:focus-visible{box-shadow:0 0 0 3px color-mix(in oklab,var(--focus) 38%,transparent)}
931
+ @media(max-width:400px){
932
+ .media-controls{flex-wrap:wrap;overflow:visible!important;gap:8px}
933
+ .media-controls .time-display{order:2;width:100%}
934
+ .media-control{min-width:var(--target-min);min-height:var(--target-min)}
935
+ }
936
+ @media(pointer:coarse){
937
+ .media-control,.btn-icon{min-width:var(--target-min);min-height:var(--target-min)}
938
+ }
939
+
940
+ /* ========================================================================== */
941
+ /* 12. RTL */
942
+ /* ========================================================================== */
943
+ /* O eixo inline do CSS acima já é 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); }
952
+ }