prism-viz-engine 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/src/styles.css ADDED
@@ -0,0 +1,410 @@
1
+ /*
2
+ * Griotwave — neural-blue register.
3
+ *
4
+ * Not a palette I picked. These are the diagramming-oss-audit codex's own CONFIRMED
5
+ * tokens, which is the register this tool belongs to: neural blue #3B82F6, violet
6
+ * #818CF8, sky #38BDF8, mint #10FFBA (stat/accent), amber #F59E0B (verify/gap), pink
7
+ * #F472B6. Inter display+body, JetBrains Mono for code/kickers, #0A0A0C void canvas,
8
+ * radial ember-field ambient glow.
9
+ *
10
+ * Per-node colour is the LAYER ROLE's ember (from the ontology codex node colours), so
11
+ * the accent and the semantics stay separate: blue is the tool, the role tint is data.
12
+ */
13
+
14
+ :root {
15
+ --neural: #3b82f6;
16
+ --violet: #818cf8;
17
+ --sky: #38bdf8;
18
+ --mint: #10ffba;
19
+ --amber: #f59e0b;
20
+ --pink: #f472b6;
21
+ --ok: #57b98a;
22
+
23
+ --void: #0a0a0c;
24
+ --void2: #0c0e13;
25
+ --panel: #101418;
26
+ --panel2: #0d1116;
27
+ --line: rgba(255, 255, 255, 0.08);
28
+ --line2: rgba(255, 255, 255, 0.14);
29
+ --glass: rgba(255, 255, 255, 0.035);
30
+ --grid: #1a1f26;
31
+
32
+ --ink: #e8ecf2;
33
+ --mute: #9aa3b2;
34
+ --dim: #6b7385;
35
+
36
+ --sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
37
+ --mono: "JetBrains Mono", "SF Mono", ui-monospace, "Cascadia Code", Consolas, monospace;
38
+ }
39
+
40
+ * { box-sizing: border-box; }
41
+ html, body, #root { height: 100%; margin: 0; }
42
+
43
+ body {
44
+ background: var(--void);
45
+ color: var(--ink);
46
+ font-family: var(--sans);
47
+ line-height: 1.55;
48
+ -webkit-font-smoothing: antialiased;
49
+ overflow: hidden;
50
+ }
51
+
52
+ /* the audit's ambient ember-field */
53
+ body::before {
54
+ content: "";
55
+ position: fixed; inset: 0; z-index: 0; pointer-events: none;
56
+ background:
57
+ radial-gradient(680px 480px at 78% -6%, rgba(59, 130, 246, 0.14), transparent 60%),
58
+ radial-gradient(560px 400px at 8% 42%, rgba(129, 140, 248, 0.08), transparent 62%),
59
+ radial-gradient(600px 520px at 92% 88%, rgba(56, 189, 248, 0.06), transparent 60%);
60
+ }
61
+
62
+ .vz-shell { position: relative; z-index: 1; display: flex; flex-direction: column; height: 100%; }
63
+
64
+ /* ── top bar ───────────────────────────────────────────────────────────────── */
65
+ .vz-bar {
66
+ display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
67
+ padding: 9px 14px; background: var(--panel); border-bottom: 1px solid var(--line); z-index: 20;
68
+ }
69
+ .vz-brand { font-weight: 650; font-size: 13.5px; letter-spacing: -0.01em; color: var(--mint); }
70
+ .vz-host {
71
+ font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em;
72
+ color: var(--neural); border: 1px solid var(--neural); background: rgba(59,130,246,.12);
73
+ border-radius: 10px; padding: 2px 8px;
74
+ }
75
+ .vz-stat, .vz-status { font-family: var(--mono); font-size: 11px; color: var(--mute); }
76
+ .vz-status { color: var(--mint); }
77
+ .vz-spacer { flex: 1; }
78
+
79
+ button {
80
+ font: inherit; font-size: 11.5px; padding: 5px 11px; border-radius: 7px;
81
+ border: 1px solid var(--line2); background: var(--glass); color: var(--ink); cursor: pointer;
82
+ }
83
+ button:hover:not(:disabled) { border-color: var(--neural); color: var(--neural); }
84
+ button:disabled { opacity: 0.42; cursor: not-allowed; }
85
+ button:focus-visible { outline: 2px solid var(--neural); outline-offset: 1px; }
86
+ button.vz-pri { background: var(--neural); border-color: var(--neural); color: #04070d; font-weight: 650; }
87
+ button.vz-pri:hover { color: #04070d; filter: brightness(1.1); }
88
+
89
+ .vz-body { flex: 1; display: flex; min-height: 0; }
90
+
91
+ /* ── palette ───────────────────────────────────────────────────────────────── */
92
+ .vz-palette {
93
+ width: 286px; flex: none; display: flex; flex-direction: column;
94
+ background: var(--panel2); border-right: 1px solid var(--line);
95
+ }
96
+ .vz-palette-hd { padding: 12px 13px; border-bottom: 1px solid var(--line); display: flex; flex-direction: column; gap: 9px; }
97
+ .vz-palette-title { font-size: 12.5px; font-weight: 650; display: flex; align-items: center; gap: 7px; }
98
+ .vz-count {
99
+ font-family: var(--mono); font-size: 10px; color: var(--neural);
100
+ border: 1px solid var(--line2); border-radius: 9px; padding: 1px 6px;
101
+ }
102
+ .vz-search {
103
+ font: inherit; font-size: 11.5px; padding: 6px 9px; border-radius: 7px;
104
+ border: 1px solid var(--line2); background: var(--void2); color: var(--ink); width: 100%;
105
+ }
106
+ .vz-search:focus { outline: none; border-color: var(--neural); }
107
+ .vz-repos { display: flex; flex-wrap: wrap; gap: 5px; }
108
+ .vz-chip {
109
+ font-family: var(--mono); font-size: 9.5px; padding: 2px 7px; border-radius: 9px;
110
+ border: 1px solid var(--line2); background: transparent; color: var(--mute); cursor: pointer;
111
+ }
112
+ .vz-chip.on { border-color: var(--neural); color: var(--neural); background: rgba(59,130,246,.12); }
113
+
114
+ .vz-palette-body { flex: 1; overflow-y: auto; padding: 10px 11px 26px; }
115
+ .vz-group { margin-bottom: 16px; }
116
+ .vz-group-hd {
117
+ font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em;
118
+ color: var(--dim); margin: 0 0 7px; display: flex; align-items: center; gap: 6px; font-weight: 500;
119
+ }
120
+
121
+ .vz-card {
122
+ border: 1px solid var(--line); border-left: 3px solid var(--ember, var(--dim));
123
+ background: var(--panel); border-radius: 9px; padding: 8px 10px; margin-bottom: 7px;
124
+ cursor: grab; display: flex; flex-direction: column; gap: 3px;
125
+ }
126
+ .vz-card:hover { border-color: var(--line2); border-left-color: var(--ember); background: var(--glass); }
127
+ .vz-card:active { cursor: grabbing; }
128
+ .vz-card.placed { opacity: 0.5; }
129
+ .vz-card-top { display: flex; align-items: center; gap: 6px; }
130
+ .vz-card-kind {
131
+ font-family: var(--mono); font-size: 8.5px; text-transform: uppercase; letter-spacing: 0.07em;
132
+ color: var(--dim); border: 1px solid var(--line); border-radius: 3px; padding: 0 4px;
133
+ }
134
+ .vz-card-layer {
135
+ font-size: 9px; color: var(--ember, var(--dim)); margin-left: auto;
136
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 130px;
137
+ }
138
+ .vz-placed { font-family: var(--mono); font-size: 8.5px; color: var(--mint); }
139
+ .vz-card-label { font-size: 12px; font-weight: 560; line-height: 1.3; }
140
+ .vz-card-src, .vz-src {
141
+ font-family: var(--mono); font-size: 9.5px; color: var(--dim); background: none; border: 0;
142
+ padding: 0; text-align: left; cursor: pointer; overflow: hidden; text-overflow: ellipsis;
143
+ white-space: nowrap; max-width: 100%;
144
+ }
145
+ .vz-card-src:hover:not(:disabled), .vz-src:hover:not(:disabled) { color: var(--sky); }
146
+ .vz-card-foot { display: flex; align-items: center; gap: 7px; margin-top: 2px; }
147
+ .vz-lic { font-family: var(--mono); font-size: 9px; color: var(--amber); }
148
+ .vz-warn { font-family: var(--mono); font-size: 9px; color: var(--pink); font-weight: 700; }
149
+ .vz-empty { font-size: 12px; color: var(--dim); line-height: 1.5; padding: 10px 2px; }
150
+
151
+ /* ── canvas + lanes ────────────────────────────────────────────────────────── */
152
+ .vz-canvas { flex: 1; min-width: 0; position: relative; background: var(--void); }
153
+ .react-flow__renderer { z-index: 2; }
154
+
155
+ .vz-lane {
156
+ position: absolute; left: 0; z-index: 0; pointer-events: none;
157
+ border-bottom: 1px dashed var(--line);
158
+ background: linear-gradient(90deg, rgba(255,255,255,.018), transparent 460px);
159
+ }
160
+ /* The pinned label rail — screen space, tracking each band's projected position.
161
+ Bands move with the graph; the legend stays put. */
162
+ .vz-rail {
163
+ position: absolute; left: 0; top: 0; bottom: 0; width: 200px;
164
+ z-index: 5; pointer-events: none; overflow: hidden;
165
+ border-right: 1px solid var(--line);
166
+ background: linear-gradient(90deg, var(--panel2) 0 88%, transparent);
167
+ }
168
+ .vz-rail-row {
169
+ position: absolute; left: 0; width: 100%; padding: 0 12px;
170
+ display: flex; flex-direction: column; justify-content: center; gap: 3px;
171
+ border-bottom: 1px solid var(--soft);
172
+ transition: opacity 160ms var(--motion-ease, cubic-bezier(.2,.8,.2,1));
173
+ }
174
+ .vz-lane-name { font-size: 11.5px; font-weight: 600; line-height: 1.25; color: var(--ink); }
175
+ .vz-lane-name::before {
176
+ content: ""; display: inline-block; width: 3px; height: 11px; border-radius: 2px;
177
+ background: var(--ember); margin-right: 7px; vertical-align: -1px;
178
+ }
179
+ .vz-lane-eq { font-family: var(--mono); font-size: 9px; color: var(--sky); padding-left: 10px; }
180
+
181
+ /* ── the node ──────────────────────────────────────────────────────────────── */
182
+ .vz-node {
183
+ width: 232px; border: 1px solid var(--line2); border-left: 3px solid var(--ember, var(--neural));
184
+ background: var(--panel); border-radius: 9px; padding: 8px 10px 9px;
185
+ display: flex; flex-direction: column; gap: 3px;
186
+ box-shadow: 0 2px 6px rgba(0,0,0,.35);
187
+ }
188
+ .vz-node.sel { border-color: var(--neural); box-shadow: 0 0 0 2px var(--neural), 0 6px 16px rgba(0,0,0,.45); }
189
+ .vz-node.tracing { box-shadow: 0 0 0 2px var(--mint), 0 0 18px rgba(16,255,186,.45); }
190
+ .vz-node-top { display: flex; align-items: center; gap: 6px; }
191
+ .vz-repo {
192
+ font-family: var(--mono); font-size: 8.5px; text-transform: uppercase; letter-spacing: 0.06em;
193
+ color: var(--mute); font-weight: 700;
194
+ }
195
+ .vz-kind {
196
+ font-family: var(--mono); font-size: 8px; text-transform: uppercase; letter-spacing: 0.07em;
197
+ color: var(--dim); border: 1px solid var(--line); border-radius: 3px; padding: 0 4px;
198
+ }
199
+ .vz-dec { font-family: var(--mono); font-size: 8.5px; font-weight: 700; text-transform: uppercase; }
200
+ .vz-node .vz-warn { margin-left: auto; }
201
+ .vz-label { font-size: 12px; font-weight: 560; line-height: 1.3; }
202
+ .vz-src-file { color: inherit; }
203
+ .vz-src-line { color: var(--sky); }
204
+ .vz-code { display: flex; align-items: center; gap: 8px; margin-top: 1px; }
205
+ .vz-fit { font-family: var(--mono); font-size: 9px; color: var(--violet); }
206
+ .vz-fit b { color: var(--mint); margin-left: 2px; }
207
+
208
+ .vz-handle { width: 9px; height: 9px; background: var(--panel); border: 2px solid var(--neural); }
209
+
210
+ /* xyflow's own chrome ships a light default; without these the minimap renders as a
211
+ white slab on the void canvas and the controls read as a light-mode widget. Observed
212
+ in a screenshot of the running app, not inferred from the DOM. */
213
+ .react-flow__minimap {
214
+ background: var(--panel2);
215
+ border: 1px solid var(--line);
216
+ border-radius: 8px;
217
+ }
218
+ .react-flow__minimap-mask { fill: rgba(4, 6, 10, 0.62); }
219
+ .react-flow__controls { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45); }
220
+ .react-flow__controls-button {
221
+ background: var(--panel);
222
+ border-bottom: 1px solid var(--line);
223
+ fill: var(--mute);
224
+ }
225
+ .react-flow__controls-button:hover { background: var(--glass); fill: var(--neural); }
226
+ .react-flow__attribution { background: transparent; }
227
+ .react-flow__attribution a { color: var(--dim); }
228
+ .react-flow__edge-path { stroke: var(--dim); stroke-width: 1.6; }
229
+ .react-flow__edge.selected .react-flow__edge-path { stroke: var(--neural); stroke-width: 2.2; }
230
+ .react-flow__connection-path { stroke: var(--neural); stroke-width: 2; }
231
+
232
+ /* ── inspector ─────────────────────────────────────────────────────────────── */
233
+ .vz-inspector {
234
+ width: 310px; flex: none; overflow-y: auto; padding: 15px 15px 40px;
235
+ background: var(--panel2); border-left: 1px solid var(--line);
236
+ display: flex; flex-direction: column; gap: 12px;
237
+ }
238
+ .vz-inspector h2 {
239
+ font-size: 14.5px; font-weight: 650; margin: 0; line-height: 1.25;
240
+ border-left: 3px solid var(--neural); padding-left: 9px;
241
+ }
242
+ .vz-hint { font-size: 12px; color: var(--mute); margin: 0; line-height: 1.55; }
243
+ .vz-hint b { color: var(--ink); }
244
+ .vz-hint code { font-family: var(--mono); font-size: 11px; color: var(--mint); }
245
+ .vz-field { display: flex; flex-direction: column; gap: 3px; }
246
+ .vz-field-k {
247
+ font-family: var(--mono); font-size: 9px; text-transform: uppercase; letter-spacing: 0.12em;
248
+ color: var(--neural); font-weight: 600;
249
+ }
250
+ .vz-field-v { font-size: 12.5px; line-height: 1.45; word-break: break-word; }
251
+ .vz-field-v.mono, .mono { font-family: var(--mono); font-size: 10.5px; color: var(--mute); }
252
+ .vz-select {
253
+ font: inherit; font-size: 11.5px; padding: 5px 7px; border-radius: 7px;
254
+ border: 1px solid var(--line2); background: var(--void2); color: var(--ink); width: 100%;
255
+ }
256
+ .vz-reveal {
257
+ font-family: var(--mono); font-size: 10.5px; text-align: left; width: 100%;
258
+ display: flex; justify-content: space-between; gap: 8px; align-items: center;
259
+ }
260
+ .vz-reveal-cta { color: var(--sky); flex: none; }
261
+ .vz-list { margin: 0; padding-left: 16px; display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--mute); }
262
+ .vz-notcopy { color: var(--pink); }
263
+ .vz-dim { color: var(--dim); font-size: 12px; }
264
+ .vz-legend { display: flex; flex-direction: column; gap: 5px; margin-top: 4px; }
265
+ .vz-legend-row { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--mute); }
266
+ .vz-legend-row i { width: 9px; height: 9px; border-radius: 2px; flex: none; }
267
+ .vz-legend-row em { font-family: var(--mono); font-size: 9px; color: var(--sky); font-style: normal; }
268
+
269
+ /* ── pickers + the routing readout ─────────────────────────────────────────── */
270
+ .vz-picker {
271
+ font: inherit; font-size: 11px; padding: 4px 7px; border-radius: 6px;
272
+ border: 1px solid var(--line2); background: var(--glass); color: var(--ink);
273
+ }
274
+ .vz-picker:focus { outline: none; border-color: var(--neural); }
275
+ .vz-route {
276
+ display: inline-flex; align-items: baseline; gap: 7px;
277
+ padding: 3px 9px; border-radius: 8px;
278
+ border: 1px solid color-mix(in srgb, var(--mint) 38%, var(--line));
279
+ background: rgba(16, 255, 186, 0.07);
280
+ }
281
+ .vz-route b { font-size: 11.5px; color: var(--mint); font-weight: 650; }
282
+ .vz-route em {
283
+ font-family: var(--mono); font-size: 9.5px; color: var(--mute); font-style: normal;
284
+ }
285
+ .vz-route .vz-fellback { font-family: var(--mono); font-size: 9.5px; color: var(--amber); font-style: normal; }
286
+
287
+ /* ── isometric canvas ──────────────────────────────────────────────────────── */
288
+ .vz-iso {
289
+ flex: 1; min-width: 0; position: relative; overflow: hidden; cursor: grab;
290
+ background:
291
+ radial-gradient(1200px 700px at 50% 0%, rgba(59, 130, 246, 0.06), transparent 60%),
292
+ var(--void);
293
+ touch-action: none;
294
+ }
295
+ .vz-iso:active { cursor: grabbing; }
296
+ .vz-iso-svg { width: 100%; height: 100%; display: block; transform-origin: 50% 50%; }
297
+
298
+ /* Two stacked strokes — FossFLOW Connector.tsx:107-125. The halo is the ground colour,
299
+ 1.4x the width at 0.7 opacity, painted underneath. Nothing avoids anything in the
300
+ routing, so this is what keeps a line readable across a crowded scene.
301
+ `stroke-linejoin: round` softens every corner, same as theirs. */
302
+ .vz-iso-edge-halo {
303
+ fill: none; stroke: var(--void); stroke-width: 2.8; opacity: 0.7;
304
+ stroke-linecap: round; stroke-linejoin: round;
305
+ }
306
+ .vz-iso-edge {
307
+ fill: none; stroke: var(--dim); stroke-width: 2; opacity: 0.85;
308
+ stroke-linecap: round; stroke-linejoin: round;
309
+ }
310
+ .vz-iso-node { cursor: pointer; }
311
+ .vz-iso-face { stroke: rgba(0, 0, 0, 0.45); stroke-width: 1; stroke-linejoin: round; }
312
+ /* one light source, top-left: lid full, south-west wall mid, south-east wall dark.
313
+ That single convention is what reads as solid rather than as three flat shapes. */
314
+ .vz-iso-face.top { filter: brightness(1.06); }
315
+ .vz-iso-face.left { filter: brightness(0.62); }
316
+ .vz-iso-face.right { filter: brightness(0.4); }
317
+ .vz-iso-node:hover .vz-iso-face.top { filter: brightness(1.28); }
318
+ .vz-iso-node.sel .vz-iso-face { stroke: var(--mint); stroke-width: 1.6; }
319
+ .vz-iso-node.sel .vz-iso-face.top { filter: brightness(1.35); }
320
+ .vz-iso-label {
321
+ fill: #06080c; font-family: var(--sans); font-size: 11px; font-weight: 650;
322
+ pointer-events: none; paint-order: stroke;
323
+ stroke: rgba(255, 255, 255, 0.55); stroke-width: 2.4px; stroke-linejoin: round;
324
+ }
325
+ .vz-iso-empty { position: absolute; inset: 0; display: grid; place-content: center; max-width: 40ch; margin: auto; text-align: center; }
326
+
327
+ @media (prefers-reduced-motion: reduce) {
328
+ * { transition: none !important; animation: none !important; }
329
+ /* diagram-design animation.md:76 — decoration is dropped entirely, not just slowed */
330
+ [data-motion-decorative] { display: none !important; }
331
+ }
332
+
333
+ /* Icons sit on the lid; the label moves below the box when one is present so the two
334
+ never compete for the same face. */
335
+ .vz-iso-icon { pointer-events: none; }
336
+
337
+ /* ── the reference gallery ─────────────────────────────────────────────────── */
338
+ .vz-gallery { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--void); }
339
+ .vz-gallery-bar {
340
+ display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
341
+ padding: 10px 14px; border-bottom: 1px solid var(--line); background: var(--panel);
342
+ }
343
+ .vz-gallery-title { font-size: 12.5px; font-weight: 650; display: flex; align-items: center; gap: 7px; }
344
+ .vz-gallery-search { width: 200px; }
345
+ .vz-gallery-note { font-family: var(--mono); font-size: 10px; color: var(--amber); }
346
+ .vz-gallery-grid {
347
+ flex: 1; overflow-y: auto; padding: 14px;
348
+ display: grid; grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); gap: 14px; align-content: start;
349
+ }
350
+ .vz-gal { margin: 0; border: 1px solid var(--line); border-radius: 12px; overflow: hidden; background: var(--panel2); cursor: zoom-in; }
351
+ .vz-gal:hover { border-color: var(--neural); }
352
+ /* The frame is scaled down rather than the document: these are finished artefacts and
353
+ reflowing them at thumbnail width would show a layout their author never shipped. */
354
+ /* Scale the FRAME, never reflow the document: these are finished pieces and reflowing
355
+ them at thumbnail width would show a layout their author never shipped. The iframe is
356
+ sized so 0.3 scale exactly fills the tile — otherwise the backing shows through as a
357
+ white band under every dark diagram. */
358
+ .vz-gal-frame { height: 186px; overflow: hidden; position: relative; background: var(--void2); }
359
+ .vz-gal-frame iframe {
360
+ width: 1120px; height: 620px; border: 0;
361
+ transform: scale(0.3); transform-origin: top left; pointer-events: none;
362
+ }
363
+ .vz-gal.wide .vz-gal-frame iframe { height: 800px; }
364
+ .vz-gal figcaption {
365
+ display: flex; justify-content: space-between; align-items: center; gap: 8px;
366
+ padding: 7px 10px; font-size: 11.5px; color: var(--ink); border-top: 1px solid var(--line);
367
+ }
368
+ .vz-gal figcaption span { font-family: var(--mono); font-size: 9.5px; color: var(--dim); }
369
+ .vz-gal-open { position: fixed; inset: 0; z-index: 60; background: rgba(3,4,6,.86); display: flex; flex-direction: column; }
370
+ .vz-gal-open-bar { display: flex; align-items: center; gap: 12px; padding: 10px 16px; background: var(--panel); border-bottom: 1px solid var(--line); font-size: 12.5px; }
371
+ .vz-gal-open-bar span { font-family: var(--mono); font-size: 10.5px; color: var(--dim); }
372
+ .vz-gal-open iframe { flex: 1; width: 100%; border: 0; background: #fff; }
373
+ button.on { border-color: var(--neural); color: var(--neural); background: rgba(59,130,246,.12); }
374
+
375
+ /* ── the catalogue: grouped by shape, shared shapes first ──────────────────── */
376
+ .vz-cat { flex: 1; overflow-y: auto; padding: 14px 16px 40px; display: flex; flex-direction: column; gap: 22px; }
377
+ .vz-cat-shape { display: flex; flex-direction: column; gap: 9px; }
378
+ .vz-cat-shape.shared {
379
+ border: 1px solid color-mix(in srgb, var(--mint) 26%, var(--line));
380
+ border-radius: 14px; padding: 12px 13px; background: rgba(16, 255, 186, 0.03);
381
+ }
382
+ .vz-cat-hd { display: flex; align-items: baseline; gap: 10px; }
383
+ .vz-cat-hd h3 { font-size: 13.5px; font-weight: 650; margin: 0; letter-spacing: -0.005em; }
384
+ .vz-cat-shape.shared .vz-cat-hd h3 { color: var(--mint); }
385
+ .vz-cat-count { font-family: var(--mono); font-size: 9.5px; color: var(--dim); margin-left: auto; }
386
+ .vz-cat-compare {
387
+ font-family: var(--mono); font-size: 9.5px; padding: 2px 8px; border-radius: 9px;
388
+ border: 1px solid var(--mint); color: var(--mint); background: rgba(16,255,186,.08);
389
+ }
390
+ .vz-cat-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(256px, 1fr)); gap: 12px; }
391
+ .vz-cat-row.vz-cat-pair { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
392
+ .vz-gal.wide .vz-gal-frame { height: 240px; }
393
+ .vz-gal-img { width: 100%; height: 100%; object-fit: contain; background: #06070a; display: block; }
394
+ .vz-gal-open-img { flex: 1; min-height: 0; object-fit: contain; background: #06070a; }
395
+ .vz-gal-src {
396
+ font-family: var(--mono); font-size: 9px; text-transform: uppercase; letter-spacing: .05em;
397
+ padding: 1px 5px; border-radius: 4px; border: 1px solid var(--line2); color: var(--dim);
398
+ }
399
+ .vz-src-diagram-design { color: var(--neural); border-color: color-mix(in srgb, var(--neural) 45%, transparent); }
400
+ .vz-src-archify { color: var(--mint); border-color: color-mix(in srgb, var(--mint) 45%, transparent); }
401
+ .vz-src-lanshu { color: var(--pink); border-color: color-mix(in srgb, var(--pink) 45%, transparent); }
402
+ .vz-src-visual-explainer { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, transparent); }
403
+ .vz-src-fossflow { color: var(--violet); border-color: color-mix(in srgb, var(--violet) 45%, transparent); }
404
+ .vz-anim { color: var(--pink) !important; }
405
+ .vz-gal figcaption b { flex: 1; font-weight: 560; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
406
+ .vz-gal-var { font-family: var(--mono); font-size: 9px; color: var(--dim); }
407
+ .vz-compare { flex: 1; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 1px; background: var(--line); min-height: 0; }
408
+ .vz-compare-pane { display: flex; flex-direction: column; background: var(--panel2); min-height: 0; }
409
+ .vz-compare-pane .vz-gal-src { margin: 8px 10px; align-self: flex-start; }
410
+ .vz-compare-pane iframe { flex: 1; width: 100%; border: 0; background: #fff; }