@fasl-work/caos-app-shell 0.19.12 → 0.20.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/CHANGELOG.md +17 -0
- package/VERSION +1 -1
- package/bin/caos-shell-gate.mjs +5 -2
- package/dist/chart.d.ts +2 -2
- package/dist/chart.js +2 -2
- package/dist/{chunk-FXPSZD6X.js → chunk-FPPJQC2J.js} +5 -0
- package/dist/index.d.ts +202 -6
- package/dist/index.js +302 -3
- package/dist/{tokens-BvU9bFPw.d.ts → tokens-CIdhVv-t.d.ts} +1 -1
- package/gate/inpage.mjs +68 -0
- package/gate/run.mjs +160 -6
- package/package.json +1 -1
- package/reserved-classes.json +60 -0
- package/styles.css +100 -4
package/gate/run.mjs
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// are JSON, not the HTML fallback; a missing asset answers 404; doc text clears a floor
|
|
10
10
|
// G5 scroll and reach boxes inside the viewport, the rail and their scrollers; tall views scroll; nothing is
|
|
11
11
|
// cut; truncated text carries its full text; a select shows its whole selected text (0.17.0); every control reachable by the pointer
|
|
12
|
-
// G6 painted area drawing surfaces have painted pixels, fill their stage, and cover at least
|
|
13
|
-
// viewport on the App route, measured on the screenshot, never on a host box
|
|
12
|
+
// G6 painted area drawing surfaces have painted pixels, fill their stage, and cover at least 60% of the
|
|
13
|
+
// viewport on the App route, measured on the screenshot, never on a host box (ADR-0082)
|
|
14
14
|
// G7 walk everything every route, tab, sub-tab and case, at five sizes, both themes, both languages,
|
|
15
15
|
// waiting for declared state (data-state, data-stale, data-drawn), never networkidle
|
|
16
16
|
// G8 idle at rest no sustained animation frames or DOM mutations after load and after a tab switch
|
|
@@ -55,7 +55,7 @@ export const DEFAULTS = {
|
|
|
55
55
|
idleMs: 3000,
|
|
56
56
|
settleMs: 15000,
|
|
57
57
|
textFloor: 400,
|
|
58
|
-
instrumentMin: 0.
|
|
58
|
+
instrumentMin: 0.6,
|
|
59
59
|
stageFillMin: 0.3,
|
|
60
60
|
stageFloor: 140,
|
|
61
61
|
viewportMinWidth: 1280,
|
|
@@ -64,6 +64,7 @@ export const DEFAULTS = {
|
|
|
64
64
|
wideFont: true,
|
|
65
65
|
wideFontSizes: '390x844,1280x800',
|
|
66
66
|
requireFocus: false,
|
|
67
|
+
requireShowcase: false,
|
|
67
68
|
};
|
|
68
69
|
|
|
69
70
|
/** The wide fallback font of the G10 pass: wider than the Windows and macOS sans fonts; DejaVu Sans is what a Linux
|
|
@@ -243,7 +244,6 @@ export async function runGate(o) {
|
|
|
243
244
|
if (res.status() !== 404) fail('G4', { route: '/__caos_gate_missing__.json' }, `a missing asset answered ${res.status()}, not 404 (an SPA fallback hides missing artifacts and broken deep links)`);
|
|
244
245
|
await ctx.close();
|
|
245
246
|
}
|
|
246
|
-
|
|
247
247
|
// ---- G1: the subject, and the route list (from the header nav unless given).
|
|
248
248
|
let routes = opt.routes ? String(opt.routes).split(',').map((r) => r.trim()).filter(Boolean) : null;
|
|
249
249
|
{
|
|
@@ -265,6 +265,7 @@ export async function runGate(o) {
|
|
|
265
265
|
sink.splice(0);
|
|
266
266
|
await ctx.close();
|
|
267
267
|
}
|
|
268
|
+
const storyRoutes = new Set(routes.filter((route) => /^\/story(?:\/|$)/.test(route)));
|
|
268
269
|
|
|
269
270
|
// ---- G18 on a surface App: under --require-focus the App route must be among the measured routes, or nothing would be measured
|
|
270
271
|
if (opt.requireFocus && workbenchRoutes.size === 0 && !routes.includes(appRoute)) {
|
|
@@ -380,7 +381,7 @@ export async function runGate(o) {
|
|
|
380
381
|
|
|
381
382
|
if (primary) {
|
|
382
383
|
// ADR-0071 rule 9 / G5: every header route reached by a real pointer click from the App route.
|
|
383
|
-
for (const route of routes.filter((r) => r !== workbenchRoute)) {
|
|
384
|
+
for (const route of routes.filter((r) => r !== workbenchRoute && !storyRoutes.has(r))) {
|
|
384
385
|
await page.goto(urlFor(workbenchRoute), { waitUntil: 'load', timeout: 30000 }).catch(() => undefined);
|
|
385
386
|
await settle(page);
|
|
386
387
|
const link = page.locator('header nav a[href]');
|
|
@@ -408,6 +409,159 @@ export async function runGate(o) {
|
|
|
408
409
|
}
|
|
409
410
|
}
|
|
410
411
|
|
|
412
|
+
// ---- G21 (ADR-0082): a required showcase story must be a real, directly addressable 3–6 step experience.
|
|
413
|
+
if (opt.requireShowcase) {
|
|
414
|
+
const stories = routes.filter((route) => /^\/story(?:\/|$)/.test(route));
|
|
415
|
+
if (stories.length === 0) {
|
|
416
|
+
fail('G21', { route: '/story', mode: 'routes' }, 'no /story/<step> route is declared; ADR-0082 requires a directly addressable guided story');
|
|
417
|
+
} else {
|
|
418
|
+
const size = primarySize;
|
|
419
|
+
const [w, h] = size;
|
|
420
|
+
const mode = `${w}x${h} ${themes[0]} ${langs[0]} story`;
|
|
421
|
+
const ctx = await openContext(size, themes[0], langs[0]);
|
|
422
|
+
const { page, sink } = await newPage(ctx);
|
|
423
|
+
const visited = [];
|
|
424
|
+
for (const route of stories) {
|
|
425
|
+
const at = { route, mode };
|
|
426
|
+
const res = await page.goto(urlFor(route), { waitUntil: 'load', timeout: 30000 }).catch((e) => e);
|
|
427
|
+
const status = res && typeof res.status === 'function' ? res.status() : `error ${res?.message ?? res}`;
|
|
428
|
+
if (status !== 200) { fail('G21', at, `direct story link answered ${status}`); continue; }
|
|
429
|
+
const settled = await settle(page);
|
|
430
|
+
if (settled) fail('G21', at, `story did not settle: ${settled}`);
|
|
431
|
+
const f = await page.evaluate(() => window.__caosGate.showcaseFacts());
|
|
432
|
+
if (!f.present) { fail('G21', at, 'route does not render [data-story]'); continue; }
|
|
433
|
+
if (f.count < 3 || f.count > 6) fail('G21', at, `story has ${f.count} visible steps; ADR-0082 requires 3 to 6`);
|
|
434
|
+
if (!f.headline || !/\d/.test(f.headline)) fail('G21', at, `step headline does not state a numeric finding ("${f.headline}")`);
|
|
435
|
+
if (f.headlineWords > 12) fail('G21', at, `finding headline has ${f.headlineWords} words; ADR-0082 caps it at 12`);
|
|
436
|
+
if (!f.stage || !f.panel || !f.layout) fail('G21', at, 'story needs one visible stage and one docked panel');
|
|
437
|
+
if (f.layout && w >= opt.viewportMinWidth) {
|
|
438
|
+
const share = (f.layout.width * f.layout.height) / (w * h);
|
|
439
|
+
if (share < 0.8) fail('G21', at, `story stage and docked panel cover ${(100 * share).toFixed(1)}% of the viewport (floor 80%, ADR-0082)`);
|
|
440
|
+
}
|
|
441
|
+
if (f.calls.length < 3) fail('G21', at, `stage has ${f.calls.length} visible anchored callouts; ADR-0082 requires at least 3`);
|
|
442
|
+
for (const call of f.calls) {
|
|
443
|
+
if (!call.value?.trim() || !call.unit?.trim()) fail('G21', at, `callout ${call.id} has no value or unit`);
|
|
444
|
+
if (!['alarm', 'warning', 'normal', 'selected', 'inactive'].includes(call.state)) fail('G21', at, `callout ${call.id} has no recognised semantic state`);
|
|
445
|
+
if (!call.source?.trim()) fail('G21', at, `callout ${call.id} has no value provenance`);
|
|
446
|
+
if (![call.anchor?.x, call.anchor?.y].every((v) => Number.isFinite(v) && v >= 0 && v <= 1)) fail('G21', at, `callout ${call.id} has no normalized subject anchor`);
|
|
447
|
+
else if (call.box && f.stage) {
|
|
448
|
+
const x = f.stage.left + call.anchor.x * f.stage.width, y = f.stage.top + call.anchor.y * f.stage.height;
|
|
449
|
+
const clearance = 8;
|
|
450
|
+
if (x >= call.box.left - clearance && x <= call.box.right + clearance && y >= call.box.top - clearance && y <= call.box.bottom + clearance)
|
|
451
|
+
fail('G21', at, `callout ${call.id} covers its own subject anchor`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
for (let i = 0; i < f.calls.length; i += 1) for (let j = i + 1; j < f.calls.length; j += 1) {
|
|
455
|
+
const a = f.calls[i].box, b = f.calls[j].box;
|
|
456
|
+
if (a && b && a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top) fail('G21', at, `callouts ${f.calls[i].id} and ${f.calls[j].id} overlap`);
|
|
457
|
+
}
|
|
458
|
+
if (f.calls.some(({ box }) => !box || box.left < f.stage.left || box.right > f.stage.right || box.top < f.stage.top || box.bottom > f.stage.bottom)) fail('G21', at, 'one or more callouts escape the stage instead of following its subject');
|
|
459
|
+
if (f.calls.length && await page.locator('[data-story] [data-caos-callout-leader]').count() < f.calls.length) fail('G21', at, 'not every scene callout has a leader to its subject anchor');
|
|
460
|
+
if (f.scroll.width > f.viewport.width + 1) fail('G21', at, `story page overflows horizontally (${f.scroll.width}px in ${f.viewport.width}px)`);
|
|
461
|
+
visited.push({ route, step: f.step, hrefs: f.links.map((x) => x.href) });
|
|
462
|
+
drain(sink, at);
|
|
463
|
+
}
|
|
464
|
+
const first = visited.find((x) => x.hrefs.length >= 3);
|
|
465
|
+
if (first) {
|
|
466
|
+
const hrefs = [...new Set(first.hrefs.map((href) => routeOf(new URL(href, base).href)).filter(Boolean))];
|
|
467
|
+
if (hrefs.length < 3 || hrefs.some((href) => !stories.includes(href))) fail('G21', { route: first.route, mode }, `stepper links do not directly address at least three declared story steps (${hrefs.join(', ') || 'none'})`);
|
|
468
|
+
await page.goto(urlFor(first.route), { waitUntil: 'load', timeout: 30000 }).catch(() => undefined);
|
|
469
|
+
await settle(page);
|
|
470
|
+
const perf = await page.evaluate(() => window.__caosGate.storyPerformanceFacts());
|
|
471
|
+
if (perf.lcp === null || perf.lcp > 2500) fail('G21', { route: first.route, mode }, `story LCP is ${perf.lcp === null ? 'unavailable' : `${perf.lcp.toFixed(0)} ms`}; ADR-0082 ceiling is 2500 ms`);
|
|
472
|
+
const assetBytes = perf.assets3d.reduce((sum, asset) => sum + asset.bytes, 0);
|
|
473
|
+
if (perf.assets3d.some((asset) => !asset.bytes)) fail('G21', { route: first.route, mode }, 'a 3D asset has no measurable compressed transfer size');
|
|
474
|
+
if (assetBytes > 15 * 1024 * 1024) fail('G21', { route: first.route, mode }, `compressed 3D assets total ${(assetBytes / 1024 / 1024).toFixed(2)} MiB; ADR-0082 ceiling is 15 MiB`);
|
|
475
|
+
const main = page.locator('[data-story]').first();
|
|
476
|
+
await main.focus().catch(() => undefined);
|
|
477
|
+
await page.keyboard.press('End').catch(() => undefined);
|
|
478
|
+
await page.waitForFunction((step) => window.__caosGate.showcaseFacts().step !== step, first.step, { timeout: 3000 }).catch(() => undefined);
|
|
479
|
+
const endFacts = await page.evaluate(() => window.__caosGate.showcaseFacts());
|
|
480
|
+
if (endFacts.step === first.step) fail('G21', { route: first.route, mode }, 'End key does not move the story to its final step');
|
|
481
|
+
await page.keyboard.press('Home').catch(() => undefined);
|
|
482
|
+
await page.waitForFunction((step) => window.__caosGate.showcaseFacts().step === step, first.step, { timeout: 3000 }).catch(() => undefined);
|
|
483
|
+
const homeFacts = await page.evaluate(() => window.__caosGate.showcaseFacts());
|
|
484
|
+
if (homeFacts.step !== first.step) fail('G21', { route: first.route, mode }, 'Home key does not return the story to its first step');
|
|
485
|
+
const sceneItem = page.locator('[data-story] [data-linked-view="scene"][data-linked-id]').first();
|
|
486
|
+
const panelItem = (id) => page.locator(`[data-story] [data-linked-view="panel"][data-linked-id="${id}"]`).first();
|
|
487
|
+
if (!(await sceneItem.count())) fail('G21', { route: first.route, mode }, 'story has no selectable linked scene objects');
|
|
488
|
+
else {
|
|
489
|
+
const id = await sceneItem.getAttribute('data-linked-id');
|
|
490
|
+
const panelMatch = panelItem(id);
|
|
491
|
+
if (!(await panelMatch.count())) fail('G21', { route: first.route, mode }, `scene object ${id} has no linked panel item`);
|
|
492
|
+
else {
|
|
493
|
+
await sceneItem.evaluate((el) => el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })));
|
|
494
|
+
await page.waitForFunction((target) => document.querySelector(`[data-linked-view="panel"][data-linked-id="${CSS.escape(target)}"][data-linked-selected="true"]`), id, { timeout: 2000 }).catch(() => undefined);
|
|
495
|
+
if (await panelMatch.getAttribute('data-linked-selected') !== 'true') fail('G21', { route: first.route, mode }, `selecting scene object ${id} does not select the matching panel item`);
|
|
496
|
+
await panelMatch.evaluate((el) => el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })));
|
|
497
|
+
await page.waitForFunction((target) => document.querySelector(`[data-linked-view="scene"][data-linked-id="${CSS.escape(target)}"][data-linked-selected="true"]`), id, { timeout: 2000 }).catch(() => undefined);
|
|
498
|
+
if (await sceneItem.getAttribute('data-linked-selected') !== 'true') fail('G21', { route: first.route, mode }, `selecting panel item ${id} does not highlight the matching scene object`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const cursorInput = page.locator('[data-story] [data-linked-cursor]').first();
|
|
502
|
+
if (!(await cursorInput.count())) fail('G21', { route: first.route, mode }, 'story has no shared scrub control for linked scene and plots');
|
|
503
|
+
else {
|
|
504
|
+
await cursorInput.evaluate((el) => { const input = el; const min = Number(input.min || 0), max = Number(input.max || 100); const next = String(Math.round(min + (max - min) * .63)); Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set.call(input, next); input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); });
|
|
505
|
+
await page.waitForTimeout(80);
|
|
506
|
+
const cursorFacts = await page.evaluate(() => window.__caosGate.showcaseFacts());
|
|
507
|
+
const value = cursorFacts.cursorInputs[0]?.value;
|
|
508
|
+
if (!cursorFacts.cursors.length || cursorFacts.cursors.some((position) => position !== value)) fail('G21', { route: first.route, mode }, 'scrubbing does not move every linked-view marker to the same cursor');
|
|
509
|
+
}
|
|
510
|
+
const play = page.locator('[data-story] button[aria-pressed]').first();
|
|
511
|
+
if (!(await play.count())) fail('G21', { route: first.route, mode }, 'story has no play/pause control with an observable state');
|
|
512
|
+
else {
|
|
513
|
+
await play.click();
|
|
514
|
+
if (await play.getAttribute('aria-pressed') !== 'true') fail('G21', { route: first.route, mode }, 'play control does not enter playing state');
|
|
515
|
+
const link = page.locator('[data-story-link]').nth(1);
|
|
516
|
+
await link.click();
|
|
517
|
+
if (await page.locator('[data-story] button[aria-pressed]').first().getAttribute('aria-pressed') !== 'false') fail('G21', { route: first.route, mode }, 'story autoplay does not pause on reader interaction');
|
|
518
|
+
}
|
|
519
|
+
// Measure a real step change against ADR-0082: eased stage motion, interaction latency and frames while it moves.
|
|
520
|
+
await page.evaluate(() => { window.__caosGate.resetStoryInteractions(); window.__caosGate.storyFrameSample = window.__caosGate.sampleStoryFrames(1600); });
|
|
521
|
+
const next = page.locator('[data-story] .caos-story-controls button').nth(2);
|
|
522
|
+
if (await next.isEnabled()) {
|
|
523
|
+
await next.click();
|
|
524
|
+
await page.waitForTimeout(90);
|
|
525
|
+
const earlyMotion = await page.evaluate(() => window.__caosGate.showcaseFacts().motion);
|
|
526
|
+
await page.waitForTimeout(260);
|
|
527
|
+
const lateMotion = await page.evaluate(() => window.__caosGate.showcaseFacts().motion);
|
|
528
|
+
if (!earlyMotion || earlyMotion.animationName === 'none' || earlyMotion.animationName === 'auto' || earlyMotion.transform === 'none' || earlyMotion.transform === 'matrix(1, 0, 0, 1, 0, 0)') fail('G21', { route: first.route, mode }, 'story step has no visible eased stage transition');
|
|
529
|
+
else if (Number.parseFloat(earlyMotion.animationDuration) * (earlyMotion.animationDuration.endsWith('ms') ? 1 : 1000) < 200) fail('G21', { route: first.route, mode }, `normal stage transition is shorter than 200 ms (${earlyMotion.animationDuration})`);
|
|
530
|
+
if (!lateMotion || lateMotion.transform !== 'matrix(1, 0, 0, 1, 0, 0)') fail('G21', { route: first.route, mode }, 'the staged transition does not settle at its final state');
|
|
531
|
+
}
|
|
532
|
+
const frameFacts = await page.evaluate(() => window.__caosGate.storyFrameSample);
|
|
533
|
+
if (!frameFacts || frameFacts.median === null || frameFacts.p05 === null) fail('G21', { route: first.route, mode }, 'story animation frame rate could not be measured');
|
|
534
|
+
else {
|
|
535
|
+
if (frameFacts.median < 55) fail('G21', { route: first.route, mode }, `story median is ${frameFacts.median.toFixed(1)} fps; ADR-0082 floor is 55 fps`);
|
|
536
|
+
if (frameFacts.p05 < 30) fail('G21', { route: first.route, mode }, `story fifth-percentile frame rate is ${frameFacts.p05.toFixed(1)} fps; ADR-0082 floor is 30 fps`);
|
|
537
|
+
}
|
|
538
|
+
const interactions = await page.evaluate(() => window.__caosGate.storyPerformanceFacts().events);
|
|
539
|
+
if (!interactions.length) fail('G21', { route: first.route, mode }, 'browser interaction latency was not measurable');
|
|
540
|
+
else if (Math.max(...interactions.map((entry) => entry.duration)) > 200) fail('G21', { route: first.route, mode }, `story interaction latency is ${Math.max(...interactions.map((entry) => entry.duration)).toFixed(0)} ms; ADR-0082 ceiling is 200 ms`);
|
|
541
|
+
await page.emulateMedia({ reducedMotion: 'reduce' });
|
|
542
|
+
const anyOtherLink = page.locator('[data-story-link]').nth(1);
|
|
543
|
+
if (await anyOtherLink.count()) {
|
|
544
|
+
await anyOtherLink.click();
|
|
545
|
+
const reducedMotion = await page.evaluate(() => window.__caosGate.showcaseFacts().motion);
|
|
546
|
+
const durationMs = reducedMotion ? Number.parseFloat(reducedMotion.animationDuration) * (reducedMotion.animationDuration.endsWith('ms') ? 1 : 1000) : Infinity;
|
|
547
|
+
if (reducedMotion?.animationName !== 'none' && durationMs > 150) fail('G21', { route: first.route, mode: 'reduced motion' }, `stage motion lasts ${durationMs} ms with reduced motion requested; ceiling is 150 ms`);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
// The mobile story uses one column: the docked panel follows the stage and the three critical callouts remain visible.
|
|
551
|
+
const mobile = await openContext([390, 844], themes[0], langs[0]);
|
|
552
|
+
const { page: phone, sink: phoneSink } = await newPage(mobile);
|
|
553
|
+
await phone.goto(urlFor(stories[0]), { waitUntil: 'load', timeout: 30000 }).catch(() => undefined);
|
|
554
|
+
await settle(phone);
|
|
555
|
+
const mobileFacts = await phone.evaluate(() => window.__caosGate.showcaseFacts());
|
|
556
|
+
if (mobileFacts.calls.length !== 3) fail('G21', { route: stories[0], mode: '390x844 mobile story' }, `mobile story shows ${mobileFacts.calls.length} callouts; ADR-0082 requires the three primary callouts`);
|
|
557
|
+
if (!mobileFacts.stage || !mobileFacts.panel || mobileFacts.panel.top < mobileFacts.stage.bottom - 1) fail('G21', { route: stories[0], mode: '390x844 mobile story' }, 'the docked panel does not sit below the scene on mobile');
|
|
558
|
+
if (mobileFacts.scroll.width > mobileFacts.viewport.width + 1) fail('G21', { route: stories[0], mode: '390x844 mobile story' }, `mobile story overflows horizontally (${mobileFacts.scroll.width}px in ${mobileFacts.viewport.width}px)`);
|
|
559
|
+
drain(phoneSink, { route: stories[0], mode: '390x844 mobile story' });
|
|
560
|
+
await mobile.close();
|
|
561
|
+
await ctx.close();
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
411
565
|
// ---- G10: the wide-font pass. Every route, tab and sampled case again, in Spanish (the longer labels) when it is
|
|
412
566
|
// measured, rendered in the wide fallback font from the first frame: what a reader with wider fonts sees.
|
|
413
567
|
if (opt.wideFont !== false && opt.wideFontSizes) {
|
|
@@ -938,7 +1092,7 @@ export async function runGate(o) {
|
|
|
938
1092
|
const big = s.w >= opt.viewportMinWidth;
|
|
939
1093
|
if (big && f.rail && f.rail.scroll > f.rail.client + 1) fail('G18', at, `${label}: the rail scrolls (${f.rail.scroll}px of content in ${f.rail.client}px)`);
|
|
940
1094
|
if (f.tabRows.some((r) => r > 1)) fail('G18', at, `${label}: a tab row wraps onto ${Math.max(...f.tabRows)} rows`);
|
|
941
|
-
if (big && f.share <
|
|
1095
|
+
if (big && f.share < 0.8) fail('G18', at, `${label}: the stage covers ${(100 * f.share).toFixed(1)}% of the viewport (floor 80% on Focus and story routes, ADR-0082)`);
|
|
942
1096
|
if (f.exitInside === false) fail('G18', at, `${label}: the exit control is outside the viewport`);
|
|
943
1097
|
for (const c of f.covered) fail('G18', at, `${label}: ${c.chrome} covers the control "${c.control}" (the stage must clear the Focus chrome)`);
|
|
944
1098
|
if (f.cutSelects.count) fail('G5', at, `${label}: ${f.cutSelects.count} select(s) cut the selected text (a case name cut in its box, or a display that names another case than the one the select holds, or a plain select with a text wider than its room): ${f.cutSelects.list.join('; ')}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fasl-work/caos-app-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Shared web-app shell, workbench primitives, content primitives, design system and measured gate for the CAOS / Faena public apps (ADR-0016, ADR-0017, ADR-0071).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Felipe Santibáñez-Leal",
|
package/reserved-classes.json
CHANGED
|
@@ -30,6 +30,11 @@
|
|
|
30
30
|
"caos-bars-legend",
|
|
31
31
|
"caos-bars-readout",
|
|
32
32
|
"caos-bars-swatch",
|
|
33
|
+
"caos-callout-eyebrow",
|
|
34
|
+
"caos-callout-layer",
|
|
35
|
+
"caos-callout-leaders",
|
|
36
|
+
"caos-callout-source",
|
|
37
|
+
"caos-callout-trend",
|
|
33
38
|
"caos-card",
|
|
34
39
|
"caos-chart",
|
|
35
40
|
"caos-chart-axis-title",
|
|
@@ -158,12 +163,34 @@
|
|
|
158
163
|
"caos-readout-title",
|
|
159
164
|
"caos-readout-unit",
|
|
160
165
|
"caos-readout-value",
|
|
166
|
+
"caos-scene-callout",
|
|
167
|
+
"caos-scene3d-controls",
|
|
168
|
+
"caos-scene3d-frame",
|
|
169
|
+
"caos-scene3d-legend",
|
|
170
|
+
"caos-scene3d-renderer",
|
|
171
|
+
"caos-scene3d-status",
|
|
172
|
+
"caos-scene3d-viewport",
|
|
161
173
|
"caos-stage",
|
|
162
174
|
"caos-stale",
|
|
163
175
|
"caos-stale-note",
|
|
164
176
|
"caos-stale-overlay",
|
|
165
177
|
"caos-stat-source",
|
|
166
178
|
"caos-stat-value",
|
|
179
|
+
"caos-story",
|
|
180
|
+
"caos-story-actions",
|
|
181
|
+
"caos-story-controls",
|
|
182
|
+
"caos-story-header",
|
|
183
|
+
"caos-story-heading",
|
|
184
|
+
"caos-story-headline",
|
|
185
|
+
"caos-story-layout",
|
|
186
|
+
"caos-story-panel",
|
|
187
|
+
"caos-story-stage",
|
|
188
|
+
"caos-story-step",
|
|
189
|
+
"caos-story-step-headline",
|
|
190
|
+
"caos-story-step-number",
|
|
191
|
+
"caos-story-step-verb",
|
|
192
|
+
"caos-story-stepper",
|
|
193
|
+
"caos-story-verb",
|
|
167
194
|
"caos-surface",
|
|
168
195
|
"caos-surface-actions",
|
|
169
196
|
"caos-surface-body",
|
|
@@ -318,12 +345,15 @@
|
|
|
318
345
|
"fill",
|
|
319
346
|
"fixed",
|
|
320
347
|
"good",
|
|
348
|
+
"is-active",
|
|
321
349
|
"live",
|
|
322
350
|
"lucide",
|
|
323
351
|
"on",
|
|
324
352
|
"precomputed",
|
|
325
353
|
"primary",
|
|
326
354
|
"rev",
|
|
355
|
+
"trend-down",
|
|
356
|
+
"trend-up",
|
|
327
357
|
"warn",
|
|
328
358
|
"wide"
|
|
329
359
|
],
|
|
@@ -356,6 +386,11 @@
|
|
|
356
386
|
"caos-bars-legend",
|
|
357
387
|
"caos-bars-readout",
|
|
358
388
|
"caos-bars-swatch",
|
|
389
|
+
"caos-callout-eyebrow",
|
|
390
|
+
"caos-callout-layer",
|
|
391
|
+
"caos-callout-leaders",
|
|
392
|
+
"caos-callout-source",
|
|
393
|
+
"caos-callout-trend",
|
|
359
394
|
"caos-card",
|
|
360
395
|
"caos-chart",
|
|
361
396
|
"caos-chart-axis-title",
|
|
@@ -486,12 +521,34 @@
|
|
|
486
521
|
"caos-readout-title",
|
|
487
522
|
"caos-readout-unit",
|
|
488
523
|
"caos-readout-value",
|
|
524
|
+
"caos-scene-callout",
|
|
525
|
+
"caos-scene3d-controls",
|
|
526
|
+
"caos-scene3d-frame",
|
|
527
|
+
"caos-scene3d-legend",
|
|
528
|
+
"caos-scene3d-renderer",
|
|
529
|
+
"caos-scene3d-status",
|
|
530
|
+
"caos-scene3d-viewport",
|
|
489
531
|
"caos-stage",
|
|
490
532
|
"caos-stale",
|
|
491
533
|
"caos-stale-note",
|
|
492
534
|
"caos-stale-overlay",
|
|
493
535
|
"caos-stat-source",
|
|
494
536
|
"caos-stat-value",
|
|
537
|
+
"caos-story",
|
|
538
|
+
"caos-story-actions",
|
|
539
|
+
"caos-story-controls",
|
|
540
|
+
"caos-story-header",
|
|
541
|
+
"caos-story-heading",
|
|
542
|
+
"caos-story-headline",
|
|
543
|
+
"caos-story-layout",
|
|
544
|
+
"caos-story-panel",
|
|
545
|
+
"caos-story-stage",
|
|
546
|
+
"caos-story-step",
|
|
547
|
+
"caos-story-step-headline",
|
|
548
|
+
"caos-story-step-number",
|
|
549
|
+
"caos-story-step-verb",
|
|
550
|
+
"caos-story-stepper",
|
|
551
|
+
"caos-story-verb",
|
|
495
552
|
"caos-surface",
|
|
496
553
|
"caos-surface-actions",
|
|
497
554
|
"caos-surface-body",
|
|
@@ -595,6 +652,7 @@
|
|
|
595
652
|
"header-sep",
|
|
596
653
|
"hint",
|
|
597
654
|
"icon-btn",
|
|
655
|
+
"is-active",
|
|
598
656
|
"katex",
|
|
599
657
|
"l-en",
|
|
600
658
|
"l-es",
|
|
@@ -644,6 +702,8 @@
|
|
|
644
702
|
"tone-good",
|
|
645
703
|
"tone-neutral",
|
|
646
704
|
"tone-warn",
|
|
705
|
+
"trend-down",
|
|
706
|
+
"trend-up",
|
|
647
707
|
"two-col",
|
|
648
708
|
"uplot",
|
|
649
709
|
"warn",
|
package/styles.css
CHANGED
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
--color-good: #3fb950;
|
|
40
40
|
--color-warn: #d29922;
|
|
41
41
|
--color-bad: #f85149;
|
|
42
|
+
--color-step-1: #56d4e5;
|
|
43
|
+
--color-step-2: #79c0ff;
|
|
44
|
+
--color-step-3: #d2a8ff;
|
|
45
|
+
--color-step-4: #7ee787;
|
|
46
|
+
--color-step-5: #ffca80;
|
|
42
47
|
--color-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 16px rgba(0, 0, 0, 0.45);
|
|
43
48
|
color-scheme: dark;
|
|
44
49
|
}
|
|
@@ -60,6 +65,11 @@
|
|
|
60
65
|
--color-good: #197c36;
|
|
61
66
|
--color-warn: #946300;
|
|
62
67
|
--color-bad: #cf222e;
|
|
68
|
+
--color-step-1: #0550ae;
|
|
69
|
+
--color-step-2: #0969da;
|
|
70
|
+
--color-step-3: #6639ba;
|
|
71
|
+
--color-step-4: #1a7f37;
|
|
72
|
+
--color-step-5: #9a6700;
|
|
63
73
|
--color-shadow: 0 1px 2px rgba(31, 35, 40, 0.06), 0 4px 10px rgba(31, 35, 40, 0.04);
|
|
64
74
|
}
|
|
65
75
|
|
|
@@ -235,9 +245,11 @@ code, kbd { font-family: var(--font-mono); }
|
|
|
235
245
|
/* A WORKBENCH or a SURFACE is not prose: capping an instrument at the reading width wastes every pixel beyond it. */
|
|
236
246
|
.page-body.wide { max-width: var(--maxw-wide); padding-inline: 1.25rem; }
|
|
237
247
|
.app-shell.fixed .page-body.wide { flex: 1 1 auto; min-height: 0; padding-top: 0.75rem; padding-bottom: 0.75rem; }
|
|
248
|
+
.app-shell.fixed .page-body.wide.caos-wb { padding-top: 0.15rem; padding-bottom: 0.15rem; }
|
|
249
|
+
.app-shell.fixed.contain:has(.caos-wb) > .site-header .header-inner { height: 48px; }
|
|
238
250
|
|
|
239
251
|
/* Workbench (WorkbenchLayout): a sized rail beside the instrument, on the full viewport. */
|
|
240
|
-
.caos-wb { display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); gap: 0.
|
|
252
|
+
.caos-wb { --rail-w: clamp(232px, 17vw, 320px); display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); gap: 0.6rem; align-items: stretch; min-width: 0; padding-top: 0.4rem; padding-bottom: 0.4rem; }
|
|
241
253
|
.page-body.wide.caos-wb { padding-inline: var(--space-4); }
|
|
242
254
|
.caos-wb > * { min-width: 0; }
|
|
243
255
|
.app-shell.fixed.contain > .page > .caos-wb { flex: 1 1 auto; min-height: 0; }
|
|
@@ -619,7 +631,7 @@ svg.lucide { flex: none; }
|
|
|
619
631
|
.caos-verdict.compact .caos-verdict-text { margin: 0; }
|
|
620
632
|
|
|
621
633
|
/* PlotCard: the frame every chart or table sits in. */
|
|
622
|
-
.caos-plot { margin: 0; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); padding: 0.
|
|
634
|
+
.caos-plot { margin: 0; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); padding: 0.25rem 0.6rem 0.2rem; min-width: 0; }
|
|
623
635
|
.caos-plot-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--space-2); margin-bottom: 0.2rem; font-style: normal; text-align: left; min-height: 1.5rem; min-width: 0; }
|
|
624
636
|
.caos-plot-head .badge { font-size: 0.68rem; padding: 0.05rem 0.45rem; }
|
|
625
637
|
.caos-plot-title { min-width: 0; overflow-wrap: anywhere; font-size: var(--text-sm); color: var(--color-fg-subtle); font-weight: 600; }
|
|
@@ -629,6 +641,7 @@ svg.lucide { flex: none; }
|
|
|
629
641
|
.caos-plot-note { margin: 0.4rem 0 0; font-size: 0.78rem; color: var(--color-fg-faint); }
|
|
630
642
|
.caos-plot.fill { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
|
|
631
643
|
.caos-plot.fill > .caos-plot-body { flex: 1 1 0; min-height: 0; overflow: hidden; }
|
|
644
|
+
.caos-plot.fill > .caos-plot-body > .caos-table { height: 100%; }
|
|
632
645
|
/* The note of a filling card keeps at most 35% of the card and scrolls beyond it, so the drawing keeps a floor
|
|
633
646
|
(CAOS_APP_SHELL#95: a long note left plots of 10 to 90 px at 1280 x 800). */
|
|
634
647
|
.caos-plot.fill > .caos-plot-note { flex: 0 1 auto; max-height: 35%; overflow-y: auto; }
|
|
@@ -641,14 +654,14 @@ svg.lucide { flex: none; }
|
|
|
641
654
|
[data-caos-note-fit] .caos-plot.fill > .caos-plot-note { max-height: none; min-height: 0; }
|
|
642
655
|
}
|
|
643
656
|
|
|
644
|
-
/* The stage floor of a filling card (shell 0.11.2; the gate's G6 floor, `--stage-floor`
|
|
657
|
+
/* The stage floor of a filling card (shell 0.11.2; the gate's G6 floor, `--stage-floor` 146 px). At 1280 px and wider the
|
|
645
658
|
body of a filling card, the box the gate measures, keeps at least the floor itself, so the card grows when its header
|
|
646
659
|
or its note wraps (a narrow column, the Spanish title, two badges, the wide font) and the containing panel scrolls
|
|
647
660
|
when the cards do not fit (ADR-0071; the rail never does). 0.11.1 derived a card height from a fixed chrome instead,
|
|
648
661
|
and a header that wrapped to a third line left the stage under the floor (CAOS_Contraste 0.07.000: 138 px; a
|
|
649
662
|
400 px column with a note: 106 px). Below 1280 px the document scrolls and nothing changes. */
|
|
650
663
|
@media (min-width: 1280px) {
|
|
651
|
-
.caos-plot.fill > .caos-plot-body { min-height: var(--caos-stage-floor,
|
|
664
|
+
.caos-plot.fill > .caos-plot-body { min-height: var(--caos-stage-floor, 146px); }
|
|
652
665
|
}
|
|
653
666
|
|
|
654
667
|
/* A row of views side by side. Every view of the row takes an equal share, a filling card included (known shell
|
|
@@ -713,6 +726,12 @@ svg.lucide { flex: none; }
|
|
|
713
726
|
.caos-table th.caos-col-text, .caos-table td.caos-col-text { text-align: left; white-space: normal; min-width: 16ch; }
|
|
714
727
|
.caos-table thead th { position: sticky; top: 0; background: var(--color-surface); color: var(--color-fg-subtle); font-weight: 600; border-bottom-color: var(--color-fg-faint); }
|
|
715
728
|
.caos-table tbody tr:hover { background: var(--color-surface-2); }
|
|
729
|
+
/* A full-height table is a plotted view too: use taller rows on a very tall desktop so its data occupies the
|
|
730
|
+
instrument instead of leaving the lower half of a 1440 px stage visually empty (ADR-0082 G6). */
|
|
731
|
+
@media (min-width: 2400px) and (min-height: 1200px) {
|
|
732
|
+
.caos-plot.fill > .caos-plot-body > .caos-table th,
|
|
733
|
+
.caos-plot.fill > .caos-plot-body > .caos-table td { padding-block: 0.58rem; }
|
|
734
|
+
}
|
|
716
735
|
|
|
717
736
|
@media (max-width: 900px) {
|
|
718
737
|
/* The views of a row stack; a filling view gets a fixed readable height instead of the panel's. */
|
|
@@ -931,6 +950,83 @@ svg.lucide { flex: none; }
|
|
|
931
950
|
.tabs-has-slot > .tabs-slot { grid-column: 2; grid-row: 1; align-self: stretch; display: flex; align-items: center; padding-bottom: 0.4rem; margin-bottom: 0.9rem; border-bottom: 1px solid var(--color-border); }
|
|
932
951
|
.tabs-has-slot > .tabs-slot > .caos-focus-enter { align-self: center; padding-block: 0.2rem; }
|
|
933
952
|
.tabs-has-slot > .tabpanel { grid-column: 1 / -1; grid-row: 2; }
|
|
953
|
+
|
|
954
|
+
/* ADR-0082 showcase frame: one focused stage, one docked panel, readable step navigation and anchored scene notes. */
|
|
955
|
+
.caos-story { display: flex; flex-direction: column; gap: 0; min-width: 0; min-height: 0; height: 100%; padding: 0; color: var(--color-fg); background: var(--color-bg); }
|
|
956
|
+
body:has([data-story]) .app-shell > .site-header,
|
|
957
|
+
body:has([data-story]) .app-shell > .site-footer { display: none !important; }
|
|
958
|
+
body:has([data-story]) .app-shell > .page { display: flex; flex: 1 1 auto; min-height: 0; overflow: hidden; padding: 0; }
|
|
959
|
+
body:has([data-story]) .app-shell > .page > .caos-story { width: 100%; height: 100%; flex: 1 1 auto; }
|
|
960
|
+
.caos-story-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); min-width: 0; }
|
|
961
|
+
.caos-story-heading { min-width: 0; }
|
|
962
|
+
.caos-story-verb { display: block; color: var(--color-step-1); font: 700 var(--text-xs) var(--font-mono); letter-spacing: .09em; text-transform: uppercase; }
|
|
963
|
+
.caos-story-headline { margin: .1rem 0; font-size: clamp(1rem, 1.6vw, 1.45rem); line-height: 1.2; }
|
|
964
|
+
.caos-story-heading > p { margin: .15rem 0 0; color: var(--color-fg-subtle); font-size: var(--text-sm); }
|
|
965
|
+
.caos-story[data-step-accent="1"] .caos-story-verb { color: var(--color-step-1); }
|
|
966
|
+
.caos-story[data-step-accent="2"] .caos-story-verb { color: var(--color-step-2); }
|
|
967
|
+
.caos-story[data-step-accent="3"] .caos-story-verb { color: var(--color-step-3); }
|
|
968
|
+
.caos-story[data-step-accent="4"] .caos-story-verb { color: var(--color-step-4); }
|
|
969
|
+
.caos-story[data-step-accent="5"] .caos-story-verb { color: var(--color-step-5); }
|
|
970
|
+
.caos-story-actions,.caos-story-controls { display: flex; align-items: center; gap: var(--space-2); }
|
|
971
|
+
.caos-story button { min-height: 36px; padding: .35rem .65rem; color: var(--color-fg); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); cursor: pointer; }
|
|
972
|
+
.caos-story button:disabled { opacity: .55; cursor: not-allowed; }
|
|
973
|
+
.caos-story-stepper { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: var(--space-2); min-width: 0; }
|
|
974
|
+
.caos-story-step { display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-rows: auto auto; align-items: center; column-gap: .55rem; min-width: 0; min-height: 40px; padding: .2rem .55rem; color: var(--color-fg-subtle); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); text-decoration: none; }
|
|
975
|
+
.caos-story-step:hover { color: var(--color-fg); text-decoration: none; }
|
|
976
|
+
.caos-story-step.is-active { color: var(--color-fg); border-color: var(--color-accent); box-shadow: inset 3px 0 var(--color-accent); }
|
|
977
|
+
.caos-story-step-number { grid-row: 1 / span 2; display: grid; place-items: center; width: 1.55rem; height: 1.55rem; border: 1px solid currentColor; border-radius: 50%; font: 700 var(--text-xs) var(--font-mono); }
|
|
978
|
+
.caos-story-step-verb { overflow: hidden; color: var(--color-fg); font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
|
|
979
|
+
.caos-story-step-headline { overflow: hidden; color: var(--color-fg-subtle); font-size: var(--text-xs); text-overflow: ellipsis; white-space: nowrap; }
|
|
980
|
+
.caos-story-controls { justify-content: flex-end; }
|
|
981
|
+
.caos-story-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(260px, 29%); gap: var(--space-3); flex: 1 1 auto; min-width: 0; min-height: 0; }
|
|
982
|
+
.caos-story-stage { position: relative; isolation: isolate; min-width: 0; min-height: 360px; overflow: hidden; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); }
|
|
983
|
+
.caos-story-stage > :not(.caos-callout-layer) { width: 100%; height: 100%; }
|
|
984
|
+
.caos-story-panel { min-width: 0; min-height: 0; overflow: auto; padding: var(--space-3); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); }
|
|
985
|
+
/* A route change first moves the instrument into view, then the docked evidence panel; the sequence is short and tied to the finding. */
|
|
986
|
+
[data-story-transition] > .caos-story-stage { animation: caos-story-stage-enter 320ms cubic-bezier(.2,.72,.26,1) both; }
|
|
987
|
+
[data-story-transition] > .caos-story-panel { animation: caos-story-panel-enter 240ms cubic-bezier(.2,.72,.26,1) 100ms both; }
|
|
988
|
+
@keyframes caos-story-stage-enter { from { transform: translateY(7px) scale(.994); } to { transform: translateY(0) scale(1); } }
|
|
989
|
+
@keyframes caos-story-panel-enter { from { transform: translateY(6px); } to { transform: translateY(0); } }
|
|
990
|
+
.caos-callout-layer { position: absolute; z-index: 4; inset: 0; overflow: hidden; pointer-events: none; }
|
|
991
|
+
.caos-scene3d-frame { display: grid; grid-template-rows: auto minmax(0, 1fr); min-width: 0; min-height: 0; width: 100%; height: 100%; overflow: hidden; background: var(--color-bg); color: var(--color-fg); }
|
|
992
|
+
.caos-scene3d-controls { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); min-width: 0; padding: var(--space-2) var(--space-3); border-block-end: 1px solid var(--color-border); background: var(--color-surface); }
|
|
993
|
+
.caos-scene3d-viewport { position: relative; min-width: 0; min-height: 0; overflow: hidden; isolation: isolate; }
|
|
994
|
+
.caos-scene3d-renderer, .caos-scene3d-renderer > canvas, .caos-scene3d-renderer > svg { display: block; width: 100%; height: 100%; min-width: 0; min-height: 0; }
|
|
995
|
+
.caos-scene3d-status { position: absolute; inset-inline-start: var(--space-3); inset-block-end: var(--space-3); z-index: 2; max-width: min(32rem, calc(100% - 2 * var(--space-3))); padding: var(--space-2) var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-fg); }
|
|
996
|
+
.caos-scene3d-legend { position: absolute; inset-inline-end: var(--space-3); inset-block-end: var(--space-3); z-index: 2; max-width: min(24rem, calc(100% - 2 * var(--space-3))); padding: var(--space-2) var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-fg); }
|
|
997
|
+
.caos-callout-leaders { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
|
|
998
|
+
.caos-callout-leaders line { stroke: var(--color-fg-faint); stroke-width: 1.1; stroke-dasharray: 2 4; opacity: .52; }
|
|
999
|
+
.caos-scene-callout { position: absolute; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-content: center; gap: .05rem .4rem; padding: .35rem .5rem; color: var(--color-fg); background: color-mix(in srgb, var(--color-surface) 94%, transparent); border: 1px solid var(--color-border); border-left: 3px solid var(--color-accent); border-radius: var(--radius-md); box-shadow: var(--color-shadow); backdrop-filter: blur(8px); }
|
|
1000
|
+
.caos-scene-callout[data-callout-state="alarm"] { border-left-color: var(--color-bad); }
|
|
1001
|
+
.caos-scene-callout[data-callout-state="warning"] { border-left-color: var(--color-warn); }
|
|
1002
|
+
.caos-scene-callout[data-callout-state="normal"] { border-left-color: var(--color-good); }
|
|
1003
|
+
.caos-scene-callout[data-callout-state="inactive"] { border-left-color: var(--color-fg-faint); }
|
|
1004
|
+
.caos-callout-eyebrow { overflow: hidden; color: var(--color-fg); font: 700 .68rem var(--font-mono); letter-spacing: .05em; text-overflow: ellipsis; white-space: nowrap; }
|
|
1005
|
+
.caos-scene-callout > strong { overflow: hidden; font: 700 .92rem var(--font-mono); font-variant-numeric: tabular-nums; text-overflow: ellipsis; white-space: nowrap; }
|
|
1006
|
+
.caos-scene-callout > strong small,.caos-callout-source { color: var(--color-fg); font-size: .68rem; font-weight: 500; }
|
|
1007
|
+
.caos-callout-trend { grid-column: 2; grid-row: 1 / span 2; align-self: center; font-size: 1.1rem; font-weight: 800; }
|
|
1008
|
+
.caos-callout-trend.trend-up { color: var(--color-bad); }
|
|
1009
|
+
.caos-callout-trend.trend-down { color: var(--color-good); }
|
|
1010
|
+
.caos-callout-source { grid-column: 1 / -1; }
|
|
1011
|
+
@media (max-width: 760px) {
|
|
1012
|
+
body:has([data-story]) .app-shell > .page { overflow-y: auto; }
|
|
1013
|
+
.caos-story { height: auto; min-height: 100%; }
|
|
1014
|
+
.caos-story-header { display: grid; grid-template-columns: minmax(0, 1fr); align-items: start; gap: .45rem; }
|
|
1015
|
+
.caos-story-heading { width: 100%; }
|
|
1016
|
+
.caos-story-actions { width: 100%; flex-wrap: wrap; justify-content: space-between; gap: .35rem; }
|
|
1017
|
+
.caos-story-controls { flex: 1 1 auto; justify-content: flex-start; gap: .35rem; }
|
|
1018
|
+
.caos-story button { min-height: 34px; padding: .3rem .5rem; font-size: .78rem; white-space: nowrap; }
|
|
1019
|
+
.caos-story-stepper { grid-auto-columns: minmax(90px, 1fr); grid-auto-flow: column; grid-template-columns: unset; overflow-x: auto; }
|
|
1020
|
+
.caos-story-step-headline { display: none; }
|
|
1021
|
+
.caos-story-layout { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(46dvh, 1fr) auto; }
|
|
1022
|
+
.caos-story-panel { max-height: 30dvh; }
|
|
1023
|
+
.caos-story-stage { min-height: 300px; }
|
|
1024
|
+
.caos-scene-callout { padding: .25rem .35rem; }
|
|
1025
|
+
}
|
|
1026
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1027
|
+
.caos-story *, .caos-callout-layer * { scroll-behavior: auto !important; animation-duration: .01ms !important; transition-duration: .01ms !important; }
|
|
1028
|
+
[data-story-transition] > .caos-story-stage, [data-story-transition] > .caos-story-panel { animation: none !important; }
|
|
1029
|
+
}
|
|
934
1030
|
/* The same slot in the workbench's group tab row (shell 0.17.0, BL-074): the Focus control of the App ends the row, so the case select keeps
|
|
935
1031
|
the rail head's whole width. The tab row's own spacing (a tighter rule than the surface's) is repeated under the slot. */
|
|
936
1032
|
.caos-cw > .tabs.tabs-has-slot { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto minmax(0, 1fr); column-gap: 0.5rem; }
|