evalstate HF Staff commited on
Commit
6ba7af7
Β·
verified Β·
1 Parent(s): 5748992

Upload 4 files

Browse files
Files changed (4) hide show
  1. app.js +566 -0
  2. data.js +0 -0
  3. index.html +184 -17
  4. styles.css +1076 -0
app.js ADDED
@@ -0,0 +1,566 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const data = window.REPORT_DATA;
2
+
3
+ const groupDefinitions = [
4
+ {
5
+ id: "kimi-k2",
6
+ label: "Kimi-K2.5 / K2.6",
7
+ members: ["moonshotai/Kimi-K2.5", "moonshotai/Kimi-K2.6"],
8
+ note: "Shared TypeScript-style tool declaration output across K2.5 and K2.6.",
9
+ },
10
+ {
11
+ id: "minimax-m2",
12
+ label: "MiniMax-M2.5 / M2.7",
13
+ members: ["MiniMaxAI/MiniMax-M2.5", "MiniMaxAI/MiniMax-M2.7"],
14
+ note: "Same XML/tag-style tool rendering; model-name boilerplate differs.",
15
+ },
16
+ {
17
+ id: "qwen-3",
18
+ label: "Qwen3.5 / Qwen3.6",
19
+ members: ["Qwen/Qwen3.5-397B-A17B", "Qwen/Qwen3.6-35B-A3B"],
20
+ note: "Shared XML/tag-style tool rendering in the sampled cases.",
21
+ },
22
+ ];
23
+
24
+ const modelByName = new Map(data.models.map((model) => [model.model, model]));
25
+ const groupedNames = new Set(groupDefinitions.flatMap((group) => group.members));
26
+ const modelGroups = [
27
+ ...data.models
28
+ .filter((model) => !groupedNames.has(model.model))
29
+ .map((model) => ({
30
+ id: model.model,
31
+ label: shortModel(model.model),
32
+ members: [model.model],
33
+ note: "",
34
+ })),
35
+ ...groupDefinitions,
36
+ ]
37
+ .map((group) => ({
38
+ ...group,
39
+ models: group.members.map((name) => modelByName.get(name)).filter(Boolean),
40
+ }))
41
+ .filter((group) => group.models.length)
42
+ .sort((a, b) => a.label.localeCompare(b.label));
43
+
44
+ const state = {
45
+ page: location.hash === "#matrix" ? "matrix" : "overview",
46
+ group: modelGroups[0]?.id,
47
+ caseId: "minimal_string_required",
48
+ examplePath: "",
49
+ dialect: "all",
50
+ view: "output",
51
+ };
52
+
53
+ const featuredCases = [
54
+ "minimal_string_required",
55
+ "nested_arrays_enums",
56
+ "nullable_anyof_refs",
57
+ "oneof_allof_constraints",
58
+ ];
59
+
60
+ const caseLabels = {
61
+ minimal_string_required: "Simple required string",
62
+ nested_arrays_enums: "Nested arrays + enums",
63
+ nullable_anyof_refs: "Refs + nullable anyOf",
64
+ oneof_allof_constraints: "oneOf/allOf stress",
65
+ };
66
+
67
+ const featureNames = [
68
+ "tool_name",
69
+ "description",
70
+ "parameters",
71
+ "properties",
72
+ "required",
73
+ "array",
74
+ "enum",
75
+ "anyOf",
76
+ "oneOf",
77
+ "ref",
78
+ "default",
79
+ "additionalProperties",
80
+ ];
81
+
82
+ function $(id) {
83
+ return document.getElementById(id);
84
+ }
85
+
86
+ function label(value) {
87
+ return String(value ?? "β€”").replaceAll("_", " ");
88
+ }
89
+
90
+ function shortModel(model) {
91
+ return model.split("/").pop();
92
+ }
93
+
94
+ function family(model) {
95
+ return model.split("/")[0] || model;
96
+ }
97
+
98
+ function selectedModel() {
99
+ return selectedGroup().models[0] || data.models[0];
100
+ }
101
+
102
+ function selectedGroup() {
103
+ return modelGroups.find((group) => group.id === state.group) || modelGroups[0];
104
+ }
105
+
106
+ function caseInfo(id) {
107
+ return data.cases.find((item) => item.id === id) || data.cases[0];
108
+ }
109
+
110
+ function isGood(status) {
111
+ return typeof status === "string" && (status.startsWith("present") || status.startsWith("transformed"));
112
+ }
113
+
114
+ function statusClass(status) {
115
+ if (status === "not_applicable") return "na";
116
+ return isGood(status) ? "good" : "warn";
117
+ }
118
+
119
+ function dialectLabel(dialect) {
120
+ const labels = {
121
+ typescript: "TypeScript-style declarations",
122
+ xml: "XML/tag-style instructions",
123
+ json: "JSON tool block",
124
+ "custom-channel": "Custom channel syntax",
125
+ "message-json": "Message functions JSON",
126
+ builtin: "Builtin tool block",
127
+ other: "Other",
128
+ };
129
+ return labels[dialect] || label(dialect);
130
+ }
131
+
132
+ function supportCoverage(model) {
133
+ if (model.support_variant === "tools_argument") return `${model.standard_supported}/${model.case_count}`;
134
+ if (model.support_variant === "message_functions_json") return `${model.message_supported}/${model.case_count}`;
135
+ if (model.support_variant === "special_modes") return `${model.special_supported}/${model.special_total}`;
136
+ if (model.support_variant?.startsWith("custom_")) return `${model.custom_supported}/${model.case_count}`;
137
+ return `${Math.max(model.standard_supported, model.custom_supported, model.message_supported)}/${model.case_count}`;
138
+ }
139
+
140
+ function filteredModels() {
141
+ return modelGroups.filter((group) => {
142
+ return state.dialect === "all" || group.models.some((model) => model.dialect === state.dialect);
143
+ });
144
+ }
145
+
146
+ function renderModelList() {
147
+ $("model-list").innerHTML = filteredModels()
148
+ .map((group) => {
149
+ const model = group.models[0];
150
+ return `
151
+ <button class="model-card ${model.dialect} ${state.page === "matrix" && group.id === state.group ? "active" : ""}" type="button" data-group="${group.id}">
152
+ <strong>${group.label}</strong>
153
+ <span>${dialectLabel(model.dialect)}</span>
154
+ </button>
155
+ `;
156
+ })
157
+ .join("");
158
+ }
159
+
160
+ function chooseDefaultExample(model) {
161
+ const current = model.cases[state.caseId]?.[model.support_variant]?.rendered_path;
162
+ if (current) return current;
163
+ for (const variant of Object.values(model.cases[state.caseId] || {})) {
164
+ if (variant.rendered_path) return variant.rendered_path;
165
+ }
166
+ if (model.representative_paths?.length) return model.representative_paths[0];
167
+ for (const variants of Object.values(model.cases)) {
168
+ for (const variant of Object.values(variants)) {
169
+ if (variant.rendered_path) return variant.rendered_path;
170
+ }
171
+ }
172
+ return Object.keys(data.snippets)[0];
173
+ }
174
+
175
+ function availableExamples(model) {
176
+ const paths = [];
177
+ const variants = model.cases[state.caseId] || {};
178
+ if (variants[model.support_variant]?.rendered_path) {
179
+ paths.push(variants[model.support_variant].rendered_path);
180
+ }
181
+ for (const variant of Object.values(variants)) {
182
+ if (variant.meaningful && variant.rendered_path) paths.push(variant.rendered_path);
183
+ }
184
+ return [...new Set(paths)];
185
+ }
186
+
187
+ function renderHeader(group) {
188
+ const model = group.models[0];
189
+ $("active-family").textContent = group.models.length > 1 ? family(model.model) : family(model.model);
190
+ $("active-model").textContent = group.label;
191
+ $("active-summary").textContent =
192
+ `${dialectLabel(model.dialect)}. ${group.note || model.support_detail} This view shows one representative rendered prompt; evidence packets for each grouped model are kept below.`;
193
+ $("active-summary").style.borderLeftColor = `var(--${model.dialect})`;
194
+ $("active-dialect").textContent = dialectLabel(model.dialect);
195
+ $("active-path").textContent = model.support_path;
196
+ $("active-coverage").textContent = group.models.length > 1 ? `${supportCoverage(model)} each` : `${supportCoverage(model)} cases`;
197
+ }
198
+
199
+ function renderExamples(model) {
200
+ const paths = availableExamples(model);
201
+ if (!paths.length) {
202
+ state.examplePath = "";
203
+ $("rendered-output").innerHTML = unsupportedMessage(model);
204
+ renderViewMode();
205
+ return;
206
+ }
207
+ if (!paths.includes(state.examplePath)) state.examplePath = paths[0];
208
+ renderOutput(model);
209
+ }
210
+
211
+ function renderCases(model) {
212
+ $("case-tabs").innerHTML = featuredCases
213
+ .filter((id) => model.cases[id])
214
+ .map((id) => {
215
+ const supported = hasMeaningfulOutput(model, id);
216
+ const title = supported ? "Rendered output available" : "Input example only; no meaningful rendered output for this model/path";
217
+ return `<button type="button" class="${id === state.caseId ? "active" : ""} ${supported ? "" : "unsupported"}" data-case="${id}" title="${title}">${caseLabels[id] || caseInfo(id).label}</button>`;
218
+ })
219
+ .join("");
220
+ $("case-description").textContent = caseInfo(state.caseId).description;
221
+ $("case-title").textContent = caseLabels[state.caseId] || caseInfo(state.caseId).label;
222
+ renderInputSchema();
223
+ }
224
+
225
+ function hasMeaningfulOutput(model, caseId) {
226
+ const variants = model.cases[caseId] || {};
227
+ return Object.values(variants).some((variant) => variant.meaningful && variant.rendered_path);
228
+ }
229
+
230
+ function renderInputSchema() {
231
+ const item = caseInfo(state.caseId);
232
+ $("input-schema").innerHTML = highlightInputJson(JSON.stringify(item.schema.tools, null, 2));
233
+ }
234
+
235
+ function renderOutput(model) {
236
+ if (!state.examplePath) {
237
+ $("rendered-output").innerHTML = unsupportedMessage(model);
238
+ return;
239
+ }
240
+ const text = data.snippets[state.examplePath] || "No rendered prompt available for this selection.";
241
+ $("rendered-output").innerHTML = highlightRendered(text, caseInfo(state.caseId));
242
+ }
243
+
244
+ function unsupportedMessage(model) {
245
+ const variant = model.cases[state.caseId]?.[model.support_variant];
246
+ const reason = variant?.error ? ` Renderer error: ${escapeHtml(variant.error)}.` : "";
247
+ return `<span class="empty-render">No meaningful model-visible output was produced for <strong>${escapeHtml(caseLabels[state.caseId] || caseInfo(state.caseId).label)}</strong> using ${escapeHtml(model.support_path)}.${reason} Use <strong>Tool Definition</strong> to inspect the JSON schema that failed this renderer.</span>`;
248
+ }
249
+
250
+ function renderViewMode() {
251
+ const isOutput = state.view === "output";
252
+ $("rendered-output").hidden = !isOutput;
253
+ $("input-schema").hidden = isOutput;
254
+ for (const button of document.querySelectorAll("[data-view]")) {
255
+ const active = button.dataset.view === state.view;
256
+ button.classList.toggle("active", active);
257
+ button.setAttribute("aria-selected", String(active));
258
+ }
259
+ }
260
+
261
+ function renderPage() {
262
+ const isOverview = state.page === "overview";
263
+ $("overview-panel").hidden = !isOverview;
264
+ $("matrix-view").hidden = isOverview;
265
+ $("overview-link").classList.toggle("active", isOverview);
266
+ if (location.hash !== (isOverview ? "#overview" : "#matrix")) {
267
+ history.replaceState(null, "", isOverview ? "#overview" : "#matrix");
268
+ }
269
+ }
270
+
271
+ function highlightInputJson(text) {
272
+ return escapeHtml(text).replace(
273
+ /(&quot;(name|description|parameters|properties|required|type|enum|items|anyOf|oneOf|allOf|\$defs|\$ref|default|additionalProperties|minimum)&quot;)/g,
274
+ '<span class="schema-token">$1</span>',
275
+ );
276
+ }
277
+
278
+ function highlightRendered(text, item) {
279
+ const boilerplatePattern = /knowledge cutoff|current date|general guidelines|multimodal|you are |system_prompt|reasoning mode|today date|user<|<\|user|accurately answer|be very attentive/i;
280
+ return highlightRenderedTokens(text, item, boilerplatePattern);
281
+ }
282
+
283
+ function highlightRenderedTokens(text, item, boilerplatePattern) {
284
+ let escaped = escapeHtml(text);
285
+ for (const name of item.tool_names || []) {
286
+ escaped = escaped.replaceAll(escapeHtml(name), `<span class="tool-token">${escapeHtml(name)}</span>`);
287
+ }
288
+ escaped = escaped.replace(
289
+ /\b(parameters|properties|required|enum|anyOf|oneOf|allOf|additionalProperties|tool_call|AVAILABLE_TOOLS|namespace functions|DSML|tool_declare)\b/g,
290
+ '<span class="schema-token-output">$1</span>',
291
+ );
292
+ escaped = escaped
293
+ .split("\n")
294
+ .map((line) => (boilerplatePattern.test(line) ? `<span class="boilerplate-token">${line}</span>` : line))
295
+ .join("\n");
296
+ return escaped;
297
+ }
298
+
299
+ function currentVariant(model) {
300
+ const variants = model.cases[state.caseId] || {};
301
+ return variants[model.support_variant] || Object.values(variants).find((variant) => variant.meaningful) || Object.values(variants)[0];
302
+ }
303
+
304
+ function renderFeatures(model) {
305
+ const variant = currentVariant(model);
306
+ if (!variant) {
307
+ $("feature-list").innerHTML = `<p class="muted">No feature packet for this case.</p>`;
308
+ return;
309
+ }
310
+ $("feature-list").innerHTML = featureNames
311
+ .map((name) => {
312
+ const status = variant.features?.[name] || "not_applicable";
313
+ return `
314
+ <div class="feature-row">
315
+ <strong>${label(name)}</strong>
316
+ <span class="status ${statusClass(status)}">${label(status)}</span>
317
+ </div>
318
+ `;
319
+ })
320
+ .join("");
321
+ }
322
+
323
+ function renderSpecialModes(model) {
324
+ const modes = model.special_modes || [];
325
+ const notes = model.highlighted_notes || [];
326
+ if (!modes.length && !notes.length) {
327
+ $("special-list").innerHTML = `<p class="muted">No special/builtin probes or highlighted special-tool notes in this evidence set.</p>`;
328
+ return;
329
+ }
330
+ const modeHtml = modes
331
+ .map((mode) => `
332
+ <button class="special-row" type="button" data-path="${mode.rendered_path || ""}">
333
+ <strong>${label(mode.probe_id)}</strong>
334
+ <span class="status ${mode.meaningful && mode.counts_as_special_tool ? "good" : "warn"}">${label(mode.category || mode.style_family)} Β· ${mode.token_count ?? "β€”"} tokens</span>
335
+ <small>${escapeHtml(mode.description || "")}</small>
336
+ </button>
337
+ `)
338
+ .join("");
339
+ const notesHtml = notes
340
+ .map((note) => `
341
+ <div class="special-row note-row">
342
+ <strong>${escapeHtml(note.title)}</strong>
343
+ <span class="status warn">${escapeHtml(note.category)}</span>
344
+ <small>${escapeHtml(note.detail)}</small>
345
+ ${note.evidence ? `<small class="evidence-path">${escapeHtml(note.evidence)}</small>` : ""}
346
+ </div>
347
+ `)
348
+ .join("");
349
+ $("special-list").innerHTML = modeHtml + notesHtml;
350
+ }
351
+
352
+ function stringifyEvidence(item) {
353
+ if (item.snippet) return item.snippet;
354
+ if (item.observation) return item.observation;
355
+ if (item.artifact) return item.artifact;
356
+ return JSON.stringify(item, null, 2);
357
+ }
358
+
359
+ function renderClaims(group) {
360
+ const sections = group.models.map((model) => {
361
+ const claims = model.findings_packet?.claims || [];
362
+ if (!claims.length) {
363
+ return `<section class="packet-group"><h4>${model.model}</h4><p class="muted">No findings JSON packet found for this model.</p></section>`;
364
+ }
365
+ return `
366
+ <section class="packet-group">
367
+ <h4>${model.model}</h4>
368
+ ${claims
369
+ .map((claim, index) => `
370
+ <details class="claim" ${index === 0 ? "open" : ""}>
371
+ <summary>
372
+ <strong>${label(claim.evidence_class)} Β· ${claim.confidence || "confidence unknown"}</strong>
373
+ <p>${escapeHtml(claim.claim)}</p>
374
+ </summary>
375
+ <div class="claim-body">
376
+ ${(claim.evidence || []).map((item) => `<div class="evidence-item">${escapeHtml(stringifyEvidence(item))}</div>`).join("")}
377
+ </div>
378
+ </details>
379
+ `)
380
+ .join("")}
381
+ </section>
382
+ `;
383
+ });
384
+ $("claims").innerHTML = sections.join("");
385
+ }
386
+
387
+ function escapeHtml(text) {
388
+ return String(text)
389
+ .replaceAll("&", "&amp;")
390
+ .replaceAll("<", "&lt;")
391
+ .replaceAll(">", "&gt;")
392
+ .replaceAll('"', "&quot;");
393
+ }
394
+
395
+ function renderAll() {
396
+ const group = selectedGroup();
397
+ const model = group.models[0];
398
+ renderPage();
399
+ renderModelList();
400
+ renderHeader(group);
401
+ renderExamples(model);
402
+ renderCases(model);
403
+ renderFeatures(model);
404
+ renderSpecialModes(model);
405
+ renderClaims(group);
406
+ renderViewMode();
407
+ }
408
+
409
+ function bindEvents() {
410
+ $("overview-link").addEventListener("click", () => {
411
+ state.page = "overview";
412
+ renderAll();
413
+ });
414
+
415
+ $("dialect-filter").addEventListener("click", (event) => {
416
+ const button = event.target.closest("[data-dialect]");
417
+ if (!button) return;
418
+ state.dialect = button.dataset.dialect;
419
+ for (const item of $("dialect-filter").querySelectorAll("[data-dialect]")) {
420
+ item.classList.toggle("active", item.dataset.dialect === state.dialect);
421
+ }
422
+ const visible = filteredModels();
423
+ if (visible.length && !visible.some((group) => group.id === state.group)) {
424
+ state.group = visible[0].id;
425
+ state.examplePath = chooseDefaultExample(visible[0].models[0]);
426
+ renderAll();
427
+ return;
428
+ }
429
+ renderModelList();
430
+ });
431
+
432
+ $("model-list").addEventListener("click", (event) => {
433
+ const button = event.target.closest("[data-group]");
434
+ if (!button) return;
435
+ state.page = "matrix";
436
+ state.group = button.dataset.group;
437
+ const model = selectedModel();
438
+ state.examplePath = chooseDefaultExample(model);
439
+ renderAll();
440
+ });
441
+
442
+ $("case-tabs").addEventListener("click", (event) => {
443
+ const button = event.target.closest("[data-case]");
444
+ if (!button) return;
445
+ state.caseId = button.dataset.case;
446
+ const model = selectedModel();
447
+ const path = model.cases[state.caseId]?.[model.support_variant]?.rendered_path;
448
+ if (path) state.examplePath = path;
449
+ renderAll();
450
+ });
451
+
452
+ document.querySelector(".view-tabs").addEventListener("click", (event) => {
453
+ const button = event.target.closest("[data-view]");
454
+ if (!button) return;
455
+ state.view = button.dataset.view;
456
+ renderViewMode();
457
+ });
458
+
459
+ $("special-list").addEventListener("click", (event) => {
460
+ const button = event.target.closest("[data-path]");
461
+ if (!button?.dataset.path) return;
462
+ state.examplePath = button.dataset.path;
463
+ renderOutput(selectedModel());
464
+ });
465
+
466
+ window.addEventListener("hashchange", () => {
467
+ state.page = location.hash === "#matrix" ? "matrix" : "overview";
468
+ renderAll();
469
+ });
470
+ }
471
+
472
+ function renderMarkdownSummary() {
473
+ const md = data.overview?.summary;
474
+ if (!md) return;
475
+ let titleRewritten = false;
476
+ const lines = md.split("\n");
477
+ const parts = [];
478
+ let paragraph = [];
479
+ let list = [];
480
+
481
+ const inline = (text) =>
482
+ escapeHtml(text)
483
+ .replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
484
+ .replace(/`([^`]+)`/g, "<code>$1</code>");
485
+
486
+ const flushParagraph = () => {
487
+ if (!paragraph.length) return;
488
+ parts.push(`<p>${inline(paragraph.join(" "))}</p>`);
489
+ paragraph = [];
490
+ };
491
+
492
+ const flushList = () => {
493
+ if (!list.length) return;
494
+ parts.push(`<ul>${list.map((item) => `<li>${inline(item)}</li>`).join("")}</ul>`);
495
+ list = [];
496
+ };
497
+
498
+ const isTableDivider = (line) => /^\|\s*[-:| ]+\s*\|$/.test(line);
499
+ const cells = (line) => line.split("|").slice(1, -1).map((cell) => inline(cell.trim()));
500
+
501
+ for (let index = 0; index < lines.length; index += 1) {
502
+ const line = lines[index].trim();
503
+ if (!line) {
504
+ flushParagraph();
505
+ flushList();
506
+ continue;
507
+ }
508
+
509
+ if (line.startsWith("|") && lines[index + 1] && isTableDivider(lines[index + 1].trim())) {
510
+ flushParagraph();
511
+ flushList();
512
+ const headers = cells(line).map((cell) => `<th>${cell}</th>`).join("");
513
+ index += 2;
514
+ const rows = [];
515
+ while (index < lines.length && lines[index].trim().startsWith("|")) {
516
+ rows.push(`<tr>${cells(lines[index].trim()).map((cell) => `<td>${cell}</td>`).join("")}</tr>`);
517
+ index += 1;
518
+ }
519
+ index -= 1;
520
+ parts.push(`<div class="table-wrap"><table><thead><tr>${headers}</tr></thead><tbody>${rows.join("")}</tbody></table></div>`);
521
+ continue;
522
+ }
523
+
524
+ const heading = line.match(/^(#{1,3})\s+(.+)$/);
525
+ if (heading) {
526
+ flushParagraph();
527
+ flushList();
528
+ const level = heading[1].length;
529
+ let text = heading[2];
530
+ if (level === 1 && !titleRewritten) {
531
+ text = "Tool Schema Rendering Atlas - Summary";
532
+ titleRewritten = true;
533
+ }
534
+ const tag = level === 1 ? "h2" : level === 2 ? "h3" : "h4";
535
+ parts.push(`<${tag}>${inline(text)}</${tag}>`);
536
+ continue;
537
+ }
538
+
539
+ const date = line.match(/^_(.+)_$/);
540
+ if (date) {
541
+ flushParagraph();
542
+ flushList();
543
+ parts.push(`<p class="summary-date">${inline(date[1])}</p>`);
544
+ continue;
545
+ }
546
+
547
+ const bullet = line.match(/^-\s+(.+)$/);
548
+ if (bullet) {
549
+ flushParagraph();
550
+ list.push(bullet[1]);
551
+ continue;
552
+ }
553
+
554
+ flushList();
555
+ paragraph.push(line);
556
+ }
557
+
558
+ flushParagraph();
559
+ flushList();
560
+ const html = parts.join("");
561
+ $("exec-summary").innerHTML = `<p class="eyebrow">Executive summary</p>${html}`;
562
+ }
563
+
564
+ renderMarkdownSummary();
565
+ bindEvents();
566
+ renderAll();
data.js ADDED
The diff for this file is too large to render. See raw diff
 
index.html CHANGED
@@ -1,19 +1,186 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Tool Schema Rendering Atlas</title>
7
+ <meta
8
+ name="description"
9
+ content="Model-centric report showing how JSON tool definitions are rendered into prompts."
10
+ />
11
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
13
+ <link
14
+ href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300..800;1,9..40,300..800&family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Fira+Code:wght@400;600&display=swap"
15
+ rel="stylesheet"
16
+ />
17
+ <link rel="stylesheet" href="./styles.css" />
18
+ </head>
19
+ <body>
20
+ <main class="app-shell">
21
+ <aside class="model-rail" aria-label="Models">
22
+ <div class="rail-head">
23
+ <p class="eyebrow">Tool Schema Rendering Atlas</p>
24
+ </div>
25
+
26
+ <button id="overview-link" class="overview-link" type="button">
27
+ <strong>Overview &amp; method</strong>
28
+ <span>What models see, and how this report was built</span>
29
+ </button>
30
+
31
+ <div class="legend" id="dialect-filter" aria-label="Dialect filter">
32
+ <button type="button" data-dialect="all" class="active">All</button>
33
+ <button type="button" data-dialect="typescript"><i class="dot typescript"></i>TypeScript</button>
34
+ <button type="button" data-dialect="xml"><i class="dot xml"></i>XML/tags</button>
35
+ <button type="button" data-dialect="json"><i class="dot json"></i>JSON</button>
36
+ <button type="button" data-dialect="custom-channel"><i class="dot custom-channel"></i>Custom</button>
37
+ <button type="button" data-dialect="message-json"><i class="dot message-json"></i>Message JSON</button>
38
+ </div>
39
+
40
+ <div id="model-list" class="model-list"></div>
41
+ </aside>
42
+
43
+ <section class="model-workspace">
44
+ <section id="overview-panel" class="overview-panel" hidden>
45
+ <div class="overview-hero">
46
+ <p class="eyebrow">What Do Models See?</p>
47
+ <h2>Tool schemas become prompt text.</h2>
48
+ <p>
49
+ This atlas compares what a model receives after a JSON tool definition has passed through tokenizer-aware chat templates, provider-style tool renderers, or model-supplied custom renderers.
50
+ </p>
51
+ </div>
52
+
53
+ <div class="method-diagram" aria-label="Methodology diagram">
54
+ <div class="method-step">
55
+ <span>1</span>
56
+ <strong>Tool definition</strong>
57
+ <p>Start from shared OpenAI-style JSON tool definitions covering required strings, nested arrays, enums, refs, nullable unions, and oneOf/allOf stress cases.</p>
58
+ </div>
59
+ <div class="method-arrow">β†’</div>
60
+ <div class="method-step">
61
+ <span>2</span>
62
+ <strong>Tokenizer + template</strong>
63
+ <p>Download the model tokenizer where available, inspect its chat template and tool-use conventions, then render through realistic tokenizer templates or model-supplied custom renderer code.</p>
64
+ </div>
65
+ <div class="method-arrow">β†’</div>
66
+ <div class="method-step">
67
+ <span>3</span>
68
+ <strong>Model-visible output</strong>
69
+ <p>Compare the rendered prompt text, feature survival, special modes, and copied evidence claims so each model’s tool dialect remains auditable.</p>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="method-notes">
74
+ <section>
75
+ <p class="eyebrow">Reading The Report</p>
76
+ <h3>Start with a model, then compare the same input case across dialects.</h3>
77
+ <p>
78
+ The left rail groups models with identical sampled renderings, while the evidence section keeps each underlying packet visible. The right inspector records which schema features survived the template render.
79
+ </p>
80
+ </section>
81
+ <section>
82
+ <p class="eyebrow">Reading The Output</p>
83
+ <h3>The rendered prompt is shown whole.</h3>
84
+ <p>
85
+ Inline highlights call out tool names and schema-bearing keywords, but the report keeps the full rendered text visible so boilerplate, wrappers, and surrounding template instructions remain auditable.
86
+ </p>
87
+ </section>
88
+ </div>
89
+
90
+ <section class="exec-summary" id="exec-summary"></section>
91
+ </section>
92
+
93
+ <div id="matrix-view">
94
+ <header class="workspace-head">
95
+ <div>
96
+ <p class="eyebrow" id="active-family">Selected model</p>
97
+ <h2 id="active-model">Model</h2>
98
+ <p id="active-summary" class="summary-text"></p>
99
+ </div>
100
+ <div class="snapshot">
101
+ <div>
102
+ <span>Dialect</span>
103
+ <strong id="active-dialect">β€”</strong>
104
+ </div>
105
+ <div>
106
+ <span>Support path</span>
107
+ <strong id="active-path">β€”</strong>
108
+ </div>
109
+ <div>
110
+ <span>Coverage</span>
111
+ <strong id="active-coverage">β€”</strong>
112
+ </div>
113
+ </div>
114
+ </header>
115
+
116
+ <section class="studio">
117
+ <div class="prompt-panel">
118
+ <div class="panel-head">
119
+ <div>
120
+ <h3 id="case-title">Same JSON tool schema, model-specific prompt dialect</h3>
121
+ </div>
122
+ <div class="case-picker">
123
+ <span>Example tool definition</span>
124
+ <div id="case-tabs" class="case-tabs" aria-label="Example tool definitions"></div>
125
+ </div>
126
+ </div>
127
+
128
+ <div class="view-tabs" role="tablist" aria-label="Transformation view">
129
+ <button id="input-tab" type="button" data-view="input" role="tab" aria-selected="false">
130
+ Tool Definition
131
+ </button>
132
+ <button id="output-tab" type="button" class="active" data-view="output" role="tab" aria-selected="true">
133
+ Model Visible Output
134
+ </button>
135
+ <div class="tab-tools">
136
+ </div>
137
+ </div>
138
+ <p class="render-caveat">
139
+ Template render evidence only. Hosted APIs or serving layers may normalize, reject, or rewrite tool schemas before the model receives them.
140
+ </p>
141
+
142
+ <pre id="rendered-output" class="code-pane rendered-code" tabindex="0"></pre>
143
+ <pre id="input-schema" class="code-pane input-code" tabindex="0" hidden></pre>
144
+
145
+ <div class="output-legend" aria-label="Inline token highlight legend">
146
+ <span><i class="swatch tool"></i>Tool names</span>
147
+ <span><i class="swatch schema"></i>Schema keywords</span>
148
+ <span><i class="swatch boilerplate"></i>Boilerplate text</span>
149
+ </div>
150
+ </div>
151
+
152
+ <aside class="inspector">
153
+ <div class="inspector-section">
154
+ <p class="eyebrow">Current transformation</p>
155
+ <p id="case-description" class="muted"></p>
156
+ </div>
157
+
158
+ <div class="inspector-section">
159
+ <p class="eyebrow">Feature survival</p>
160
+ <div id="feature-list" class="feature-list"></div>
161
+ </div>
162
+
163
+ <div class="inspector-section">
164
+ <p class="eyebrow">Special modes</p>
165
+ <div id="special-list" class="special-list"></div>
166
+ </div>
167
+ </aside>
168
+ </section>
169
+
170
+ <section class="evidence-packets">
171
+ <div class="panel-head">
172
+ <div>
173
+ <p class="eyebrow">Evidence packet</p>
174
+ <h3>Claims copied from each selected model’s findings JSON</h3>
175
+ </div>
176
+ </div>
177
+ <div id="claims" class="claims"></div>
178
+ </section>
179
+ </div>
180
+ </section>
181
+ </main>
182
+
183
+ <script src="./data.js"></script>
184
+ <script src="./app.js"></script>
185
+ </body>
186
  </html>
styles.css ADDED
@@ -0,0 +1,1076 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ═══════════════════════════════════════════════════════════════
2
+ Tool Schema Rendering Atlas β€” Warm Editorial Theme
3
+ ═══════════════════════════════════════════════════════════════ */
4
+
5
+ :root {
6
+ color-scheme: light;
7
+
8
+ /* ── Surface palette ──────────────────────────────────────── */
9
+ --ink: #1a1d21;
10
+ --muted: #6b7280;
11
+ --paper: #faf8f5;
12
+ --panel: #ffffff;
13
+ --surface: #f5f2ed;
14
+ --line: #e2dfd8;
15
+ --soft: #edeae4;
16
+
17
+ /* ── Dialect accent colors (rich & saturated for light) ──── */
18
+ --typescript: #3b5fc0;
19
+ --xml: #0a7558;
20
+ --json: #a67612;
21
+ --custom-channel: #c43730;
22
+ --message-json: #7c4dba;
23
+ --builtin: #2d6f91;
24
+ --other: #6b7280;
25
+
26
+ /* ── Tinted backgrounds for active states ─────────────────── */
27
+ --tint-ts: rgba(59, 95, 192, 0.07);
28
+ --tint-xml: rgba(10, 117, 88, 0.07);
29
+ --tint-json: rgba(166, 118, 18, 0.07);
30
+ --tint-cc: rgba(196, 55, 48, 0.07);
31
+ --tint-mj: rgba(124, 77, 186, 0.07);
32
+
33
+ --shadow: 0 12px 40px rgba(26, 29, 33, 0.06), 0 2px 8px rgba(26, 29, 33, 0.04);
34
+ --shadow-sm: 0 4px 14px rgba(26, 29, 33, 0.05);
35
+ --shadow-up: 0 -2px 12px rgba(26, 29, 33, 0.04);
36
+
37
+ --mono: "Fira Code", "SFMono-Regular", "Cascadia Code", "Liberation Mono", monospace;
38
+ --sans: "DM Sans", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
39
+ --serif: "Fraunces", "Georgia", serif;
40
+
41
+ font-family: var(--sans);
42
+ font-optical-sizing: auto;
43
+ }
44
+
45
+ * { box-sizing: border-box; }
46
+
47
+ body {
48
+ background: var(--paper);
49
+ color: var(--ink);
50
+ margin: 0;
51
+ -webkit-font-smoothing: antialiased;
52
+ -moz-osx-font-smoothing: grayscale;
53
+ }
54
+
55
+ /* Fine grain texture for a printed/paper feel */
56
+ body::before {
57
+ content: "";
58
+ position: fixed;
59
+ inset: 0;
60
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.018'/%3E%3C/svg%3E");
61
+ pointer-events: none;
62
+ z-index: 9999;
63
+ }
64
+
65
+ button, select { font: inherit; }
66
+ code, pre { font-family: var(--mono); }
67
+
68
+ /* ── Shell Layout ───────────────────────────────────────────── */
69
+
70
+ .app-shell {
71
+ display: grid;
72
+ grid-template-columns: 348px minmax(0, 1fr);
73
+ min-height: 100svh;
74
+ }
75
+
76
+ /* ── Sidebar Rail ───────────────────────────────────────────── */
77
+
78
+ .model-rail {
79
+ background:
80
+ linear-gradient(180deg, #fdfcfa 0%, var(--paper) 100%);
81
+ border-right: 1px solid var(--line);
82
+ height: 100svh;
83
+ overflow: auto;
84
+ padding: 22px 20px;
85
+ position: sticky;
86
+ top: 0;
87
+ }
88
+
89
+ .rail-head { margin-bottom: 12px; }
90
+
91
+ /* ── Overview Link Button ───────────────────────────────────── */
92
+
93
+ .overview-link {
94
+ background: var(--ink);
95
+ border: 1px solid var(--ink);
96
+ border-radius: 10px;
97
+ color: #fff;
98
+ cursor: pointer;
99
+ display: grid;
100
+ gap: 4px;
101
+ margin-bottom: 14px;
102
+ min-height: 60px;
103
+ padding: 12px 14px;
104
+ text-align: left;
105
+ transition: box-shadow 200ms ease, transform 200ms ease;
106
+ width: 100%;
107
+ }
108
+
109
+ .overview-link:hover {
110
+ box-shadow: 0 8px 24px rgba(26, 29, 33, 0.18);
111
+ transform: translateY(-1px);
112
+ }
113
+
114
+ .overview-link:not(.active) {
115
+ background: var(--panel);
116
+ border-color: var(--line);
117
+ color: var(--ink);
118
+ }
119
+
120
+ .overview-link:not(.active):hover {
121
+ border-color: #c8c4bc;
122
+ box-shadow: var(--shadow-sm);
123
+ }
124
+
125
+ .overview-link strong {
126
+ font-size: 14px;
127
+ line-height: 1.25;
128
+ }
129
+
130
+ .overview-link span {
131
+ color: rgba(255, 255, 255, 0.62);
132
+ font-size: 12px;
133
+ font-weight: 620;
134
+ line-height: 1.35;
135
+ }
136
+
137
+ .overview-link:not(.active) span { color: var(--muted); }
138
+
139
+ /* ── Shared Text Styles ─────────────────────────────────────── */
140
+
141
+ .summary-text,
142
+ .muted {
143
+ color: var(--muted);
144
+ line-height: 1.52;
145
+ }
146
+
147
+ .eyebrow {
148
+ color: #8c8a84;
149
+ font-family: var(--sans);
150
+ font-size: 10.5px;
151
+ font-weight: 700;
152
+ letter-spacing: 0.16em;
153
+ margin: 0 0 9px;
154
+ text-transform: uppercase;
155
+ }
156
+
157
+ select {
158
+ background: var(--panel);
159
+ border: 1px solid var(--line);
160
+ border-radius: 8px;
161
+ color: var(--ink);
162
+ min-height: 42px;
163
+ padding: 0 12px;
164
+ width: 100%;
165
+ }
166
+
167
+ /* ── Dialect Filter Legend ────────────��──────────────────────── */
168
+
169
+ .legend {
170
+ display: flex;
171
+ flex-wrap: wrap;
172
+ gap: 6px;
173
+ margin: 16px 0;
174
+ }
175
+
176
+ .legend button {
177
+ align-items: center;
178
+ background: var(--panel);
179
+ border: 1px solid var(--line);
180
+ border-radius: 999px;
181
+ color: var(--muted);
182
+ cursor: pointer;
183
+ display: inline-flex;
184
+ font-size: 11px;
185
+ font-weight: 650;
186
+ gap: 5px;
187
+ min-height: 27px;
188
+ padding: 0 10px;
189
+ transition: background 140ms ease, border-color 140ms ease, color 140ms ease, box-shadow 140ms ease;
190
+ }
191
+
192
+ .legend button:hover {
193
+ border-color: #c8c4bc;
194
+ color: var(--ink);
195
+ }
196
+
197
+ .legend button.active {
198
+ background: var(--ink);
199
+ border-color: var(--ink);
200
+ color: #fff;
201
+ box-shadow: 0 2px 8px rgba(26, 29, 33, 0.15);
202
+ }
203
+
204
+ /* ── Dialect Dots ───────────────────────────────────────────── */
205
+
206
+ .dot {
207
+ border-radius: 50%;
208
+ display: inline-block;
209
+ height: 8px;
210
+ width: 8px;
211
+ }
212
+
213
+ .dot.typescript, .model-card.typescript::before { background: var(--typescript); }
214
+ .dot.xml, .model-card.xml::before { background: var(--xml); }
215
+ .dot.json, .model-card.json::before { background: var(--json); }
216
+ .dot.custom-channel, .model-card.custom-channel::before { background: var(--custom-channel); }
217
+ .dot.message-json, .model-card.message-json::before { background: var(--message-json); }
218
+
219
+ /* ── Model Card List ────────────────────────────────────────── */
220
+
221
+ .model-list {
222
+ display: grid;
223
+ gap: 6px;
224
+ }
225
+
226
+ .model-card {
227
+ background: var(--panel);
228
+ border: 1px solid var(--line);
229
+ border-radius: 9px;
230
+ color: var(--ink);
231
+ cursor: pointer;
232
+ display: grid;
233
+ gap: 3px;
234
+ overflow: hidden;
235
+ min-height: 52px;
236
+ padding: 10px 12px 10px 17px;
237
+ position: relative;
238
+ text-align: left;
239
+ transition: background 160ms ease, transform 160ms ease, border-color 160ms ease, opacity 160ms ease, box-shadow 160ms ease;
240
+ }
241
+
242
+ .model-card::before {
243
+ content: "";
244
+ inset: 0 auto 0 0;
245
+ position: absolute;
246
+ width: 4px;
247
+ }
248
+
249
+ .model-list:has(.model-card.active) .model-card:not(.active) { opacity: 0.55; }
250
+ .model-list:has(.model-card.active) .model-card:not(.active):hover { opacity: 1; }
251
+
252
+ .model-card:hover {
253
+ border-color: #c8c4bc;
254
+ box-shadow: var(--shadow-sm);
255
+ transform: translateY(-1px);
256
+ }
257
+
258
+ /* ── Per-dialect active glow tints ──────────────────────────── */
259
+
260
+ .model-card.active {
261
+ background: var(--tint-xml);
262
+ border-color: var(--xml);
263
+ box-shadow: 0 0 0 3px rgba(10, 117, 88, 0.08), var(--shadow-sm);
264
+ }
265
+
266
+ .model-card.typescript.active {
267
+ background: var(--tint-ts);
268
+ border-color: var(--typescript);
269
+ box-shadow: 0 0 0 3px rgba(59, 95, 192, 0.08), var(--shadow-sm);
270
+ }
271
+
272
+ .model-card.json.active {
273
+ background: var(--tint-json);
274
+ border-color: var(--json);
275
+ box-shadow: 0 0 0 3px rgba(166, 118, 18, 0.08), var(--shadow-sm);
276
+ }
277
+
278
+ .model-card.custom-channel.active {
279
+ background: var(--tint-cc);
280
+ border-color: var(--custom-channel);
281
+ box-shadow: 0 0 0 3px rgba(196, 55, 48, 0.08), var(--shadow-sm);
282
+ }
283
+
284
+ .model-card.message-json.active {
285
+ background: var(--tint-mj);
286
+ border-color: var(--message-json);
287
+ box-shadow: 0 0 0 3px rgba(124, 77, 186, 0.08), var(--shadow-sm);
288
+ }
289
+
290
+ .model-card strong {
291
+ font-size: 13.5px;
292
+ font-weight: 650;
293
+ line-height: 1.25;
294
+ }
295
+
296
+ .model-card span {
297
+ color: var(--muted);
298
+ font-size: 11px;
299
+ font-weight: 600;
300
+ text-transform: capitalize;
301
+ }
302
+
303
+ /* ── Summary Bar ────────────────────────────────────────────── */
304
+
305
+ .summary-text {
306
+ background: var(--surface);
307
+ border-left: 4px solid var(--custom-channel);
308
+ border-radius: 6px;
309
+ color: #4a4f57;
310
+ display: -webkit-box;
311
+ font-size: 14px;
312
+ font-weight: 560;
313
+ height: 72px;
314
+ line-clamp: 3;
315
+ line-height: 1.48;
316
+ margin: 0;
317
+ max-width: 780px;
318
+ overflow: hidden;
319
+ padding: 11px 14px;
320
+ -webkit-box-orient: vertical;
321
+ -webkit-line-clamp: 3;
322
+ }
323
+
324
+ /* ── Workspace ──────────────────────────────────────────────── */
325
+
326
+ .model-workspace {
327
+ min-width: 0;
328
+ padding: 28px clamp(24px, 4vw, 48px) 58px;
329
+ }
330
+
331
+ .workspace-head {
332
+ align-items: end;
333
+ display: grid;
334
+ gap: 24px;
335
+ grid-template-columns: minmax(0, 1fr) minmax(400px, 0.72fr);
336
+ margin-bottom: 24px;
337
+ }
338
+
339
+ /* ── Overview Panel ─────────────────────────────────────────── */
340
+
341
+ .overview-panel { display: grid; gap: 24px; }
342
+ .overview-panel[hidden] { display: none; }
343
+
344
+ .overview-hero {
345
+ border-bottom: 1px solid var(--line);
346
+ padding: 26px 0 30px;
347
+ }
348
+
349
+ .overview-hero h2 { max-width: 880px; }
350
+
351
+ .overview-hero p:not(.eyebrow) {
352
+ color: #4a4f57;
353
+ font-size: 19px;
354
+ font-weight: 480;
355
+ line-height: 1.52;
356
+ margin: 0;
357
+ max-width: 860px;
358
+ }
359
+
360
+ /* ── Method Diagram ─────────────────────────────────────────── */
361
+
362
+ .method-diagram {
363
+ align-items: stretch;
364
+ display: grid;
365
+ gap: 12px;
366
+ grid-template-columns: minmax(0, 1fr) 32px minmax(0, 1fr) 32px minmax(0, 1fr);
367
+ }
368
+
369
+ .method-step {
370
+ background: var(--panel);
371
+ border: 1px solid var(--line);
372
+ border-radius: 10px;
373
+ box-shadow: var(--shadow);
374
+ display: grid;
375
+ gap: 9px;
376
+ min-height: 190px;
377
+ padding: 18px;
378
+ position: relative;
379
+ }
380
+
381
+ .method-step::before {
382
+ background: var(--xml);
383
+ border-radius: 10px 0 0 10px;
384
+ content: "";
385
+ inset: 0 auto 0 0;
386
+ position: absolute;
387
+ width: 4px;
388
+ }
389
+
390
+ .method-step span {
391
+ align-items: center;
392
+ background: var(--xml);
393
+ border-radius: 999px;
394
+ color: #fff;
395
+ display: inline-flex;
396
+ font-size: 12px;
397
+ font-weight: 800;
398
+ height: 26px;
399
+ justify-content: center;
400
+ width: 26px;
401
+ }
402
+
403
+ .method-step strong {
404
+ font-family: var(--serif);
405
+ font-size: 19px;
406
+ font-weight: 700;
407
+ }
408
+
409
+ .method-step p,
410
+ .method-notes p {
411
+ color: var(--muted);
412
+ line-height: 1.55;
413
+ margin: 0;
414
+ }
415
+
416
+ .method-arrow {
417
+ align-self: center;
418
+ color: #c0bdb5;
419
+ font-size: 24px;
420
+ font-weight: 850;
421
+ justify-self: center;
422
+ }
423
+
424
+ .method-notes {
425
+ display: grid;
426
+ gap: 24px;
427
+ grid-template-columns: repeat(2, minmax(0, 1fr));
428
+ }
429
+
430
+ .method-notes section {
431
+ border-top: 1px solid var(--line);
432
+ padding-top: 18px;
433
+ }
434
+
435
+ .method-notes h3 {
436
+ margin-bottom: 10px;
437
+ max-width: 560px;
438
+ }
439
+
440
+ /* ── Headings ───────────────────────────────────────────────── */
441
+
442
+ h2 {
443
+ font-family: var(--serif);
444
+ font-size: clamp(36px, 5vw, 62px);
445
+ font-weight: 800;
446
+ letter-spacing: -0.02em;
447
+ line-height: 0.96;
448
+ margin: 0 0 14px;
449
+ overflow-wrap: anywhere;
450
+ }
451
+
452
+ h3 {
453
+ font-family: var(--serif);
454
+ font-size: 20px;
455
+ font-weight: 650;
456
+ margin: 0;
457
+ }
458
+
459
+ /* ── Snapshot Cards ─────────────────────────────────────────── */
460
+
461
+ .snapshot {
462
+ background: var(--panel);
463
+ border: 1px solid var(--line);
464
+ border-radius: 10px;
465
+ display: grid;
466
+ grid-template-columns: repeat(3, 1fr);
467
+ }
468
+
469
+ .snapshot div {
470
+ min-width: 0;
471
+ padding: 14px 16px;
472
+ }
473
+
474
+ .snapshot div + div {
475
+ border-left: 1px solid var(--line);
476
+ }
477
+
478
+ .snapshot span {
479
+ color: var(--muted);
480
+ display: block;
481
+ font-size: 10px;
482
+ font-weight: 700;
483
+ letter-spacing: 0.12em;
484
+ margin-bottom: 6px;
485
+ text-transform: uppercase;
486
+ }
487
+
488
+ .snapshot strong {
489
+ color: var(--ink);
490
+ display: block;
491
+ font-size: 13px;
492
+ font-weight: 620;
493
+ line-height: 1.25;
494
+ text-transform: capitalize;
495
+ }
496
+
497
+ /* ── Studio Layout ──────────────────────────────────────────── */
498
+
499
+ .studio {
500
+ align-items: start;
501
+ display: grid;
502
+ gap: 16px;
503
+ grid-template-columns: minmax(0, 1fr) 330px;
504
+ }
505
+
506
+ .prompt-panel,
507
+ .inspector,
508
+ .evidence-packets {
509
+ background: var(--panel);
510
+ border: 1px solid var(--line);
511
+ border-radius: 10px;
512
+ box-shadow: var(--shadow);
513
+ overflow: hidden;
514
+ }
515
+
516
+ .panel-head {
517
+ align-items: end;
518
+ border-bottom: 1px solid var(--line);
519
+ display: grid;
520
+ gap: 16px;
521
+ grid-template-columns: minmax(250px, 0.72fr) minmax(0, 1.28fr);
522
+ padding: 16px 18px;
523
+ }
524
+
525
+ /* ── Case Picker ────────────────────────────────────────────── */
526
+
527
+ .case-picker {
528
+ display: grid;
529
+ gap: 6px;
530
+ justify-items: end;
531
+ }
532
+
533
+ .case-picker > span {
534
+ color: var(--muted);
535
+ font-size: 10px;
536
+ font-weight: 700;
537
+ letter-spacing: 0.1em;
538
+ text-transform: uppercase;
539
+ }
540
+
541
+ /* ── View Tabs ──────────────────────────────────────────────── */
542
+
543
+ .view-tabs {
544
+ align-items: end;
545
+ background: var(--surface);
546
+ border-bottom: 1px solid var(--line);
547
+ display: flex;
548
+ flex-wrap: wrap;
549
+ gap: 4px;
550
+ min-height: 58px;
551
+ padding: 10px 18px 0;
552
+ }
553
+
554
+ .view-tabs button {
555
+ background: transparent;
556
+ border: 1px solid transparent;
557
+ border-radius: 8px 8px 0 0;
558
+ color: var(--muted);
559
+ cursor: pointer;
560
+ font-weight: 700;
561
+ min-height: 38px;
562
+ padding: 0 13px;
563
+ transition: color 120ms ease, background 120ms ease;
564
+ }
565
+
566
+ .view-tabs button:hover { color: var(--ink); }
567
+
568
+ .view-tabs button.active {
569
+ background: var(--panel);
570
+ border-color: var(--line);
571
+ border-bottom-color: var(--panel);
572
+ color: var(--ink);
573
+ }
574
+
575
+ .tab-tools {
576
+ align-items: center;
577
+ display: flex;
578
+ flex: 1 1 420px;
579
+ flex-wrap: wrap;
580
+ gap: 10px;
581
+ justify-content: flex-end;
582
+ min-height: 38px;
583
+ padding-bottom: 8px;
584
+ }
585
+
586
+ /* ── Render Caveat Banner ───────────────────────────────────── */
587
+
588
+ .render-caveat {
589
+ background: #fef9ee;
590
+ border-bottom: 1px solid var(--line);
591
+ color: #7d5a15;
592
+ font-size: 11.5px;
593
+ font-weight: 620;
594
+ line-height: 1.5;
595
+ margin: 0;
596
+ padding: 7px 18px;
597
+ }
598
+
599
+ /* ── Empty Render State ─────────────────────────────────────── */
600
+
601
+ .empty-render {
602
+ color: rgba(230, 240, 234, 0.65);
603
+ display: block;
604
+ font-family: var(--sans);
605
+ font-size: 14px;
606
+ line-height: 1.55;
607
+ max-width: 600px;
608
+ }
609
+
610
+ .empty-render strong { color: #ffffff; }
611
+
612
+ /* ── Code Panes ─────────────────────────────────────────────── */
613
+
614
+ pre,
615
+ .code-pane {
616
+ background: #111714;
617
+ border-radius: 0;
618
+ color: #d8ede0;
619
+ font-size: 12.5px;
620
+ line-height: 1.6;
621
+ margin: 0;
622
+ max-height: 68svh;
623
+ min-height: 520px;
624
+ overflow: auto;
625
+ padding: 20px;
626
+ white-space: pre-wrap;
627
+ word-break: break-word;
628
+ }
629
+
630
+ .input-code {
631
+ background: #17130f;
632
+ color: #e8dcc8;
633
+ }
634
+
635
+ /* ── Token Highlights ───────────────────────────────────────── */
636
+
637
+ .tool-token {
638
+ background: rgba(10, 117, 88, 0.25);
639
+ border-radius: 3px;
640
+ color: #ffffff;
641
+ font-weight: 750;
642
+ padding: 0 3px;
643
+ }
644
+
645
+ .schema-token-output {
646
+ background: rgba(166, 118, 18, 0.2);
647
+ border-radius: 3px;
648
+ color: #ffe9b4;
649
+ font-weight: 680;
650
+ padding: 0 3px;
651
+ }
652
+
653
+ .boilerplate-token {
654
+ color: rgba(216, 237, 224, 0.36);
655
+ }
656
+
657
+ .schema-token {
658
+ color: #f0c75e;
659
+ font-weight: 750;
660
+ }
661
+
662
+ /* ── Inspector Panel ────────────────────────────────────────── */
663
+
664
+ .inspector { overflow: hidden; }
665
+
666
+ .inspector-section { padding: 16px 18px; }
667
+
668
+ .inspector-section + .inspector-section {
669
+ border-top: 1px solid var(--line);
670
+ }
671
+
672
+ /* ── Case Tabs ──────────────────────────────────────────────── */
673
+
674
+ .case-tabs {
675
+ display: flex;
676
+ flex-wrap: wrap;
677
+ gap: 5px;
678
+ justify-content: flex-end;
679
+ }
680
+
681
+ .case-tabs button {
682
+ background: var(--surface);
683
+ border: 1px solid var(--line);
684
+ border-radius: 999px;
685
+ color: var(--ink);
686
+ cursor: pointer;
687
+ font-size: 11px;
688
+ font-weight: 600;
689
+ min-height: 28px;
690
+ padding: 0 10px;
691
+ text-align: left;
692
+ text-transform: capitalize;
693
+ transition: background 120ms ease, border-color 120ms ease;
694
+ }
695
+
696
+ .case-tabs button:hover {
697
+ border-color: #c8c4bc;
698
+ }
699
+
700
+ .case-tabs button.active {
701
+ background: rgba(10, 117, 88, 0.08);
702
+ border-color: rgba(10, 117, 88, 0.35);
703
+ color: #06614a;
704
+ font-weight: 700;
705
+ }
706
+
707
+ .case-tabs button.unsupported {
708
+ border-color: rgba(166, 118, 18, 0.35);
709
+ color: #7d5a15;
710
+ }
711
+
712
+ .case-tabs button.unsupported::after {
713
+ align-items: center;
714
+ background: #fef3d6;
715
+ border-radius: 50%;
716
+ color: #a67612;
717
+ content: "!";
718
+ display: inline-flex;
719
+ font-size: 10px;
720
+ font-weight: 900;
721
+ height: 14px;
722
+ justify-content: center;
723
+ margin-left: 5px;
724
+ width: 14px;
725
+ }
726
+
727
+ /* ── Feature / Special Lists ────────────────────────────────── */
728
+
729
+ .feature-list,
730
+ .special-list,
731
+ .claims {
732
+ display: grid;
733
+ gap: 6px;
734
+ }
735
+
736
+ /* ── Output Legend ───────────────────────────────────────────── */
737
+
738
+ .output-legend {
739
+ align-items: center;
740
+ background: var(--surface);
741
+ border-top: 1px solid var(--line);
742
+ display: flex;
743
+ flex-wrap: wrap;
744
+ gap: 14px;
745
+ padding: 8px 18px;
746
+ }
747
+
748
+ .output-legend span {
749
+ align-items: center;
750
+ color: var(--muted);
751
+ display: flex;
752
+ font-size: 11px;
753
+ font-weight: 650;
754
+ gap: 7px;
755
+ }
756
+
757
+ .swatch {
758
+ border-radius: 999px;
759
+ display: inline-block;
760
+ height: 8px;
761
+ width: 20px;
762
+ }
763
+
764
+ .swatch.tool { background: rgba(10, 117, 88, 0.3); }
765
+ .swatch.schema { background: rgba(166, 118, 18, 0.25); }
766
+ .swatch.boilerplate { background: rgba(26, 29, 33, 0.12); }
767
+
768
+ /* ── Feature / Special Rows ─────────────────────────────────── */
769
+
770
+ .feature-row,
771
+ .special-row {
772
+ align-items: center;
773
+ background: transparent;
774
+ border: 0;
775
+ border-bottom: 1px solid var(--line);
776
+ color: inherit;
777
+ cursor: default;
778
+ display: grid;
779
+ gap: 10px;
780
+ grid-template-columns: minmax(104px, 0.5fr) minmax(0, 1fr);
781
+ padding: 8px 0;
782
+ text-align: left;
783
+ width: 100%;
784
+ }
785
+
786
+ .special-row { cursor: pointer; }
787
+ .special-row.note-row { cursor: default; }
788
+
789
+ .special-row small {
790
+ color: var(--muted);
791
+ font-size: 11px;
792
+ grid-column: 1 / -1;
793
+ line-height: 1.45;
794
+ }
795
+
796
+ .special-row .evidence-path {
797
+ font-family: var(--mono);
798
+ overflow-wrap: anywhere;
799
+ }
800
+
801
+ .feature-row:last-child,
802
+ .special-row:last-child { border-bottom: 0; }
803
+
804
+ .feature-row strong,
805
+ .special-row strong {
806
+ font-size: 12.5px;
807
+ font-weight: 650;
808
+ text-transform: capitalize;
809
+ }
810
+
811
+ /* ── Status Badges ──────────────────────────────────────────── */
812
+
813
+ .status {
814
+ border-radius: 999px;
815
+ display: inline-flex;
816
+ font-family: var(--mono);
817
+ font-size: 10.5px;
818
+ line-height: 1.25;
819
+ min-height: 24px;
820
+ padding: 4px 8px;
821
+ overflow-wrap: anywhere;
822
+ }
823
+
824
+ .status.good {
825
+ background: #e6f6ed;
826
+ color: #0a7558;
827
+ }
828
+
829
+ .status.warn {
830
+ background: #fef3d6;
831
+ color: #7d5a15;
832
+ }
833
+
834
+ .status.na {
835
+ background: var(--surface);
836
+ color: #a3a09a;
837
+ }
838
+
839
+ /* ── Evidence Packets ───────────────────────────────────────── */
840
+
841
+ .evidence-packets { margin-top: 18px; }
842
+ .evidence-packets .panel-head { grid-template-columns: 1fr; }
843
+ .claims { padding: 18px; }
844
+
845
+ .packet-group { display: grid; gap: 8px; }
846
+
847
+ .packet-group + .packet-group {
848
+ border-top: 1px solid var(--line);
849
+ margin-top: 12px;
850
+ padding-top: 18px;
851
+ }
852
+
853
+ .packet-group h4 {
854
+ font-size: 13px;
855
+ font-weight: 650;
856
+ margin: 0;
857
+ overflow-wrap: anywhere;
858
+ }
859
+
860
+ .claim {
861
+ border: 1px solid var(--line);
862
+ border-radius: 9px;
863
+ overflow: hidden;
864
+ transition: border-color 120ms ease;
865
+ }
866
+
867
+ .claim:hover {
868
+ border-color: #c8c4bc;
869
+ }
870
+
871
+ .claim summary {
872
+ align-items: center;
873
+ cursor: pointer;
874
+ display: grid;
875
+ gap: 12px;
876
+ grid-template-columns: 160px minmax(0, 1fr);
877
+ padding: 12px 14px;
878
+ transition: background 120ms ease;
879
+ }
880
+
881
+ .claim summary:hover { background: var(--surface); }
882
+
883
+ .claim summary strong {
884
+ color: var(--muted);
885
+ font-size: 11px;
886
+ text-transform: uppercase;
887
+ }
888
+
889
+ .claim p {
890
+ line-height: 1.5;
891
+ margin: 0;
892
+ }
893
+
894
+ .claim-body {
895
+ background: var(--surface);
896
+ border-top: 1px solid var(--line);
897
+ color: var(--muted);
898
+ display: grid;
899
+ gap: 10px;
900
+ padding: 14px;
901
+ }
902
+
903
+ .evidence-item {
904
+ background: #111714;
905
+ border-radius: 6px;
906
+ color: #d8ede0;
907
+ font-family: var(--mono);
908
+ font-size: 11.5px;
909
+ line-height: 1.5;
910
+ padding: 10px;
911
+ white-space: pre-wrap;
912
+ word-break: break-word;
913
+ }
914
+
915
+ /* ── Executive Summary Section ───────────────────────────────── */
916
+
917
+ .exec-summary {
918
+ border-top: 1px solid var(--line);
919
+ padding-top: 24px;
920
+ }
921
+
922
+ .exec-summary:empty { display: none; }
923
+
924
+ .exec-summary h2 {
925
+ font-size: 28px;
926
+ margin: 28px 0 10px;
927
+ }
928
+
929
+ .exec-summary h3 {
930
+ font-size: 18px;
931
+ margin: 24px 0 8px;
932
+ }
933
+
934
+ .exec-summary h4 {
935
+ font-family: var(--sans);
936
+ font-size: 14px;
937
+ font-weight: 700;
938
+ margin: 18px 0 6px;
939
+ }
940
+
941
+ .exec-summary p {
942
+ color: #4a4f57;
943
+ line-height: 1.6;
944
+ margin: 0 0 8px;
945
+ max-width: 860px;
946
+ }
947
+
948
+ .exec-summary .summary-date {
949
+ color: var(--muted);
950
+ font-style: italic;
951
+ }
952
+
953
+ .exec-summary code {
954
+ background: var(--surface);
955
+ border: 1px solid var(--line);
956
+ border-radius: 4px;
957
+ font-size: 0.88em;
958
+ padding: 1px 5px;
959
+ }
960
+
961
+ .exec-summary ul {
962
+ color: #4a4f57;
963
+ line-height: 1.6;
964
+ margin: 0 0 12px;
965
+ max-width: 860px;
966
+ padding-left: 20px;
967
+ }
968
+
969
+ .exec-summary li { margin-bottom: 4px; }
970
+
971
+ .exec-summary .table-wrap {
972
+ margin: 16px 0;
973
+ overflow-x: auto;
974
+ -webkit-overflow-scrolling: touch;
975
+ }
976
+
977
+ .exec-summary table {
978
+ border-collapse: collapse;
979
+ font-size: 12.5px;
980
+ min-width: 700px;
981
+ width: 100%;
982
+ }
983
+
984
+ .exec-summary th {
985
+ background: var(--surface);
986
+ border: 1px solid var(--line);
987
+ font-weight: 700;
988
+ padding: 8px 10px;
989
+ text-align: left;
990
+ white-space: nowrap;
991
+ }
992
+
993
+ .exec-summary td {
994
+ border: 1px solid var(--line);
995
+ padding: 7px 10px;
996
+ vertical-align: top;
997
+ }
998
+
999
+ .exec-summary tbody tr:hover {
1000
+ background: rgba(10, 117, 88, 0.03);
1001
+ }
1002
+
1003
+ /* ── Animations ─────────────────────────────────────────────── */
1004
+
1005
+ @keyframes fadeSlideIn {
1006
+ from { opacity: 0; transform: translateY(10px); }
1007
+ to { opacity: 1; transform: translateY(0); }
1008
+ }
1009
+
1010
+ .workspace-head,
1011
+ .studio,
1012
+ .evidence-packets,
1013
+ .overview-hero,
1014
+ .method-diagram,
1015
+ .method-notes,
1016
+ .exec-summary {
1017
+ animation: fadeSlideIn 400ms ease both;
1018
+ }
1019
+
1020
+ .overview-hero { animation-delay: 40ms; }
1021
+ .method-diagram { animation-delay: 140ms; }
1022
+ .method-notes { animation-delay: 240ms; }
1023
+ .exec-summary { animation-delay: 340ms; }
1024
+ .workspace-head { animation-delay: 40ms; }
1025
+ .studio { animation-delay: 100ms; }
1026
+ .evidence-packets { animation-delay: 180ms; }
1027
+
1028
+ /* ── Responsive ─────────────────────────────────────────────── */
1029
+
1030
+ @media (max-width: 1120px) {
1031
+ .app-shell,
1032
+ .workspace-head,
1033
+ .studio,
1034
+ .method-diagram,
1035
+ .method-notes {
1036
+ grid-template-columns: 1fr;
1037
+ }
1038
+
1039
+ .method-arrow { transform: rotate(90deg); }
1040
+
1041
+ .model-rail {
1042
+ height: auto;
1043
+ position: relative;
1044
+ }
1045
+
1046
+ .model-list {
1047
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
1048
+ }
1049
+
1050
+ pre,
1051
+ .code-pane { max-height: none; }
1052
+ }
1053
+
1054
+ @media (max-width: 680px) {
1055
+ .model-rail,
1056
+ .model-workspace { padding: 16px; }
1057
+
1058
+ h2 { font-size: 32px; }
1059
+
1060
+ .snapshot,
1061
+ .panel-head,
1062
+ .claim summary { grid-template-columns: 1fr; }
1063
+
1064
+ .case-tabs { justify-content: flex-start; }
1065
+ .view-tabs { align-items: start; }
1066
+
1067
+ .tab-tools {
1068
+ justify-content: flex-start;
1069
+ visibility: visible !important;
1070
+ }
1071
+
1072
+ .snapshot div + div {
1073
+ border-left: 0;
1074
+ border-top: 1px solid var(--line);
1075
+ }
1076
+ }