@deeeed/metamask-harness 0.28.0 → 0.29.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +41 -0
  3. package/adapters/extension/build-lavamoat.sh +2 -1
  4. package/adapters/extension/ensure-browser.sh +82 -9
  5. package/adapters/extension/inject.mjs +1 -0
  6. package/adapters/extension/launch-browser.cjs +83 -1
  7. package/adapters/extension/lib/chrome-args.cjs +325 -1
  8. package/adapters/extension/lib/playwright-cdp.cjs +34 -0
  9. package/adapters/extension/lib/slot-title.cjs +2 -4
  10. package/adapters/extension/lib/validation-launch-supervisor.cjs +292 -0
  11. package/adapters/extension/lib/validation-process-ownership.cjs +69 -0
  12. package/adapters/extension/reattach.sh +2 -1
  13. package/adapters/extension/sidepanel-toggle.sh +14 -96
  14. package/adapters/extension/wallet-fixture-state.cjs +8 -31
  15. package/adapters/manifest.json +16 -0
  16. package/adapters/shared/private-atomic-write.cjs +47 -0
  17. package/adapters/shared/setup-base.sh +864 -0
  18. package/dist/adapters/extension/runtime.js +367 -24
  19. package/dist/adapters/extension/validation-process-ownership.js +10 -0
  20. package/dist/cli-commands.js +1 -0
  21. package/dist/command-contract.js +12 -0
  22. package/dist/commands/launch/extension.js +130 -19
  23. package/dist/commands/setup-base.js +24 -0
  24. package/dist/mm-harness-cli.js +28 -2
  25. package/library/actions/extension/analytics/consent.mjs +203 -0
  26. package/library/actions/extension/analytics/set_consent.mjs +19 -143
  27. package/library/actions/extension/perps/perps.mjs +2 -16
  28. package/library/actions/extension/perps/state.mjs +20 -0
  29. package/library/actions/extension/wallet/list_accounts.mjs +3 -25
  30. package/library/actions/extension/wallet/read_state.mjs +3 -23
  31. package/library/actions/extension/wallet/select_account.mjs +6 -33
  32. package/library/actions/extension/wallet/setup.mjs +2 -20
  33. package/library/actions/extension/wallet/state.mjs +111 -0
  34. package/library/recipes/runner/action-validation.extension.recipe.json +1 -1
  35. package/library/recipes/runner/action-validation.mobile.recipe.json +1 -1
  36. package/package.json +7 -4
  37. package/scripts/site-contrast.mjs +538 -0
  38. package/site/architecture.html +415 -0
  39. package/site/assets/progress.mjs +272 -0
  40. package/site/assets/style.css +808 -0
  41. package/site/cheatsheet.html +305 -0
  42. package/site/index.html +643 -0
  43. package/site/recipes.html +396 -0
  44. package/site/reviewers.html +374 -0
  45. package/site/tutorials/index.html +180 -0
  46. package/site/tutorials/v1.html +211 -0
  47. package/site/tutorials/v2.html +207 -0
  48. package/site/tutorials/v3.html +214 -0
  49. package/site/tutorials/v4.html +195 -0
  50. package/site/tutorials/v5.html +163 -0
  51. package/site/tutorials/v6.html +165 -0
  52. package/site/tutorials/v7.html +184 -0
