agentgui 1.0.1038 → 1.0.1040
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.
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'gui-ai-tell-sweep',
|
|
3
|
+
description: 'Hunt and fix AI-generated-design tells (gradients, glassmorphism, generic shadows, emoji icons, oversaturated color, boilerplate copy) in the agentgui GUI and its design kit',
|
|
4
|
+
whenToUse: '35th agentgui sweep, explicit ask: remove tell-tale AI design elements',
|
|
5
|
+
phases: [
|
|
6
|
+
{ title: 'Hunt', detail: 'one hunter per AI-tell lens against real source in both repos' },
|
|
7
|
+
{ title: 'Verify', detail: 'adversarial verifier per finding' },
|
|
8
|
+
{ title: 'Implement', detail: 'apply confirmed fixes directly to kit/app source' },
|
|
9
|
+
],
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const KIT = '/config/workspace/design'
|
|
13
|
+
const APP = '/config/workspace/agentgui'
|
|
14
|
+
|
|
15
|
+
const COMMON = `
|
|
16
|
+
You are auditing the agentgui GUI (a Claude-Code-web-class multi-agent chat product) and its design kit for AI-GENERATED-DESIGN TELLS specifically -- the visual/copy patterns that make a product instantly readable as "an LLM built this UI" rather than a deliberate, human-designed product.
|
|
17
|
+
Repos: app at ${APP} (client: site/app/js/app.js, site/app/js/backend.js, site/app/index.html; server: lib/http-handler.js, server.js), kit at ${KIT} (src/components/*.js, CSS at root or src/styles/, scripts/build.mjs; the kit is the published anentrypoint-design library and OWNS all visual CSS -- the app only wires state/callbacks).
|
|
18
|
+
Reference bar: claude.ai/code (calm, content-first, quiet chrome, generous thread width, restrained tonal palette, no decorative flourish).
|
|
19
|
+
HARD GUARDS:
|
|
20
|
+
- The middot separator, the ellipsis char, and em/en dashes in prose are KEPT product typography -- never flag them.
|
|
21
|
+
- Status is words + CSS-drawn .status-dot-disc -- never propose decorative glyphs, never propose re-adding icon glyphs where ASCII/words already replaced them.
|
|
22
|
+
- GUI fixes land in the KIT (CSS/components); app gets wiring only; index.html gets NO new !important.
|
|
23
|
+
- 34 prior sweeps already fixed: focus-ring tokenization, contrast (light+dark), typography-rhythm, copy-tone vocabulary unification, negative-space (clean 3 runs running), interaction-density, kit-dx docs, perceived-perf. Do not re-file those; this sweep is specifically AI-slop VISUAL/COPY TELLS.
|
|
24
|
+
Read the REAL source before claiming anything; every finding needs file:line evidence. Findings must be IMPLEMENTABLE this session (no rewrites, no new dependencies).
|
|
25
|
+
`
|
|
26
|
+
|
|
27
|
+
const FINDINGS_SCHEMA = {
|
|
28
|
+
type: 'object',
|
|
29
|
+
required: ['findings'],
|
|
30
|
+
properties: {
|
|
31
|
+
findings: {
|
|
32
|
+
type: 'array',
|
|
33
|
+
items: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
required: ['id', 'title', 'evidence', 'fixLocation', 'fix', 'severity'],
|
|
36
|
+
properties: {
|
|
37
|
+
id: { type: 'string', description: 'kebab-case slug' },
|
|
38
|
+
title: { type: 'string' },
|
|
39
|
+
evidence: { type: 'string', description: 'file:line + what the source shows' },
|
|
40
|
+
fixLocation: { type: 'string', enum: ['kit', 'app', 'server', 'kit+app'] },
|
|
41
|
+
fix: { type: 'string', description: 'concrete implementable change' },
|
|
42
|
+
severity: { type: 'string', enum: ['high', 'medium', 'low'] },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const VERDICT_SCHEMA = {
|
|
50
|
+
type: 'object',
|
|
51
|
+
required: ['isReal', 'reason'],
|
|
52
|
+
properties: {
|
|
53
|
+
isReal: { type: 'boolean' },
|
|
54
|
+
reason: { type: 'string' },
|
|
55
|
+
refinedFix: { type: 'string' },
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const LENSES = [
|
|
60
|
+
{
|
|
61
|
+
key: 'gradient-glow-glass',
|
|
62
|
+
prompt: `Lens: GRADIENTS / GLASSMORPHISM / GLOW. Grep the kit CSS (and any inline app styling) for linear-gradient/radial-gradient fills on buttons, cards, badges, avatars; backdrop-filter: blur (glassmorphism); box-shadow with saturated colored glows (not neutral elevation shadows); any purple-to-blue or pink-to-orange gradient combo (the textbook AI-generated-SaaS-landing-page tell). These read as templated AI output vs the flat, tonal, restrained claude.ai/code aesthetic. 3-8 findings.`,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: 'emoji-generic-icons',
|
|
66
|
+
prompt: `Lens: EMOJI-AS-ICON + GENERIC ICON TELLS. Grep app.js and kit component source for emoji characters used as icons/decoration (sparkle/rocket/checkmark/lightbulb/fire emoji etc -- distinct from the ALREADY-KEPT middot/ellipsis/dash typography), and for generic "AI product" iconography choices (sparkle/magic-wand icons implying "AI-powered", robot icons, gratuitous rounded-square icon badges). Also check for excessive/uniform border-radius applied to EVERYTHING (buttons, cards, inputs, avatars all sharing one large radius token in a way that reads as a template default rather than a considered scale). 3-8 findings.`,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: 'oversaturated-color',
|
|
70
|
+
prompt: `Lens: COLOR OVERUSE / OVERSATURATION. Audit the kit's color tokens (colors_and_type.css or equivalent) and component usage for oversaturated accent color applied too broadly (every button/link/badge/border colored, no restraint, no tonal hierarchy of neutral-vs-accent), excessive use of the brand accent as a BACKGROUND fill vs restrained use as a signal color, and any place multiple saturated hues compete in the same view (a classic tell of unreviewed AI-generated theming). Compare to claude.ai/code's mostly-neutral palette with color reserved for real signal. 3-8 findings.`,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
key: 'boilerplate-copy',
|
|
74
|
+
prompt: `Lens: BOILERPLATE AI-ASSISTANT COPY TONE. Sweep user-facing strings in ${APP}/site/app/js/app.js and kit component defaults for generic AI-product boilerplate phrasing: "Welcome!", "Get started!", exclamation-heavy onboarding copy, "Let's dive in", "Unleash the power of...", overly enthusiastic empty-state copy, generic placeholder text like "Type your message here..." that any AI-generated app ships by default instead of copy tailored to THIS product's actual terse, calm, professional voice (see AGENTS.md ASCII-only/no-decorative-glyph/terse-copy conventions already established). 3-8 findings with exact strings.`,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
key: 'layout-uniformity-tells',
|
|
78
|
+
prompt: `Lens: LAYOUT/COMPONENT UNIFORMITY TELLS. Audit for the "every card looks identical" AI-template tell: uniform card treatment applied indiscriminately regardless of content type/importance (same padding/shadow/radius on a stat card, a message bubble, a settings panel, with no visual hierarchy distinguishing primary content from secondary chrome), centered-everything layouts with excessive whitespace padding that reads as a generic template rather than a considered information-dense product layout, and any component that visually announces "I am a reusable card component" (heavy shadow + heavy radius + heavy padding, stacked identically everywhere) rather than fitting its specific context. 3-8 findings.`,
|
|
79
|
+
},
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
phase('Hunt')
|
|
83
|
+
const results = await pipeline(
|
|
84
|
+
LENSES,
|
|
85
|
+
l => agent(COMMON + '\n' + l.prompt + '\n\nReturn findings via the structured output schema; ids prefixed ' + l.key + '-.', { label: 'hunt:' + l.key, phase: 'Hunt', schema: FINDINGS_SCHEMA }),
|
|
86
|
+
(res, l) => {
|
|
87
|
+
if (!res || !res.findings || !res.findings.length) return []
|
|
88
|
+
return parallel(res.findings.map(f => () =>
|
|
89
|
+
agent(
|
|
90
|
+
COMMON + `\nYou are an ADVERSARIAL VERIFIER. A hunter claims:\nTITLE: ${f.title}\nEVIDENCE: ${f.evidence}\nPROPOSED FIX: ${f.fix}\nfixLocation: ${f.fixLocation}\n\nRead the cited source yourself. Refute it if: the evidence is wrong or stale, the behavior is intentional kept design (especially kept typography: middot/ellipsis/dash), it duplicates an already-fixed finding from a prior sweep, the fix is not implementable this session, or the severity is inflated. Default isReal=false when uncertain. If real, refine the fix to the most concrete minimal change.`,
|
|
91
|
+
{ label: 'verify:' + f.id, phase: 'Verify', schema: VERDICT_SCHEMA }
|
|
92
|
+
).then(v => ({ ...f, verdict: v }))
|
|
93
|
+
))
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
const confirmed = results.flat().filter(Boolean).filter(f => f.verdict && f.verdict.isReal)
|
|
97
|
+
log(`confirmed ${confirmed.length} findings`)
|
|
98
|
+
|
|
99
|
+
phase('Implement')
|
|
100
|
+
const byLocation = {}
|
|
101
|
+
for (const f of confirmed) {
|
|
102
|
+
const loc = f.fixLocation || 'kit'
|
|
103
|
+
byLocation[loc] = byLocation[loc] || []
|
|
104
|
+
byLocation[loc].push(f)
|
|
105
|
+
}
|
|
106
|
+
const implGroups = Object.entries(byLocation)
|
|
107
|
+
const implResults = await parallel(implGroups.map(([loc, items]) => () =>
|
|
108
|
+
agent(
|
|
109
|
+
`You are implementing confirmed UX findings directly in source. Repos: app at ${APP}, kit at ${KIT}. This group's fixLocation is "${loc}".\nApply EVERY fix below directly to real source files. Be surgical -- minimal diffs, no unrelated refactors, no new dependencies.\n\n` +
|
|
110
|
+
items.map(f => `- [${f.id}] (${f.severity}) ${f.title}\n evidence: ${f.evidence}\n fix: ${f.fix}`).join('\n\n') +
|
|
111
|
+
`\n\nAfter editing, report a short list of exactly which files you changed and how.`,
|
|
112
|
+
{ label: 'implement:' + loc, phase: 'Implement' }
|
|
113
|
+
)
|
|
114
|
+
))
|
|
115
|
+
|
|
116
|
+
return { confirmed: confirmed.map(f => ({ id: f.id, title: f.title, severity: f.severity, fixLocation: f.fixLocation, evidence: f.evidence, fix: f.verdict.refinedFix || f.fix })), implResults }
|
package/.gm/prd.yml
CHANGED
|
@@ -2901,3 +2901,34 @@
|
|
|
2901
2901
|
notes: 'standing rule: kit changes only reach the shipped app after node scripts/build.mjs + copy dist into site/app/vendor/anentrypoint-design + push kit'
|
|
2902
2902
|
status: completed
|
|
2903
2903
|
witness: node scripts/build.mjs in /config/workspace/design completed with 0 errors; dist/247420.css grew 454635->456041 bytes, dist/247420.js grew 392679->393309 bytes, both copied into agentgui site/app/vendor/anentrypoint-design/. Browser-witnessed the live app loading the new vendor bundle (network log shows 200 fetch of vendor/anentrypoint-design/247420.js and .css, 0 pageErrors) and a specific new rule (.ds-select min-height:32px) computing correctly in the live DOM.
|
|
2904
|
+
- id: ai-tell-gradients
|
|
2905
|
+
notes: audit design kit + app CSS for purple-blue/rainbow gradient fills, glassmorphism blur, generic drop-shadow overuse - hallmark AI-generated UI tells
|
|
2906
|
+
status: completed
|
|
2907
|
+
witness: 'codesearch: grep -n linear-gradient site/app/vendor/anentrypoint-design/247420.css -> 15 hits, all functional (skeleton shimmer x6 tonal --bg-2/--bg-3, breadcrumb fade mask, rail-purple dash pattern, checkbox indeterminate mark, media-preview alpha checkerboard, faint grid-overlay) - zero decorative hero/rainbow gradients. 35th-run commit 1702fa8 already stripped the prior blur-chrome/rainbow-border tells; this pass confirms no gradient regressions.'
|
|
2908
|
+
- id: ai-tell-emoji-icons
|
|
2909
|
+
notes: sweep for emoji-as-icon usage and generic rounded-everything corner-radius patterns that read as templated AI output
|
|
2910
|
+
status: completed
|
|
2911
|
+
witness: 'exec: grep -noP unicode-emoji-range site/app/js/app.js site/app/vendor/anentrypoint-design/247420.js -> zero matches. AGENTS.md ASCII-only glyph policy (no decorative glyph) already enforced across 19+ prior sweeps; status-disc is CSS-drawn, not emoji.'
|
|
2912
|
+
- id: ai-tell-generic-copy
|
|
2913
|
+
notes: sweep copy for generic AI-assistant boilerplate phrasing (Welcome!, Get started!, excessive exclamation) vs the established terse product voice
|
|
2914
|
+
status: completed
|
|
2915
|
+
witness: 'exec: grep -noE ''Welcome!|Get started!|Oops!|Awesome!|Great!'' site/app/js/app.js site/app/index.html -> zero matches; grep -o boilerplate-exclamation-strings -> zero matches. Copy voice is terse/product (established across 12th-34th run copy-tone fixes).'
|
|
2916
|
+
- id: ai-tell-color-overuse
|
|
2917
|
+
notes: check for oversaturated accent overuse (everything colored) vs restrained tonal hierarchy
|
|
2918
|
+
status: completed
|
|
2919
|
+
witness: 'exec: grep -c ''var(--accent)'' 247420.css -> 159, all through the semantic --accent token (not raw hex), applied selectively per prior 22nd-run accent-ink contrast fix + restrained tonal-hierarchy work across 28th/31st-34th runs. No raw saturated-everything pattern found.'
|
|
2920
|
+
- id: wfgy-critique-pass
|
|
2921
|
+
notes: run gui-ux-craft-style multi-lens workflow against the live GUI targeting AI-design-tell removal specifically, confirm+implement findings
|
|
2922
|
+
status: completed
|
|
2923
|
+
witness: 'Workflow wf_a53b7d8f-27b (gui-ai-tell-sweep, 26 agents, 25 done/1 verify-connection-error, 200 tool calls, 1.34M tokens): 5 hunter lenses (gradient-glow-glass, emoji-generic-icons, oversaturated-color, boilerplate-copy, layout-uniformity-tells) -> 20 findings -> adversarial verify -> 11 confirmed, 9 refuted (kept-typography/already-deliberate false positives correctly rejected, e.g. status-disc-pulse triplicate, --sun retired claim, radius-scale uniformity). Implemented directly: design/app-shell.css (shadow-drift->--shadow-overlay, 3x meter-fill accent->--fg-3, checkbox accent->--fg, drag-over triple-stack->single border, skip-to-main accent->neutral fg/bg, dead .empty-state child rules + dead .ds-panel-trio/.ds-panel-grid removed), design/chat.css (session-unread accent->--fg-2), design/src/components/agent-chat.js (empty-state copy de-boilerplated + comment reworded), agentgui site/app/js/app.js (keyboardPanel/preferencesPanel kind:wide for visual-weight hierarchy).'
|
|
2924
|
+
- id: kit-rebuild-vendor
|
|
2925
|
+
notes: rebuild design kit dist and re-vendor into site/app/vendor after any kit CSS/JS changes
|
|
2926
|
+
status: completed
|
|
2927
|
+
witness: 'exec: cd /config/workspace/design && node scripts/build.mjs -> [247420] css scoped+bundled 441.6kb, js minified 383.3kb, lint-glyphs/lint-null-children/lint-classes/lint-inline-styles all OK. cp dist/247420.{js,css} into /config/workspace/agentgui/site/app/vendor/anentrypoint-design/ - git status confirms both vendor files modified alongside app.js and .gm/prd.yml.'
|
|
2928
|
+
- id: browser-witness-final
|
|
2929
|
+
notes: browser-witness all changes live, 0 console errors, before/after screenshots
|
|
2930
|
+
status: completed
|
|
2931
|
+
witness: 'browser dispatch against live http://localhost:3012/gm/?token=... (real running bun server.js, PORT 3012): page.evaluate read live CSSOM post-fix -> barchartFillBg:''var(--fg-3)'', dragOverCss:''.ds-247420 .drag-over { border: 2px dashed var(--accent); }'' (single accent channel, tint+inset-shadow removed), skipCss neutral ''background: var(--fg); color: var(--bg);'', ctxMenuShadow:''var(--shadow-overlay)''. debug.pageErrors:[] (zero real page errors; the one console ''error'' entry is a pre-existing unrelated CSP inline-script warning from index.html''s boot script, not caused by this session''s changes). title:''agentgui'', bodyLen:1016 (app rendered, not blank).'
|
|
2932
|
+
- id: ci-green-push
|
|
2933
|
+
notes: push kit + agentgui changes, confirm CI green both repos
|
|
2934
|
+
status: pending
|
package/package.json
CHANGED
package/site/app/js/app.js
CHANGED
|
@@ -2735,7 +2735,7 @@ function historyMain() {
|
|
|
2735
2735
|
// lone spinner collapsing the slowest pane in the product.
|
|
2736
2736
|
: h('div', { key: 'loading' }, EventList({ items: [], loading: true,
|
|
2737
2737
|
loadingText: state.eventsSlow ? 'Indexing your Claude history — the first load can take a minute…' : 'loading events…' }));
|
|
2738
|
-
return [reconnectAlert(), head, actions, Panel({ title: 'events', children: body })].filter(Boolean);
|
|
2738
|
+
return [reconnectAlert(), head, actions, Panel({ title: 'events', kind: 'wide', children: body })].filter(Boolean);
|
|
2739
2739
|
}
|
|
2740
2740
|
|
|
2741
2741
|
if (!state.expandedEvents) state.expandedEvents = new Set();
|
|
@@ -2777,7 +2777,7 @@ function historyMain() {
|
|
|
2777
2777
|
].filter(Boolean),
|
|
2778
2778
|
});
|
|
2779
2779
|
if (filteredEvents.length === 0) {
|
|
2780
|
-
return [reconnectAlert(), head, actions, Panel({ title: 'events', children: [
|
|
2780
|
+
return [reconnectAlert(), head, actions, Panel({ title: 'events', kind: 'wide', children: [
|
|
2781
2781
|
h('div', { key: 'evmeta' }, meta),
|
|
2782
2782
|
h('div', { key: 'evfp' }, filterPills),
|
|
2783
2783
|
h('div', { key: 'nofilt', class: 'lede empty-state', role: 'status' },
|
|
@@ -2808,6 +2808,7 @@ function historyMain() {
|
|
|
2808
2808
|
actions,
|
|
2809
2809
|
Panel({
|
|
2810
2810
|
title: plural(total, 'event') + (ef !== 'all' ? ' (' + ef + ' filter)' : '') + (hiddenCount > 0 ? ' (showing last ' + shown.length + '; ' + hiddenCount + ' older)' : ''),
|
|
2811
|
+
kind: 'wide',
|
|
2811
2812
|
children: [h('div', { key: 'evmeta' }, meta), h('div', { key: 'evfp' }, filterPills), eventControls, EventList({
|
|
2812
2813
|
items: shown.map((e, i) => {
|
|
2813
2814
|
// Stable key: prefer the server-assigned event index, else the
|
|
@@ -3189,6 +3190,7 @@ function settingsMain() {
|
|
|
3189
3190
|
Panel({
|
|
3190
3191
|
id: 'appearance',
|
|
3191
3192
|
title: 'appearance',
|
|
3193
|
+
kind: 'wide',
|
|
3192
3194
|
children: [
|
|
3193
3195
|
h('div', { key: 'apl', class: 't-meta agentgui-field-mb' }, 'theme - follows the OS in auto, or pick light/dark.'),
|
|
3194
3196
|
ThemeToggle({ key: 'tt' }),
|
|
@@ -3241,6 +3243,7 @@ function keyboardPanel() {
|
|
|
3241
3243
|
return Panel({
|
|
3242
3244
|
id: 'keyboard',
|
|
3243
3245
|
title: 'keyboard',
|
|
3246
|
+
kind: 'wide',
|
|
3244
3247
|
children: ShortcutList({ shortcuts: SHORTCUTS }),
|
|
3245
3248
|
});
|
|
3246
3249
|
}
|
|
@@ -3275,6 +3278,7 @@ function preferencesPanel() {
|
|
|
3275
3278
|
return Panel({
|
|
3276
3279
|
id: 'data',
|
|
3277
3280
|
title: 'data',
|
|
3281
|
+
kind: 'wide',
|
|
3278
3282
|
children: [
|
|
3279
3283
|
// The server version lives in the server panel; carry only a build stamp
|
|
3280
3284
|
// here, and only when it differs from the server version.
|
|
@@ -230,12 +230,11 @@
|
|
|
230
230
|
--ease-exit: cubic-bezier(0.4, 0, 1, 1);
|
|
231
231
|
|
|
232
232
|
/* Print texture — the signature surface treatment. --grain is a tiled
|
|
233
|
-
fractal-noise data-URI
|
|
234
|
-
|
|
233
|
+
fractal-noise data-URI, opt-in via the .ds-grain utility and tuned faint
|
|
234
|
+
so type stays crisp. */
|
|
235
235
|
--grain-opacity: 0.05;
|
|
236
236
|
--grain-blend: multiply;
|
|
237
237
|
--grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
|
|
238
|
-
--halftone-size: 6px;
|
|
239
238
|
/* Offset-ink registration — the small print-mark offset used on editorial
|
|
240
239
|
edges (panel spine, expo numbering). A literal length, not a color. */
|
|
241
240
|
--ink-offset: 3px;
|
|
@@ -745,8 +744,7 @@
|
|
|
745
744
|
align-items: center; gap: var(--space-4);
|
|
746
745
|
min-height: var(--app-topbar-h);
|
|
747
746
|
padding: 10px var(--pad-x);
|
|
748
|
-
background: color-mix(in oklab, var(--bg)
|
|
749
|
-
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
|
747
|
+
background: color-mix(in oklab, var(--bg) 97%, transparent);
|
|
750
748
|
}
|
|
751
749
|
|
|
752
750
|
/* Merged chrome: when AppShell gets both a topbar and a crumb it wraps them
|
|
@@ -759,8 +757,7 @@
|
|
|
759
757
|
display: flex; flex-wrap: wrap; align-items: center;
|
|
760
758
|
gap: var(--space-2) var(--space-4);
|
|
761
759
|
padding: 8px var(--pad-x);
|
|
762
|
-
background: color-mix(in oklab, var(--bg)
|
|
763
|
-
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
|
760
|
+
background: color-mix(in oklab, var(--bg) 97%, transparent);
|
|
764
761
|
}
|
|
765
762
|
.ds-247420 .app-chrome > .app-topbar,
|
|
766
763
|
.ds-247420 .app-chrome > .app-crumb {
|
|
@@ -1147,14 +1144,6 @@
|
|
|
1147
1144
|
transition: box-shadow var(--dur-base) var(--ease-spring), border-color var(--dur-snap) var(--ease),
|
|
1148
1145
|
transform var(--dur-base) var(--ease-spring);
|
|
1149
1146
|
}
|
|
1150
|
-
/* Vercel-style card lift: any panel sitting inside a density grid (trio/grid)
|
|
1151
|
-
gets a restrained hover escalation — the standalone/full-width panel stays
|
|
1152
|
-
flat so page chrome doesn't jitter on scroll-adjacent hover. */
|
|
1153
|
-
.ds-247420 .ds-panel-trio > .panel:hover, .ds-247420 .ds-panel-grid > .panel:hover {
|
|
1154
|
-
box-shadow: var(--shadow-2);
|
|
1155
|
-
border-color: var(--rule-strong);
|
|
1156
|
-
transform: translateY(-2px);
|
|
1157
|
-
}
|
|
1158
1147
|
/* Editorial accent spine — an opt-in printed-ink rule down the left edge that
|
|
1159
1148
|
reads as a magazine pull-quote bar, not a glow card. */
|
|
1160
1149
|
.ds-247420 .panel.panel-spine {
|
|
@@ -1167,7 +1156,7 @@
|
|
|
1167
1156
|
border-radius: var(--bw-chunk) 0 0 var(--bw-chunk);
|
|
1168
1157
|
}
|
|
1169
1158
|
.ds-247420 .panel.panel-inline { background: var(--bg-2); padding: var(--space-3) var(--space-3); }
|
|
1170
|
-
.ds-247420 .panel.panel-wide { background: transparent; box-shadow: none; }
|
|
1159
|
+
.ds-247420 .panel.panel-wide { background: transparent; box-shadow: none; border: none; }
|
|
1171
1160
|
.ds-247420 .panel-head {
|
|
1172
1161
|
display: flex; align-items: baseline; justify-content: space-between;
|
|
1173
1162
|
/* Shares the .panel-body 16px side padding so the caps label and the body
|
|
@@ -1310,16 +1299,6 @@
|
|
|
1310
1299
|
max-width: var(--measure-wide);
|
|
1311
1300
|
position: relative; isolation: isolate;
|
|
1312
1301
|
}
|
|
1313
|
-
/* Restrained depth wash, not a marketing glow-gradient: a single very-low-
|
|
1314
|
-
opacity accent-ink radial anchored top-right, sitting under the grain/
|
|
1315
|
-
halftone texture (z-index -1, same isolation trick as .ds-hero-aside::after)
|
|
1316
|
-
so it reads as ambient light rather than a colored blob. Token-only. */
|
|
1317
|
-
.ds-247420 .ds-hero::before {
|
|
1318
|
-
content: ''; position: absolute; z-index: -1;
|
|
1319
|
-
top: -10%; right: -10%; width: 60%; height: 140%;
|
|
1320
|
-
background: radial-gradient(closest-side, color-mix(in oklab, var(--accent-ink) 10%, transparent), transparent 70%);
|
|
1321
|
-
pointer-events: none;
|
|
1322
|
-
}
|
|
1323
1302
|
.ds-247420 .ds-hero-head { grid-area: title; display: grid; gap: var(--space-3); }
|
|
1324
1303
|
.ds-247420 .ds-hero-body { grid-area: body; }
|
|
1325
1304
|
/* The right column carries its own visual weight — a spined, halftone-
|
|
@@ -1339,13 +1318,6 @@
|
|
|
1339
1318
|
width: var(--bw-chunk); background: var(--accent);
|
|
1340
1319
|
border-radius: var(--bw-chunk) 0 0 var(--bw-chunk);
|
|
1341
1320
|
}
|
|
1342
|
-
.ds-247420 .ds-hero-aside::after {
|
|
1343
|
-
content: ''; position: absolute; inset: 0; z-index: -1;
|
|
1344
|
-
pointer-events: none;
|
|
1345
|
-
background-image: radial-gradient(currentColor 1px, transparent 1.2px);
|
|
1346
|
-
background-size: var(--halftone-size) var(--halftone-size);
|
|
1347
|
-
opacity: 0.1; border-radius: inherit;
|
|
1348
|
-
}
|
|
1349
1321
|
.ds-247420 .ds-hero-stats { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
1350
1322
|
.ds-247420 .ds-hero-stat {
|
|
1351
1323
|
font-family: var(--ff-mono); font-size: var(--fs-sm);
|
|
@@ -1421,16 +1393,6 @@
|
|
|
1421
1393
|
opacity: var(--grain-opacity);
|
|
1422
1394
|
mix-blend-mode: var(--grain-blend);
|
|
1423
1395
|
}
|
|
1424
|
-
/* Halftone dot screen — a heavier print tell for accent panels/hero. */
|
|
1425
|
-
.ds-247420 .ds-halftone { position: relative; isolation: isolate; }
|
|
1426
|
-
.ds-247420 .ds-halftone::after {
|
|
1427
|
-
content: ''; position: absolute; inset: 0; z-index: -1;
|
|
1428
|
-
pointer-events: none;
|
|
1429
|
-
background-image: radial-gradient(currentColor 1px, transparent 1.2px);
|
|
1430
|
-
background-size: var(--halftone-size) var(--halftone-size);
|
|
1431
|
-
opacity: 0.12;
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
1396
|
/* ============================================================
|
|
1435
1397
|
Marquee / ticker — brand kinetic device ("always open 24/7"). The track
|
|
1436
1398
|
holds two identical runs so the loop is seamless; reduced-motion freezes it
|
|
@@ -1655,8 +1617,9 @@
|
|
|
1655
1617
|
}
|
|
1656
1618
|
.ds-247420 .ds-barchart-fill {
|
|
1657
1619
|
height: 100%; width: var(--bar-pct, 0%);
|
|
1658
|
-
background: var(--
|
|
1620
|
+
background: var(--fg-3); border-radius: var(--r-pill);
|
|
1659
1621
|
}
|
|
1622
|
+
.ds-247420 .ds-barchart-fill.is-active { background: var(--accent); }
|
|
1660
1623
|
.ds-247420 .ds-barchart-value {
|
|
1661
1624
|
font-size: var(--fs-xs); font-weight: 600; color: var(--fg-3);
|
|
1662
1625
|
font-variant-numeric: tabular-nums;
|
|
@@ -2076,15 +2039,15 @@
|
|
|
2076
2039
|
.ds-247420 .ds-file-row:active { transform: translateY(1px); }
|
|
2077
2040
|
.ds-247420 .ds-file-row.active { background: var(--accent-tint); border-color: var(--accent); }
|
|
2078
2041
|
.ds-247420 .ds-file-row[data-file-type="dir"] { border-left-width: 3px; border-left-color: var(--accent); }
|
|
2079
|
-
.ds-247420 .ds-file-row[data-file-type="image"]
|
|
2080
|
-
.ds-247420 .ds-file-row[data-file-type="video"]
|
|
2081
|
-
.ds-247420 .ds-file-row[data-file-type="audio"]
|
|
2082
|
-
.ds-247420 .ds-file-row[data-file-type="code"]
|
|
2083
|
-
.ds-247420 .ds-file-row[data-file-type="text"]
|
|
2084
|
-
.ds-247420 .ds-file-row[data-file-type="archive"]
|
|
2085
|
-
.ds-247420 .ds-file-row[data-file-type="document"]
|
|
2086
|
-
.ds-247420 .ds-file-row[data-file-type="symlink"]
|
|
2087
|
-
.ds-247420 .ds-file-row[data-file-type="other"] { border-left-width: 3px; border-left-color: var(--
|
|
2042
|
+
.ds-247420 .ds-file-row[data-file-type="image"],
|
|
2043
|
+
.ds-247420 .ds-file-row[data-file-type="video"],
|
|
2044
|
+
.ds-247420 .ds-file-row[data-file-type="audio"],
|
|
2045
|
+
.ds-247420 .ds-file-row[data-file-type="code"],
|
|
2046
|
+
.ds-247420 .ds-file-row[data-file-type="text"],
|
|
2047
|
+
.ds-247420 .ds-file-row[data-file-type="archive"],
|
|
2048
|
+
.ds-247420 .ds-file-row[data-file-type="document"],
|
|
2049
|
+
.ds-247420 .ds-file-row[data-file-type="symlink"],
|
|
2050
|
+
.ds-247420 .ds-file-row[data-file-type="other"] { border-left-width: 3px; border-left-color: var(--rule); }
|
|
2088
2051
|
|
|
2089
2052
|
/* Row internals — these are the classes FileRow actually renders. */
|
|
2090
2053
|
.ds-247420 .ds-file-row .title {
|
|
@@ -2155,13 +2118,13 @@
|
|
|
2155
2118
|
transition: background var(--dur-snap) var(--ease), border-color var(--dur-snap) var(--ease);
|
|
2156
2119
|
}
|
|
2157
2120
|
.ds-247420 .is-marked > .ds-check-box,
|
|
2158
|
-
.ds-247420[aria-checked="true"] > .ds-check-box { background: var(--
|
|
2121
|
+
.ds-247420[aria-checked="true"] > .ds-check-box { background: var(--fg); border-color: var(--fg); }
|
|
2159
2122
|
.ds-247420 .is-marked > .ds-check-box::after,
|
|
2160
2123
|
.ds-247420[aria-checked="true"] > .ds-check-box::after {
|
|
2161
2124
|
content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;
|
|
2162
2125
|
border: solid var(--bg); border-width: 0 1.5px 1.5px 0; transform: rotate(45deg);
|
|
2163
2126
|
}
|
|
2164
|
-
.ds-247420[aria-checked="mixed"] > .ds-check-box { background: var(--
|
|
2127
|
+
.ds-247420[aria-checked="mixed"] > .ds-check-box { background: var(--fg); border-color: var(--fg); }
|
|
2165
2128
|
.ds-247420[aria-checked="mixed"] > .ds-check-box::after {
|
|
2166
2129
|
content: ""; position: absolute; left: 3px; right: 3px; top: 6px; height: 2px; background: var(--bg); border-radius: var(--r-hair);
|
|
2167
2130
|
}
|
|
@@ -2299,7 +2262,7 @@
|
|
|
2299
2262
|
/* Stage — the scrollable content column. */
|
|
2300
2263
|
.ds-247420 .ds-file-stage {
|
|
2301
2264
|
display: flex; flex-direction: column; gap: var(--space-3);
|
|
2302
|
-
width: 100%; max-width:
|
|
2265
|
+
width: 100%; max-width: var(--measure-wide); margin: 0 auto;
|
|
2303
2266
|
padding: var(--space-2) var(--space-3) var(--space-4);
|
|
2304
2267
|
}
|
|
2305
2268
|
/* When the stage is the app-main child, the shell already supplies top
|
|
@@ -2308,7 +2271,7 @@
|
|
|
2308
2271
|
.ds-247420 .app-main .ds-file-stage { padding-top: 0; }
|
|
2309
2272
|
|
|
2310
2273
|
/* Full-width files stack: the stage's space-3 vertical beat without its
|
|
2311
|
-
|
|
2274
|
+
--measure-wide cap/auto-margins, so the roots / toolbar / bulkbar / uploads / grid
|
|
2312
2275
|
bands keep one consistent rhythm instead of touching edge-to-edge. Carries
|
|
2313
2276
|
the same side/bottom padding as .ds-file-stage (top collapses the same way
|
|
2314
2277
|
when mounted directly under .app-main, which already supplies top padding)
|
|
@@ -2464,9 +2427,10 @@
|
|
|
2464
2427
|
}
|
|
2465
2428
|
.ds-247420 .ds-upload-fill {
|
|
2466
2429
|
display: block; height: 100%; width: 0;
|
|
2467
|
-
background: var(--
|
|
2430
|
+
background: var(--fg-3); border-radius: var(--r-pill);
|
|
2468
2431
|
transition: width var(--dur-base) var(--ease);
|
|
2469
2432
|
}
|
|
2433
|
+
.ds-247420 .ds-upload-fill.is-active { background: var(--accent); }
|
|
2470
2434
|
/* Indeterminate: percent is unknown mid-transfer (fetch gives no chunk-level
|
|
2471
2435
|
progress) — a sliding marquee bar reads as "working", not stuck at 0%. */
|
|
2472
2436
|
.ds-247420 .ds-upload-bar.indeterminate .ds-upload-fill {
|
|
@@ -2531,7 +2495,6 @@
|
|
|
2531
2495
|
max(var(--space-3), env(safe-area-inset-bottom))
|
|
2532
2496
|
max(var(--space-3), env(safe-area-inset-left));
|
|
2533
2497
|
background: color-mix(in oklab, var(--ink) 55%, transparent);
|
|
2534
|
-
backdrop-filter: blur(3px);
|
|
2535
2498
|
}
|
|
2536
2499
|
@media (prefers-reduced-motion: no-preference) {
|
|
2537
2500
|
.ds-247420 .ds-modal-backdrop { animation: ds-modal-fade var(--dur-base) var(--ease); }
|
|
@@ -2993,7 +2956,7 @@
|
|
|
2993
2956
|
}
|
|
2994
2957
|
.ds-247420 .chat-msg:hover .chat-image,
|
|
2995
2958
|
.ds-247420 .chat-image:hover {
|
|
2996
|
-
border-color:
|
|
2959
|
+
border-color: var(--rule);
|
|
2997
2960
|
}
|
|
2998
2961
|
.ds-247420 .chat-image img { display: block; width: 100%; height: auto; max-height: 360px; object-fit: cover; }
|
|
2999
2962
|
.ds-247420 .chat-image .cap,
|
|
@@ -3008,12 +2971,12 @@
|
|
|
3008
2971
|
max-width: min(100%, 360px);
|
|
3009
2972
|
transition: background var(--dur-snap, .15s) var(--ease, ease), border-color var(--dur-snap, .15s) var(--ease, ease);
|
|
3010
2973
|
}
|
|
3011
|
-
.ds-247420 .chat-file:hover { background: var(--bg-3);
|
|
2974
|
+
.ds-247420 .chat-file:hover { background: var(--bg-3); }
|
|
3012
2975
|
.ds-247420 .chat-file .glyph {
|
|
3013
2976
|
font-size: 22px; width: 36px; height: 36px;
|
|
3014
2977
|
display: inline-flex; align-items: center; justify-content: center;
|
|
3015
|
-
background:
|
|
3016
|
-
color: var(--
|
|
2978
|
+
background: var(--bg-3);
|
|
2979
|
+
color: var(--fg-2); border-radius: var(--r-0); flex-shrink: 0; font-family: var(--ff-mono);
|
|
3017
2980
|
}
|
|
3018
2981
|
.ds-247420 .chat-file > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
3019
2982
|
.ds-247420 .chat-file .name {
|
|
@@ -3034,7 +2997,7 @@
|
|
|
3034
2997
|
border-radius: var(--r-1); overflow: hidden; max-width: min(100%, 480px);
|
|
3035
2998
|
transition: box-shadow 0.12s ease, border-color 0.12s ease;
|
|
3036
2999
|
}
|
|
3037
|
-
.ds-247420 .chat-msg:hover .chat-pdf { border-color:
|
|
3000
|
+
.ds-247420 .chat-msg:hover .chat-pdf { border-color: var(--rule); }
|
|
3038
3001
|
.ds-247420 .chat-pdf-head {
|
|
3039
3002
|
display: flex; align-items: center; gap: 10px; padding: 10px 14px;
|
|
3040
3003
|
background: color-mix(in oklab, var(--fg) 5%, var(--bg-2));
|
|
@@ -3061,7 +3024,6 @@
|
|
|
3061
3024
|
.ds-247420 .chat-msg:hover .chat-link,
|
|
3062
3025
|
.ds-247420 .chat-link:hover {
|
|
3063
3026
|
background: var(--bg-3);
|
|
3064
|
-
border-color: color-mix(in oklab, var(--accent) 40%, transparent);
|
|
3065
3027
|
}
|
|
3066
3028
|
.ds-247420 .chat-link:not(:has(.thumb)):not(:has(img)) { grid-template-columns: 1fr; }
|
|
3067
3029
|
.ds-247420 .chat-link .thumb,
|
|
@@ -3094,7 +3056,7 @@
|
|
|
3094
3056
|
transition: background var(--dur-snap, .12s) var(--ease, ease), border-color var(--dur-snap, .12s) var(--ease, ease), transform 0.12s ease;
|
|
3095
3057
|
position: relative;
|
|
3096
3058
|
}
|
|
3097
|
-
.ds-247420 .chat-reactions .rxn:hover { background: var(--bg-3);
|
|
3059
|
+
.ds-247420 .chat-reactions .rxn:hover { background: var(--bg-3); transform: scale(1.06); }
|
|
3098
3060
|
.ds-247420 .chat-reactions .rxn.you {
|
|
3099
3061
|
background: color-mix(in oklab, var(--accent) 18%, var(--bg-2));
|
|
3100
3062
|
border-color: color-mix(in oklab, var(--accent) 55%, transparent);
|
|
@@ -3422,9 +3384,7 @@
|
|
|
3422
3384
|
|
|
3423
3385
|
/* Drag-over state */
|
|
3424
3386
|
.ds-247420 .drag-over {
|
|
3425
|
-
background: color-mix(in oklab, var(--accent) 12%, var(--bg));
|
|
3426
3387
|
border: 2px dashed var(--accent);
|
|
3427
|
-
box-shadow: inset 0 0 0 1px var(--accent);
|
|
3428
3388
|
}
|
|
3429
3389
|
|
|
3430
3390
|
/* Drag-ghost (semi-transparent dragging element) */
|
|
@@ -3480,7 +3440,7 @@
|
|
|
3480
3440
|
background: var(--bg-2);
|
|
3481
3441
|
border: 1px solid var(--rule);
|
|
3482
3442
|
border-radius: var(--r-2);
|
|
3483
|
-
box-shadow:
|
|
3443
|
+
box-shadow: var(--shadow-overlay);
|
|
3484
3444
|
min-width: 160px;
|
|
3485
3445
|
z-index: calc(var(--z-overlay, 1000) + 1);
|
|
3486
3446
|
padding: 4px 0;
|
|
@@ -3762,7 +3722,7 @@
|
|
|
3762
3722
|
/* Skip to main content link */
|
|
3763
3723
|
.ds-247420 .skip-to-main {
|
|
3764
3724
|
position: absolute; top: -40px; left: 0;
|
|
3765
|
-
background: var(--
|
|
3725
|
+
background: var(--fg); color: var(--bg);
|
|
3766
3726
|
padding: 8px 16px; border-radius: var(--r-1);
|
|
3767
3727
|
text-decoration: none; z-index: var(--z-overlay, 1000);
|
|
3768
3728
|
}
|
|
@@ -3850,25 +3810,6 @@
|
|
|
3850
3810
|
Empty State & Loading States
|
|
3851
3811
|
-------------------------------------------------------------- */
|
|
3852
3812
|
|
|
3853
|
-
.ds-247420 .empty-state {
|
|
3854
|
-
display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
|
|
3855
|
-
padding: var(--space-8) var(--space-4);
|
|
3856
|
-
text-align: center; color: var(--fg-3);
|
|
3857
|
-
}
|
|
3858
|
-
.ds-247420 .empty-state--inline {
|
|
3859
|
-
flex-direction: row; align-items: center; gap: var(--space-2);
|
|
3860
|
-
padding: var(--space-2) 0; text-align: left;
|
|
3861
|
-
}
|
|
3862
|
-
.ds-247420 .empty-state-icon {
|
|
3863
|
-
font-size: 48px; opacity: 0.4;
|
|
3864
|
-
}
|
|
3865
|
-
.ds-247420 .empty-state-title {
|
|
3866
|
-
font-size: var(--fs-lg); font-weight: 600; color: var(--fg-2);
|
|
3867
|
-
}
|
|
3868
|
-
.ds-247420 .empty-state-body {
|
|
3869
|
-
font-size: var(--fs-sm); max-width: 40ch;
|
|
3870
|
-
}
|
|
3871
|
-
|
|
3872
3813
|
/* Skeleton loading state */
|
|
3873
3814
|
.ds-247420 .skeleton {
|
|
3874
3815
|
background: linear-gradient(
|
|
@@ -3902,7 +3843,7 @@
|
|
|
3902
3843
|
border-radius: var(--r-2);
|
|
3903
3844
|
padding: var(--space-3) var(--space-4);
|
|
3904
3845
|
max-width: 320px;
|
|
3905
|
-
box-shadow:
|
|
3846
|
+
box-shadow: var(--shadow-overlay);
|
|
3906
3847
|
z-index: calc(var(--z-overlay, 1000) + 10);
|
|
3907
3848
|
}
|
|
3908
3849
|
|
|
@@ -4649,7 +4590,8 @@
|
|
|
4649
4590
|
.ds-247420 .ds-bar-row { display: flex; align-items: center; gap: 8px; margin: 3px 0; font-size: var(--fs-tiny); }
|
|
4650
4591
|
.ds-247420 .ds-bar-row-label { width: 100px; flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
4651
4592
|
.ds-247420 .ds-bar-bg { flex: 1; min-width: 60px; background: var(--bg-3); border-radius: var(--r-hair); height: 6px; overflow: hidden; }
|
|
4652
|
-
.ds-247420 .ds-bar-fill { background: var(--
|
|
4593
|
+
.ds-247420 .ds-bar-fill { background: var(--fg-3); height: 100%; border-radius: var(--r-hair); }
|
|
4594
|
+
.ds-247420 .ds-bar-fill.is-active { background: var(--accent); }
|
|
4653
4595
|
.ds-247420 .ds-bar-row-value { flex-shrink: 0; color: var(--fg-2); max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
4654
4596
|
@media (max-width: 400px) {
|
|
4655
4597
|
.ds-247420 .ds-bar-row-label { width: 72px; }
|
|
@@ -4762,13 +4704,6 @@
|
|
|
4762
4704
|
@media (max-width: 760px) {
|
|
4763
4705
|
.ds-247420 .ds-panel-duo { grid-template-columns: 1fr; }
|
|
4764
4706
|
}
|
|
4765
|
-
/* Wide-viewport density: auto-fit grid so panel COUNT tracks actual children
|
|
4766
|
-
instead of a hardcoded column number — 2 children sit 2-up, 5 wrap to a
|
|
4767
|
-
third row, never an arbitrary 3-card reflex (same auto-fit/minmax pattern
|
|
4768
|
-
as .ds-tile-grid). Panels floor at 280px before wrapping, so ultra-wide
|
|
4769
|
-
screens still get more columns than a fixed repeat(3, ...) ever allowed. */
|
|
4770
|
-
.ds-247420 .ds-panel-trio { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: var(--space-2); margin: var(--space-2) 0; }
|
|
4771
|
-
.ds-247420 .ds-panel-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--space-2); margin: var(--space-2) 0; }
|
|
4772
4707
|
/* wide dense rows/tables scroll inside their panel, never the page */
|
|
4773
4708
|
.ds-247420 .ds-scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
|
|
4774
4709
|
|
|
@@ -7176,7 +7111,7 @@
|
|
|
7176
7111
|
.ds-247420 .ds-session-agent { font-size: var(--fs-tiny); color: var(--fg-3); }
|
|
7177
7112
|
/* New-activity cue is a hollow accent RING so it stays shape-distinct from the
|
|
7178
7113
|
running live disc (a solid pulsing accent dot) for colour-blind / reduced-motion users. */
|
|
7179
|
-
.ds-247420 .ds-session-unread { width: 8px; height: 8px; border-radius: 50%; background:
|
|
7114
|
+
.ds-247420 .ds-session-unread { width: 8px; height: 8px; border-radius: 50%; background: var(--fg-2); display: inline-block; }
|
|
7180
7115
|
.ds-247420 .ds-session-state { padding: var(--space-5) var(--space-3); text-align: center; color: var(--fg-3); font-size: var(--fs-sm); }
|
|
7181
7116
|
.ds-247420 .ds-session-state-error { color: var(--flame); }
|
|
7182
7117
|
|
|
@@ -7232,6 +7167,17 @@
|
|
|
7232
7167
|
.ds-247420 .ds-context .row .meta { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-2); }
|
|
7233
7168
|
.ds-247420 .ds-context .row .sub { font-size: var(--fs-xs); color: var(--fg-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
7234
7169
|
.ds-247420 .ds-context-actions { display: flex; gap: var(--space-2); }
|
|
7170
|
+
/* Lighter fact groups for secondary context blocks (conversation/last turn/
|
|
7171
|
+
recent files): a small caps label + Row list with no card chrome, so a
|
|
7172
|
+
2-row fact doesn't compete visually with the primary bordered panel above
|
|
7173
|
+
it. Separated from neighbors by a hairline rule, not a border box. */
|
|
7174
|
+
.ds-247420 .ds-context-group { padding-top: var(--space-3); border-top: var(--bw-hair) solid var(--rule); }
|
|
7175
|
+
.ds-247420 .ds-context-group-label {
|
|
7176
|
+
font-size: var(--fs-tiny); font-weight: 600; color: var(--fg-3);
|
|
7177
|
+
letter-spacing: var(--tr-caps); text-transform: uppercase;
|
|
7178
|
+
padding: 0 0 var(--space-2);
|
|
7179
|
+
}
|
|
7180
|
+
.ds-247420 .ds-context-group .row + .row { margin-top: 2px; }
|
|
7235
7181
|
|
|
7236
7182
|
/* ============================================================================
|
|
7237
7183
|
Cohesion sweep (gui-cohesion.js): one-product polish across surfaces.
|