@@ -0,0 +1,808 @@
1
+ /* mm-harness getting-started site — single stylesheet, no external assets. */
2
+
3
+ :root {
4
+ --bg: #0e0f13;
5
+ --bg-raised: #15171d;
6
+ --surface: #1a1d25;
7
+ --surface-2: #20242e;
8
+ --line: #2b3040;
9
+ --line-soft: #23272f;
10
+
11
+ /* Every text token clears WCAG AA (4.5:1) against the darkest-to-lightest
12
+ surfaces above; --text-faint is the floor at 5.2:1 on --surface-2. */
13
+ --text: #e9eaf0;
14
+ --text-dim: #b0b6c6;
15
+ --text-faint: #9096a8;
16
+
17
+ --accent: #f6851b;
18
+ --accent-deep: #d4700f;
19
+ --accent-soft: rgba(246, 133, 27, 0.14);
20
+ --accent-line: rgba(246, 133, 27, 0.34);
21
+
22
+ --pass: #58d68a;
23
+ --fail: #f0736b;
24
+ --warn: #f0c05a;
25
+ --info: #6aa9f0;
26
+
27
+ --radius: 12px;
28
+ --radius-sm: 8px;
29
+ --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 28px rgba(0, 0, 0, 0.28);
30
+ --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
31
+
32
+ --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
33
+ Arial, sans-serif;
34
+ --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
35
+ "Liberation Mono", monospace;
36
+ }
37
+
38
+ *, *::before, *::after { box-sizing: border-box; }
39
+
40
+ html { scroll-behavior: smooth; scroll-padding-top: 92px; }
41
+
42
+ body {
43
+ margin: 0;
44
+ background: var(--bg);
45
+ color: var(--text);
46
+ font-family: var(--sans);
47
+ font-size: 16px;
48
+ line-height: 1.65;
49
+ -webkit-font-smoothing: antialiased;
50
+ text-rendering: optimizeLegibility;
51
+ }
52
+
53
+ /* Ambient wash — keeps the page from reading as a flat README. */
54
+ body::before {
55
+ content: "";
56
+ position: fixed;
57
+ inset: 0;
58
+ pointer-events: none;
59
+ z-index: 0;
60
+ background:
61
+ radial-gradient(760px 380px at 12% -8%, rgba(246, 133, 27, 0.10), transparent 60%),
62
+ radial-gradient(620px 320px at 92% 4%, rgba(106, 169, 240, 0.06), transparent 62%);
63
+ }
64
+
65
+ body > * { position: relative; z-index: 1; }
66
+
67
+ a { color: var(--accent); text-decoration: none; }
68
+ a:hover { text-decoration: underline; text-underline-offset: 3px; }
69
+
70
+ h1, h2, h3, h4 { line-height: 1.22; letter-spacing: -0.021em; margin: 0 0 0.5em; }
71
+ h1 { font-size: clamp(2rem, 4.4vw, 2.9rem); font-weight: 680; }
72
+ h2 { font-size: clamp(1.35rem, 2.4vw, 1.7rem); font-weight: 640; margin-top: 2.4em; }
73
+ h3 { font-size: 1.1rem; font-weight: 620; margin-top: 1.8em; }
74
+ p { margin: 0 0 1.1em; }
75
+ p, li { color: #d5d8e2; }
76
+
77
+ code {
78
+ font-family: var(--mono);
79
+ font-size: 0.885em;
80
+ background: var(--surface-2);
81
+ border: 1px solid var(--line-soft);
82
+ border-radius: 5px;
83
+ padding: 0.1em 0.38em;
84
+ color: #f0d9bd;
85
+ }
86
+
87
+ strong { color: var(--text); font-weight: 620; }
88
+
89
+ /* ---------- layout ---------- */
90
+
91
+ .wrap { width: min(940px, calc(100% - 3rem)); margin: 0 auto; }
92
+ .wrap-wide { width: min(1160px, calc(100% - 3rem)); margin: 0 auto; }
93
+
94
+ main { padding-bottom: 6rem; }
95
+
96
+ /* ---------- top bar ---------- */
97
+
98
+ .topbar {
99
+ position: sticky;
100
+ top: 0;
101
+ z-index: 50;
102
+ background: rgba(14, 15, 19, 0.86);
103
+ backdrop-filter: saturate(150%) blur(14px);
104
+ -webkit-backdrop-filter: saturate(150%) blur(14px);
105
+ border-bottom: 1px solid var(--line-soft);
106
+ }
107
+
108
+ .topbar-inner {
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 1.5rem;
112
+ height: 60px;
113
+ }
114
+
115
+ .brand {
116
+ display: inline-flex;
117
+ align-items: center;
118
+ gap: 0.6rem;
119
+ font-weight: 640;
120
+ color: var(--text);
121
+ letter-spacing: -0.015em;
122
+ white-space: nowrap;
123
+ }
124
+ .brand:hover { text-decoration: none; }
125
+
126
+ /* Brand-adjacent mark: a geometric diamond, not a product logo. */
127
+ .brand-mark {
128
+ width: 20px;
129
+ height: 20px;
130
+ flex: none;
131
+ border-radius: 5px;
132
+ background: linear-gradient(145deg, var(--accent) 0%, var(--accent-deep) 100%);
133
+ transform: rotate(45deg);
134
+ box-shadow: 0 0 0 1px rgba(246, 133, 27, 0.3), 0 3px 12px rgba(246, 133, 27, 0.22);
135
+ }
136
+
137
+ .brand-name { font-family: var(--mono); font-size: 0.95rem; }
138
+
139
+ .nav { display: flex; gap: 0.3rem; margin-left: auto; flex-wrap: wrap; }
140
+
141
+ .nav a {
142
+ color: var(--text-dim);
143
+ font-size: 0.885rem;
144
+ font-weight: 500;
145
+ padding: 0.4rem 0.7rem;
146
+ border-radius: var(--radius-sm);
147
+ transition: color 0.16s var(--ease), background 0.16s var(--ease);
148
+ }
149
+ .nav a:hover { color: var(--text); background: var(--surface); text-decoration: none; }
150
+ .nav a[aria-current="page"] { color: var(--accent); background: var(--accent-soft); }
151
+
152
+ /* ---------- progress rail ---------- */
153
+
154
+ .progress-rail {
155
+ position: sticky;
156
+ top: 60px;
157
+ z-index: 45;
158
+ background: rgba(14, 15, 19, 0.9);
159
+ backdrop-filter: blur(12px);
160
+ -webkit-backdrop-filter: blur(12px);
161
+ border-bottom: 1px solid var(--line-soft);
162
+ padding: 0.7rem 0;
163
+ }
164
+
165
+ .progress-inner { display: flex; align-items: center; gap: 1rem; }
166
+
167
+ .progress-track {
168
+ flex: 1;
169
+ height: 6px;
170
+ background: var(--surface-2);
171
+ border-radius: 99px;
172
+ overflow: hidden;
173
+ }
174
+
175
+ .progress-fill {
176
+ height: 100%;
177
+ width: 0%;
178
+ border-radius: 99px;
179
+ background: linear-gradient(90deg, var(--accent-deep), var(--accent));
180
+ transition: width 0.4s var(--ease);
181
+ }
182
+
183
+ .progress-label {
184
+ font-family: var(--mono);
185
+ font-size: 0.78rem;
186
+ color: var(--text-dim);
187
+ white-space: nowrap;
188
+ font-variant-numeric: tabular-nums;
189
+ }
190
+
191
+ .progress-reset {
192
+ font: inherit;
193
+ font-size: 0.78rem;
194
+ color: var(--text-faint);
195
+ background: none;
196
+ border: 1px solid var(--line);
197
+ border-radius: 99px;
198
+ padding: 0.22rem 0.7rem;
199
+ cursor: pointer;
200
+ white-space: nowrap;
201
+ transition: all 0.16s var(--ease);
202
+ }
203
+ .progress-reset:hover { color: var(--text); border-color: var(--accent-line); }
204
+
205
+ /* ---------- hero ---------- */
206
+
207
+ .hero { padding: 4.5rem 0 2.5rem; }
208
+
209
+ .eyebrow {
210
+ display: inline-block;
211
+ font-family: var(--mono);
212
+ font-size: 0.74rem;
213
+ letter-spacing: 0.1em;
214
+ text-transform: uppercase;
215
+ color: var(--accent);
216
+ background: var(--accent-soft);
217
+ border: 1px solid var(--accent-line);
218
+ border-radius: 99px;
219
+ padding: 0.3rem 0.8rem;
220
+ margin-bottom: 1.4rem;
221
+ }
222
+
223
+ .hero h1 { max-width: 20ch; }
224
+
225
+ .lede {
226
+ font-size: 1.14rem;
227
+ color: var(--text-dim);
228
+ max-width: 66ch;
229
+ line-height: 1.68;
230
+ }
231
+
232
+ .hero-meta {
233
+ display: flex;
234
+ flex-wrap: wrap;
235
+ gap: 0.5rem 1.4rem;
236
+ margin-top: 1.6rem;
237
+ font-family: var(--mono);
238
+ font-size: 0.8rem;
239
+ color: var(--text-faint);
240
+ }
241
+
242
+ /* ---------- cards ---------- */
243
+
244
+ .card {
245
+ background: var(--surface);
246
+ border: 1px solid var(--line-soft);
247
+ border-radius: var(--radius);
248
+ padding: 1.5rem 1.6rem;
249
+ transition: border-color 0.2s var(--ease), transform 0.2s var(--ease);
250
+ }
251
+
252
+ .card h3 { margin-top: 0; }
253
+ .card p:last-child { margin-bottom: 0; }
254
+
255
+ .grid { display: grid; gap: 1rem; }
256
+ .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
257
+ .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
258
+
259
+ @media (max-width: 780px) {
260
+ .grid-2, .grid-3 { grid-template-columns: 1fr; }
261
+ }
262
+
263
+ /* Entry-path cards (Step 0) */
264
+ .path-card {
265
+ background: linear-gradient(180deg, var(--surface) 0%, var(--bg-raised) 100%);
266
+ border: 1px solid var(--line);
267
+ border-radius: var(--radius);
268
+ padding: 1.5rem;
269
+ display: flex;
270
+ flex-direction: column;
271
+ }
272
+ .path-card:hover { border-color: var(--accent-line); }
273
+ .path-card > :last-child { margin-top: auto; }
274
+
275
+ .path-tag {
276
+ font-family: var(--mono);
277
+ font-size: 0.72rem;
278
+ letter-spacing: 0.08em;
279
+ text-transform: uppercase;
280
+ color: var(--text-faint);
281
+ margin-bottom: 0.5rem;
282
+ }
283
+
284
+ /* ---------- code blocks ---------- */
285
+
286
+ .cmd {
287
+ position: relative;
288
+ background: #0b0c10;
289
+ border: 1px solid var(--line);
290
+ border-radius: var(--radius-sm);
291
+ margin: 0.9rem 0;
292
+ overflow: hidden;
293
+ }
294
+
295
+ .cmd pre {
296
+ margin: 0;
297
+ padding: 0.85rem 3.2rem 0.85rem 1rem;
298
+ overflow-x: auto;
299
+ font-family: var(--mono);
300
+ font-size: 0.855rem;
301
+ line-height: 1.6;
302
+ color: #e6e8ef;
303
+ }
304
+
305
+ .cmd pre code {
306
+ background: none;
307
+ border: none;
308
+ padding: 0;
309
+ font-size: inherit;
310
+ color: inherit;
311
+ }
312
+
313
+ /* The shell prompt marker is decorative — excluded from copy by JS. */
314
+ .cmd .p { color: var(--accent); user-select: none; }
315
+
316
+ .copy {
317
+ position: absolute;
318
+ top: 0.5rem;
319
+ right: 0.5rem;
320
+ font: 500 0.72rem var(--sans);
321
+ color: var(--text-dim);
322
+ background: var(--surface-2);
323
+ border: 1px solid var(--line);
324
+ border-radius: 6px;
325
+ padding: 0.24rem 0.55rem;
326
+ cursor: pointer;
327
+ opacity: 0;
328
+ transition: all 0.16s var(--ease);
329
+ }
330
+ .cmd:hover .copy, .copy:focus-visible { opacity: 1; }
331
+ .copy:hover { color: var(--text); border-color: var(--accent-line); background: var(--accent-soft); }
332
+ .copy[data-copied="1"] { color: var(--pass); border-color: var(--pass); opacity: 1; }
333
+
334
+ @media (hover: none) { .copy { opacity: 1; } }
335
+
336
+ /* Expected-output block */
337
+ .out {
338
+ background: var(--bg-raised);
339
+ border: 1px solid var(--line-soft);
340
+ border-left: 2px solid var(--line);
341
+ border-radius: var(--radius-sm);
342
+ padding: 0.8rem 1rem;
343
+ margin: 0.9rem 0;
344
+ overflow-x: auto;
345
+ font-family: var(--mono);
346
+ font-size: 0.8rem;
347
+ line-height: 1.62;
348
+ color: var(--text-dim);
349
+ white-space: pre;
350
+ }
351
+
352
+ .out .ok { color: var(--pass); }
353
+ .out .bad { color: var(--fail); }
354
+ .out .hint { color: var(--accent); }
355
+ .out .dim { color: var(--text-faint); }
356
+
357
+ .out-label {
358
+ font-family: var(--mono);
359
+ font-size: 0.72rem;
360
+ letter-spacing: 0.07em;
361
+ text-transform: uppercase;
362
+ color: var(--text-faint);
363
+ margin: 1.2rem 0 0.1rem;
364
+ }
365
+
366
+ /* ---------- checklist steps ---------- */
367
+
368
+ .steps { list-style: none; margin: 2rem 0 0; padding: 0; counter-reset: step; }
369
+
370
+ .step {
371
+ position: relative;
372
+ border: 1px solid var(--line-soft);
373
+ border-radius: var(--radius);
374
+ background: var(--surface);
375
+ padding: 1.4rem 1.6rem 1.5rem 3.6rem;
376
+ margin-bottom: 1rem;
377
+ transition: border-color 0.22s var(--ease), background 0.22s var(--ease);
378
+ }
379
+
380
+ .step::before {
381
+ counter-increment: step;
382
+ content: counter(step);
383
+ position: absolute;
384
+ left: 1.15rem;
385
+ top: 1.45rem;
386
+ width: 1.55rem;
387
+ height: 1.55rem;
388
+ display: grid;
389
+ place-items: center;
390
+ border-radius: 50%;
391
+ background: var(--surface-2);
392
+ border: 1px solid var(--line);
393
+ color: var(--text-faint);
394
+ font-family: var(--mono);
395
+ font-size: 0.76rem;
396
+ transition: all 0.22s var(--ease);
397
+ }
398
+
399
+ .step[data-done="1"] { border-color: rgba(88, 214, 138, 0.34); background: rgba(88, 214, 138, 0.035); }
400
+ .step[data-done="1"]::before {
401
+ content: "✓";
402
+ background: rgba(88, 214, 138, 0.14);
403
+ border-color: rgba(88, 214, 138, 0.4);
404
+ color: var(--pass);
405
+ font-size: 0.85rem;
406
+ }
407
+
408
+ .step-head {
409
+ display: flex;
410
+ align-items: flex-start;
411
+ gap: 0.8rem;
412
+ margin-bottom: 0.3rem;
413
+ }
414
+
415
+ .step-title { font-size: 1.06rem; font-weight: 620; margin: 0; flex: 1; letter-spacing: -0.012em; }
416
+
417
+ .step-check {
418
+ flex: none;
419
+ width: 1.15rem;
420
+ height: 1.15rem;
421
+ margin-top: 0.22rem;
422
+ accent-color: var(--accent);
423
+ cursor: pointer;
424
+ }
425
+
426
+ .step-why { color: var(--text-dim); font-size: 0.95rem; }
427
+
428
+ /* "if it fails" disclosure */
429
+ details.fail {
430
+ margin-top: 0.9rem;
431
+ border: 1px solid var(--line-soft);
432
+ border-radius: var(--radius-sm);
433
+ background: var(--bg-raised);
434
+ overflow: hidden;
435
+ }
436
+
437
+ details.fail > summary {
438
+ cursor: pointer;
439
+ padding: 0.6rem 0.95rem;
440
+ font-size: 0.86rem;
441
+ font-weight: 560;
442
+ color: var(--warn);
443
+ list-style: none;
444
+ transition: background 0.16s var(--ease);
445
+ }
446
+ details.fail > summary::-webkit-details-marker { display: none; }
447
+ details.fail > summary::before { content: "▸ "; color: var(--text-faint); }
448
+ details.fail[open] > summary::before { content: "▾ "; }
449
+ details.fail > summary:hover { background: var(--surface); }
450
+
451
+ .fail-body { padding: 0.2rem 0.95rem 0.9rem; font-size: 0.92rem; }
452
+ .fail-body > :last-child { margin-bottom: 0; }
453
+
454
+ /* ---------- callouts ---------- */
455
+
456
+ .note {
457
+ border: 1px solid var(--line);
458
+ border-left: 3px solid var(--accent);
459
+ background: var(--accent-soft);
460
+ border-radius: var(--radius-sm);
461
+ padding: 0.95rem 1.15rem;
462
+ margin: 1.4rem 0;
463
+ font-size: 0.95rem;
464
+ }
465
+ .note > :last-child { margin-bottom: 0; }
466
+ .note-title { font-weight: 640; color: var(--text); display: block; margin-bottom: 0.25rem; }
467
+
468
+ .note.blue { border-left-color: var(--info); background: rgba(106, 169, 240, 0.09); }
469
+ .note.plain { border-left-color: var(--line); background: var(--surface); }
470
+
471
+ /* The doctrine banner — the one idea that must land. */
472
+ .doctrine {
473
+ border: 1px solid var(--accent-line);
474
+ border-radius: var(--radius);
475
+ background: linear-gradient(135deg, rgba(246, 133, 27, 0.13), rgba(246, 133, 27, 0.03));
476
+ padding: 1.3rem 1.5rem;
477
+ margin: 1.5rem 0;
478
+ }
479
+ .doctrine p { margin: 0; font-size: 1.02rem; }
480
+ .doctrine .k { color: var(--accent); font-weight: 640; }
481
+
482
+ /* ---------- agent prompt card ---------- */
483
+
484
+ .agent-card {
485
+ border: 1px solid var(--accent-line);
486
+ border-radius: var(--radius);
487
+ background: linear-gradient(160deg, rgba(246, 133, 27, 0.10), var(--surface) 55%);
488
+ padding: 1.6rem 1.7rem;
489
+ margin: 2.2rem 0;
490
+ box-shadow: var(--shadow);
491
+ }
492
+ .agent-card h2 { margin-top: 0; }
493
+
494
+ details.prompt > summary {
495
+ cursor: pointer;
496
+ display: inline-block;
497
+ font: 560 0.9rem var(--sans);
498
+ color: var(--bg);
499
+ background: var(--accent);
500
+ border-radius: var(--radius-sm);
501
+ padding: 0.55rem 1.05rem;
502
+ list-style: none;
503
+ transition: background 0.16s var(--ease), transform 0.16s var(--ease);
504
+ }
505
+ details.prompt > summary::-webkit-details-marker { display: none; }
506
+ details.prompt > summary:hover { background: #ff9630; }
507
+ details.prompt[open] > summary { background: var(--surface-2); color: var(--text); }
508
+
509
+ .prompt-body { margin-top: 1rem; }
510
+ .prompt-body .cmd pre { white-space: pre-wrap; word-break: break-word; max-height: 30rem; overflow-y: auto; }
511
+
512
+ /* ---------- tables ---------- */
513
+
514
+ .table-scroll { overflow-x: auto; margin: 1.2rem 0; }
515
+
516
+ table {
517
+ width: 100%;
518
+ border-collapse: collapse;
519
+ font-size: 0.92rem;
520
+ border: 1px solid var(--line-soft);
521
+ border-radius: var(--radius-sm);
522
+ overflow: hidden;
523
+ }
524
+
525
+ th {
526
+ text-align: left;
527
+ font-weight: 600;
528
+ font-size: 0.78rem;
529
+ letter-spacing: 0.06em;
530
+ text-transform: uppercase;
531
+ color: var(--text-faint);
532
+ background: var(--bg-raised);
533
+ padding: 0.65rem 0.9rem;
534
+ border-bottom: 1px solid var(--line);
535
+ white-space: nowrap;
536
+ }
537
+
538
+ td {
539
+ padding: 0.7rem 0.9rem;
540
+ border-bottom: 1px solid var(--line-soft);
541
+ vertical-align: top;
542
+ color: #d5d8e2;
543
+ }
544
+
545
+ tbody tr:last-child td { border-bottom: none; }
546
+ tbody tr:hover td { background: rgba(255, 255, 255, 0.018); }
547
+
548
+ td code { white-space: nowrap; }
549
+
550
+ /* Reference-table cells are their own copy button. */
551
+ code.copyable {
552
+ cursor: pointer;
553
+ transition: all 0.16s var(--ease);
554
+ }
555
+ code.copyable:hover {
556
+ border-color: var(--accent-line);
557
+ background: var(--accent-soft);
558
+ color: var(--accent);
559
+ }
560
+ code.copyable::after {
561
+ content: "⧉";
562
+ margin-left: 0.45em;
563
+ opacity: 0;
564
+ color: var(--text-faint);
565
+ transition: opacity 0.16s var(--ease);
566
+ }
567
+ code.copyable:hover::after, code.copyable:focus-visible::after { opacity: 1; }
568
+ code.copyable.copied {
569
+ color: var(--pass);
570
+ border-color: rgba(88, 214, 138, 0.4);
571
+ background: rgba(88, 214, 138, 0.1);
572
+ }
573
+ code.copyable.copied::after { content: ""; margin-left: 0; }
574
+
575
+ /* ---------- platform filter ---------- */
576
+
577
+ .filters {
578
+ display: flex;
579
+ flex-wrap: wrap;
580
+ gap: 0.45rem;
581
+ margin: 1.5rem 0 0.5rem;
582
+ align-items: center;
583
+ }
584
+
585
+ .filter-label {
586
+ font-family: var(--mono);
587
+ font-size: 0.75rem;
588
+ letter-spacing: 0.06em;
589
+ text-transform: uppercase;
590
+ color: var(--text-faint);
591
+ margin-right: 0.4rem;
592
+ }
593
+
594
+ .chip {
595
+ font: 500 0.85rem var(--sans);
596
+ color: var(--text-dim);
597
+ background: var(--surface);
598
+ border: 1px solid var(--line);
599
+ border-radius: 99px;
600
+ padding: 0.34rem 0.9rem;
601
+ cursor: pointer;
602
+ transition: all 0.16s var(--ease);
603
+ }
604
+ .chip:hover { color: var(--text); border-color: var(--accent-line); }
605
+ .chip[aria-pressed="true"] {
606
+ color: var(--accent);
607
+ background: var(--accent-soft);
608
+ border-color: var(--accent-line);
609
+ }
610
+
611
+ .filter-empty {
612
+ color: var(--text-faint);
613
+ font-style: italic;
614
+ padding: 1.5rem 0;
615
+ }
616
+
617
+ tr[hidden], .filter-hidden { display: none !important; }
618
+
619
+ /* ---------- layer diagram ---------- */
620
+
621
+ .stack { display: flex; flex-direction: column; gap: 0.55rem; margin: 1.8rem 0; }
622
+
623
+ .layer {
624
+ width: 100%;
625
+ text-align: left;
626
+ font: inherit;
627
+ /* Buttons do not inherit color; without this the UA default wins and the
628
+ row titles render near-black on the dark surface. */
629
+ color: var(--text);
630
+ cursor: pointer;
631
+ background: var(--surface);
632
+ border: 1px solid var(--line);
633
+ border-radius: var(--radius-sm);
634
+ padding: 0.95rem 1.2rem;
635
+ display: flex;
636
+ align-items: center;
637
+ gap: 1rem;
638
+ transition: all 0.2s var(--ease);
639
+ }
640
+ .layer:hover { border-color: var(--accent-line); transform: translateX(3px); }
641
+
642
+ /* Diagram rows that are illustration, not controls. */
643
+ .layer-static { cursor: default; }
644
+ .layer-static:hover { border-color: var(--line); transform: none; }
645
+ .layer[aria-expanded="true"] {
646
+ border-color: var(--accent-line);
647
+ background: linear-gradient(90deg, var(--accent-soft), var(--surface) 62%);
648
+ }
649
+
650
+ .layer-n {
651
+ font-family: var(--mono);
652
+ font-size: 0.72rem;
653
+ color: var(--text-faint);
654
+ border: 1px solid var(--line);
655
+ border-radius: 5px;
656
+ padding: 0.1rem 0.4rem;
657
+ flex: none;
658
+ }
659
+ .layer[aria-expanded="true"] .layer-n { color: var(--accent); border-color: var(--accent-line); }
660
+
661
+ .layer-name { font-weight: 620; letter-spacing: -0.01em; }
662
+ .layer-sub { color: var(--text-dim); font-size: 0.88rem; margin-left: auto; text-align: right; }
663
+
664
+ @media (max-width: 640px) {
665
+ .layer { flex-wrap: wrap; }
666
+ .layer-sub { margin-left: 0; width: 100%; text-align: left; }
667
+ }
668
+
669
+ .layer-detail {
670
+ border: 1px solid var(--accent-line);
671
+ border-top: none;
672
+ border-radius: 0 0 var(--radius-sm) var(--radius-sm);
673
+ background: var(--bg-raised);
674
+ padding: 1.1rem 1.3rem;
675
+ margin: -0.55rem 0 0;
676
+ font-size: 0.94rem;
677
+ }
678
+ .layer-detail > :last-child { margin-bottom: 0; }
679
+ .layer-detail[hidden] { display: none; }
680
+
681
+ .arrow {
682
+ text-align: center;
683
+ color: var(--text-faint);
684
+ font-size: 0.9rem;
685
+ line-height: 1;
686
+ margin: -0.15rem 0;
687
+ }
688
+
689
+ /* ---------- video cards ---------- */
690
+
691
+ .vid {
692
+ display: flex;
693
+ flex-direction: column;
694
+ background: var(--surface);
695
+ border: 1px solid var(--line-soft);
696
+ border-radius: var(--radius);
697
+ overflow: hidden;
698
+ transition: border-color 0.2s var(--ease), transform 0.2s var(--ease);
699
+ }
700
+ .vid:hover { border-color: var(--accent-line); transform: translateY(-2px); }
701
+
702
+ .vid-thumb {
703
+ aspect-ratio: 16 / 9;
704
+ background:
705
+ radial-gradient(420px 200px at 30% 20%, rgba(246, 133, 27, 0.16), transparent 70%),
706
+ linear-gradient(150deg, var(--surface-2), var(--bg-raised));
707
+ border-bottom: 1px solid var(--line-soft);
708
+ display: grid;
709
+ place-items: center;
710
+ position: relative;
711
+ }
712
+
713
+ .vid-id {
714
+ font-family: var(--mono);
715
+ font-size: 2rem;
716
+ font-weight: 600;
717
+ color: #d98b3a;
718
+ letter-spacing: 0.04em;
719
+ }
720
+
721
+ .vid-body { padding: 1.1rem 1.25rem 1.3rem; display: flex; flex-direction: column; flex: 1; }
722
+ .vid-body h3 { margin: 0 0 0.4rem; font-size: 1rem; }
723
+ .vid-body p { font-size: 0.9rem; color: var(--text-dim); margin-bottom: 0.9rem; }
724
+ .vid-foot { margin-top: auto; display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap; }
725
+
726
+ .badge {
727
+ font-family: var(--mono);
728
+ font-size: 0.7rem;
729
+ letter-spacing: 0.06em;
730
+ text-transform: uppercase;
731
+ border-radius: 99px;
732
+ padding: 0.2rem 0.6rem;
733
+ border: 1px solid var(--line);
734
+ color: var(--text-faint);
735
+ white-space: nowrap;
736
+ }
737
+ .badge.soon { color: var(--warn); border-color: rgba(240, 192, 90, 0.35); background: rgba(240, 192, 90, 0.08); }
738
+ .badge.live { color: var(--pass); border-color: rgba(88, 214, 138, 0.35); background: rgba(88, 214, 138, 0.08); }
739
+ .badge.dur { font-family: var(--mono); }
740
+
741
+ /* ---------- annotated evidence ---------- */
742
+
743
+ .annot { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 1.1rem; margin: 1.4rem 0; align-items: start; }
744
+
745
+ @media (max-width: 900px) { .annot { grid-template-columns: 1fr; } }
746
+
747
+ .annot-notes { display: flex; flex-direction: column; gap: 0.7rem; }
748
+
749
+ .annot-note {
750
+ background: var(--surface);
751
+ border: 1px solid var(--line-soft);
752
+ border-left: 3px solid var(--accent);
753
+ border-radius: var(--radius-sm);
754
+ padding: 0.75rem 1rem;
755
+ font-size: 0.9rem;
756
+ }
757
+ .annot-note > :last-child { margin-bottom: 0; }
758
+ .annot-note b { color: var(--accent); font-family: var(--mono); font-size: 0.85rem; font-weight: 600; }
759
+
760
+ .verdict { font-family: var(--mono); font-weight: 600; }
761
+ .verdict.pass { color: var(--pass); }
762
+ .verdict.fail { color: var(--fail); }
763
+
764
+ /* ---------- misc ---------- */
765
+
766
+ .btn-row { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 1.8rem; }
767
+
768
+ .btn {
769
+ display: inline-block;
770
+ font: 560 0.92rem var(--sans);
771
+ border-radius: var(--radius-sm);
772
+ padding: 0.62rem 1.25rem;
773
+ border: 1px solid transparent;
774
+ transition: all 0.18s var(--ease);
775
+ }
776
+ .btn-primary { background: var(--accent); color: #17120b; }
777
+ .btn-primary:hover { background: #ff9630; text-decoration: none; transform: translateY(-1px); }
778
+ .btn-ghost { border-color: var(--line); color: var(--text-dim); }
779
+ .btn-ghost:hover { border-color: var(--accent-line); color: var(--text); text-decoration: none; }
780
+
781
+ .sep { border: none; border-top: 1px solid var(--line-soft); margin: 3rem 0; }
782
+
783
+ .footer {
784
+ border-top: 1px solid var(--line-soft);
785
+ padding: 2.2rem 0 3rem;
786
+ color: var(--text-faint);
787
+ font-size: 0.86rem;
788
+ }
789
+ .footer p { color: var(--text-faint); margin-bottom: 0.5rem; }
790
+
791
+ .skip {
792
+ position: absolute;
793
+ left: -9999px;
794
+ top: 0;
795
+ background: var(--accent);
796
+ color: #17120b;
797
+ padding: 0.6rem 1rem;
798
+ border-radius: 0 0 var(--radius-sm) 0;
799
+ z-index: 100;
800
+ }
801
+ .skip:focus { left: 0; }
802
+
803
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
804
+
805
+ @media (prefers-reduced-motion: reduce) {
806
+ * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
807
+ html { scroll-behavior: auto; }
808
+ }