From 92281c374d7d92c0ea53d81696c84a2e240fd28d Mon Sep 17 00:00:00 2001 From: chaxus Date: Fri, 16 Oct 2026 19:36:02 +0800 Subject: [PATCH 1/4] fix(assistant): align compact composer controls --- styles/assistant.css | 30 ++++------ test/e2e/agent-composer-ux.spec.ts | 90 +++++++++++++++++++++++++++- test/e2e/agent-request-queue.spec.ts | 7 +++ 3 files changed, 107 insertions(+), 20 deletions(-) diff --git a/styles/assistant.css b/styles/assistant.css index 574ced714..64b866f43 100644 --- a/styles/assistant.css +++ b/styles/assistant.css @@ -201,8 +201,8 @@ body.agent-panel-resizing #app iframe { max-width: min(88%, 64ch); } .agent-panel .cui-composer:focus-within { - border-color: var(--ran-color-primary); - box-shadow: 0 0 0 1px var(--ran-color-primary); + border-color: var(--ran-color-text-secondary); + box-shadow: none; } .agent-panel .cui-message-actions .cui-apply { min-height: var(--ran-size-7); @@ -626,9 +626,7 @@ r-dropdown[role='listbox'] { @media (max-width: 480px) { .agent-panel-header button, .agent-panel-quote, - .agent-panel-clear, - .agent-panel .cui-send, - .agent-panel .cui-stop { + .agent-panel-clear { width: 44px; height: 44px; flex-shrink: 0; @@ -968,7 +966,7 @@ r-dropdown[role='listbox'] { border-color: var(--ran-color-border); background: var(--ran-color-bg-elevated); border-radius: var(--ran-radius-md); - padding: 13px; + padding: var(--ran-space-3); } .agent-panel .cui-msg-user .cui-bubble { padding: 12px 16px; @@ -978,17 +976,8 @@ r-dropdown[role='listbox'] { .agent-panel .cui-send, .agent-panel .cui-stop { border-radius: var(--ran-radius-sm); - width: var(--ran-size-8, 36px); - height: var(--ran-size-8, 36px); -} -/* Both execution controls keep the same geometry at narrow panel widths, - including a docked panel in a wide editor window. */ -@container agent-panel (max-width: 480px) { - .agent-panel .cui-send, - .agent-panel .cui-stop { - width: 40px; - height: 40px; - } + width: var(--ran-size-7); + height: var(--ran-size-7); } @media (pointer: coarse) { .agent-panel .cui-send, @@ -1399,7 +1388,7 @@ r-dropdown[role='listbox'] { --ran-select-hover-border: 1px solid transparent; --ran-select-height: 32px; --ran-select-min-height: 32px; - --ran-select-font-size: var(--ran-text-label-2); + --ran-select-font-size: var(--ran-text-label-3); --ran-select-color: var(--ran-color-text-secondary); --ran-select-item-hover-color: var(--ran-color-text); --ran-select-search-focus-color: var(--ran-color-text); @@ -2158,6 +2147,11 @@ r-dropdown[role='listbox'] { cursor: pointer; text-align: start; } +.agent-panel .cui-composer .agent-sources-toggle { + min-height: var(--ran-size-7); + padding: var(--ran-space-1) var(--ran-space-2); + font-size: var(--ran-text-label-3); +} .agent-workspace-view button:hover, .agent-artifacts button:hover, .agent-sources-toggle:hover, diff --git a/test/e2e/agent-composer-ux.spec.ts b/test/e2e/agent-composer-ux.spec.ts index 5ab2c8f49..30f644b26 100644 --- a/test/e2e/agent-composer-ux.spec.ts +++ b/test/e2e/agent-composer-ux.spec.ts @@ -213,31 +213,117 @@ for (const ext of ['docx', 'xlsx', 'pptx', 'pdf'] as const) { }); } -for (const width of [360, 1280]) { +for (const width of [320, 360, 1280]) { test(`composer stays compact and grows with multiline drafts at ${width}px`, async ({ page }, testInfo) => { await page.setViewportSize({ width, height: 900 }); await page.addInitScript(() => { localStorage.setItem('document-assistant-enabled', 'true'); - localStorage.setItem('agent-local-preset', 'Qwen3-4B-q4f16_1-MLC'); + localStorage.setItem( + 'agent-writing-endpoint', + JSON.stringify({ + version: 1, + kind: 'loopback', + baseUrl: 'http://localhost:11434', + model: 'density-test', + preference: 'device-first', + localWritingConsent: false, + }), + ); }); + await page.route('http://localhost:11434/api/**', (route) => + route.fulfill({ json: { models: [{ name: 'density-test' }] } }), + ); await page.goto('/editor?new=docx&locale=en'); await page.waitForFunction(() => Boolean(window.__ooFrames.readyEditor())); await page.frameLocator('iframe[name="frameEditor"]').locator('.agent-sidebar-entry').click(); const input = page.locator('.cui-input'); const composer = page.locator('.cui-composer'); await expect(input).toBeVisible(); + const send = page.locator('.cui-send'); + await input.fill('A draft before reconnecting'); + await expect(send).toHaveAccessibleName('Reconnect'); + // Preparation stays a readable text action rather than a clipped icon square. + expect((await send.boundingBox())!.width).toBeGreaterThan(32); + expect(await send.evaluate((el) => el.scrollWidth - el.clientWidth)).toBeLessThan(2); + await send.click(); + await expect(send).toHaveAccessibleName('Send'); + await expect(input).toHaveValue('A draft before reconnecting'); + await input.fill(''); + await expect(send).toBeDisabled(); + const controls = [send, page.locator('.agent-sources-toggle'), page.locator('.agent-speech-mic')]; + const bounds = await Promise.all(controls.map((control) => control.boundingBox())); + for (const box of bounds) expect(box!.height).toBe(32); + expect(bounds[0]!.width).toBe(32); + const centers = bounds.map((box) => box!.y + box!.height / 2); + expect(Math.max(...centers) - Math.min(...centers)).toBeLessThanOrEqual(1); + await expect(page.locator('.agent-sources-toggle')).toHaveCSS('font-size', '12px'); + await expect(page.locator('.agent-speech-language')).toHaveCSS('--ran-select-font-size', '12px'); + await expect(composer).toHaveCSS('padding', '12px'); + if (width === 1280) expect((await page.locator('.agent-runtime-panel').boundingBox())!.width).toBe(432); const empty = (await composer.boundingBox())!.height; + await page.locator('.agent-runtime-panel').screenshot({ path: testInfo.outputPath(`composer-empty-${width}.png`) }); expect(empty).toBeLessThan(115); + await input.focus(); + const focus = await composer.evaluate((el) => { + const style = getComputedStyle(el); + return { + border: style.borderTopWidth, + color: style.borderTopColor, + shadow: style.boxShadow, + }; + }); + expect(focus.border).toBe('1px'); + expect(focus.shadow).toBe('none'); + // Resolve the semantic token through a temporary inherited color, including dark mode. + const neutral = await composer.evaluate((el) => { + const probe = document.createElement('span'); + probe.style.color = 'var(--ran-color-text-secondary)'; + el.append(probe); + const color = getComputedStyle(probe).color; + probe.remove(); + return color; + }); + expect(focus.color).toBe(neutral); await input.fill(Array.from({ length: 12 }, (_, i) => `第 ${i + 1} 行:保持原文的事实和数字。`).join('\n')); expect((await input.boundingBox())!.height).toBeGreaterThan(80); expect((await input.boundingBox())!.height).toBeLessThanOrEqual(160); expect((await composer.boundingBox())!.height).toBeGreaterThan(empty + 40); + await page + .locator('.agent-runtime-panel') + .screenshot({ path: testInfo.outputPath(`composer-multiline-${width}.png`) }); await input.fill('Short draft'); + await expect(send).toBeEnabled(); + await page.locator('.agent-runtime-panel').screenshot({ path: testInfo.outputPath(`composer-short-${width}.png`) }); + await page.evaluate(() => document.documentElement.setAttribute('data-ran-theme', 'dark')); + await expect(composer).toHaveCSS('box-shadow', 'none'); + await page.locator('.agent-sources-toggle').focus(); + expect(await page.locator('.agent-sources-toggle').evaluate((el) => getComputedStyle(el).outlineWidth)).toBe('2px'); + await page.locator('.agent-runtime-panel').screenshot({ path: testInfo.outputPath(`composer-dark-${width}.png`) }); await expect.poll(async () => (await composer.boundingBox())!.height).toBeLessThanOrEqual(empty + 1); await input.fill(''); await expect.poll(async () => (await composer.boundingBox())!.height).toBeLessThanOrEqual(empty + 1); + expect(await composer.evaluate((el) => el.scrollWidth - el.clientWidth)).toBeLessThan(2); await page .locator('.agent-runtime-panel') .screenshot({ path: testInfo.outputPath(`composer-compact-${width}.png`) }); }); } + +test.describe('coarse pointer composer', () => { + test.use({ hasTouch: true }); + test('retains a 44-pixel execution target on touch hardware', async ({ page }) => { + await page.setViewportSize({ width: 360, height: 900 }); + await page.addInitScript(() => { + localStorage.setItem('document-assistant-enabled', 'true'); + localStorage.setItem('agent-local-preset', 'Qwen3-4B-q4f16_1-MLC'); + }); + await page.goto('/editor?new=docx&locale=en'); + await page.waitForFunction(() => Boolean(window.__ooFrames.readyEditor())); + await page.frameLocator('iframe[name="frameEditor"]').locator('.agent-sidebar-entry').click(); + expect(await page.evaluate(() => matchMedia('(pointer: coarse)').matches)).toBe(true); + const send = page.locator('.cui-send'); + await expect(send).toBeVisible(); + expect((await send.boundingBox())!.width).toBe(44); + expect((await send.boundingBox())!.height).toBe(44); + }); +}); diff --git a/test/e2e/agent-request-queue.spec.ts b/test/e2e/agent-request-queue.spec.ts index 1b2a49363..6d26b3d1c 100644 --- a/test/e2e/agent-request-queue.spec.ts +++ b/test/e2e/agent-request-queue.spec.ts @@ -80,8 +80,15 @@ for (const ext of ['docx', 'pptx'] as const) { const stop = page.locator('.cui-send-stop'); const sendSize = await send.boundingBox(); const stopSize = await stop.boundingBox(); + expect(sendSize?.width).toBe(32); + expect(sendSize?.height).toBe(32); expect(sendSize?.width).toBe(stopSize?.width); expect(sendSize?.height).toBe(stopSize?.height); + expect(sendSize!.y).toBe(stopSize!.y); + const toolbarBounds = await page.locator('.agent-speech-mic').boundingBox(); + expect(toolbarBounds!.height).toBe(32); + expect(toolbarBounds!.y).toBe(sendSize!.y); + expect(await page.locator('.cui-composer').evaluate((el) => el.scrollWidth - el.clientWidth)).toBeLessThan(2); await expect(page.locator('.agent-panel-title')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); await expect(page.locator('.cui-progress')).toHaveCSS('border-top-width', '0px'); await expect(page.locator('.cui-progress')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); From 93c1e16929b2460e9767f0e579a0a523c9b7473f Mon Sep 17 00:00:00 2001 From: chaxus Date: Fri, 16 Oct 2026 19:43:45 +0800 Subject: [PATCH 2/4] fix(assistant): retain readable language in narrow toolbars --- styles/assistant.css | 18 +++++++++++++++ test/e2e/agent-composer-ux.spec.ts | 1 + test/e2e/agent-request-queue.spec.ts | 33 ++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/styles/assistant.css b/styles/assistant.css index 64b866f43..8a3f11cbc 100644 --- a/styles/assistant.css +++ b/styles/assistant.css @@ -2245,6 +2245,24 @@ r-dropdown[role='listbox'] { min-width: 0; max-width: 96px; } +/* A narrow toolbar keeps a real language label when Stop joins Send. + Use the select's public spacing hooks rather than losing its text lane. */ +@container agent-panel (max-width: 360px) { + .agent-panel .cui-composer-bar, + .agent-panel .cui-actions { + gap: var(--ran-space-1); + } + .agent-panel .cui-composer .agent-sources-toggle { + padding-inline: var(--ran-space-1); + } + .agent-speech-language { + --ran-select-padding: 0 var(--ran-space-1); + --ran-select-item-left: 0; + --ran-select-item-right: var(--ran-size-1); + --ran-select-icon-inset-inline-end: 0; + --ran-select-font-weight: var(--ran-text-copy-weight); + } +} .agent-sources-view [hidden] { display: none; } diff --git a/test/e2e/agent-composer-ux.spec.ts b/test/e2e/agent-composer-ux.spec.ts index 30f644b26..58648578f 100644 --- a/test/e2e/agent-composer-ux.spec.ts +++ b/test/e2e/agent-composer-ux.spec.ts @@ -294,6 +294,7 @@ for (const width of [320, 360, 1280]) { await input.fill('Short draft'); await expect(send).toBeEnabled(); await page.locator('.agent-runtime-panel').screenshot({ path: testInfo.outputPath(`composer-short-${width}.png`) }); + await composer.screenshot({ path: testInfo.outputPath(`composer-only-${width}.png`) }); await page.evaluate(() => document.documentElement.setAttribute('data-ran-theme', 'dark')); await expect(composer).toHaveCSS('box-shadow', 'none'); await page.locator('.agent-sources-toggle').focus(); diff --git a/test/e2e/agent-request-queue.spec.ts b/test/e2e/agent-request-queue.spec.ts index 6d26b3d1c..cc5dce068 100644 --- a/test/e2e/agent-request-queue.spec.ts +++ b/test/e2e/agent-request-queue.spec.ts @@ -89,6 +89,35 @@ for (const ext of ['docx', 'pptx'] as const) { expect(toolbarBounds!.height).toBe(32); expect(toolbarBounds!.y).toBe(sendSize!.y); expect(await page.locator('.cui-composer').evaluate((el) => el.scrollWidth - el.clientWidth)).toBeLessThan(2); + const language = page.locator('.agent-speech-language'); + const languageMetrics = () => + language.evaluate((el) => { + const root = el.shadowRoot ?? (el as HTMLElement & { _shadowDom: ShadowRoot })._shadowDom; + const label = root.querySelector('[part="selection-item"]')!; + const range = document.createRange(); + range.selectNodeContents(label); + return { + width: label.getBoundingClientRect().width, + textWidth: range.getBoundingClientRect().width, + text: label.textContent, + scroll: label.scrollWidth, + client: label.clientWidth, + }; + }); + const english = await languageMetrics(); + expect(english.text).toBe('English'); + expect(english.width).toBeGreaterThanOrEqual(english.textWidth); + // Longer locale names may truncate; the label must retain a readable lane. + await language.evaluate((el) => { + (el as HTMLElement & { value: string }).value = 'pt-BR'; + }); + const longer = await languageMetrics(); + expect(longer.text).toBe('Português'); + expect(longer.width).toBeGreaterThanOrEqual(32); + expect(longer.scroll).toBeGreaterThan(longer.client); + await language.evaluate((el) => { + (el as HTMLElement & { value: string }).value = 'en-US'; + }); await expect(page.locator('.agent-panel-title')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); await expect(page.locator('.cui-progress')).toHaveCSS('border-top-width', '0px'); await expect(page.locator('.cui-progress')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); @@ -97,6 +126,9 @@ for (const ext of ['docx', 'pptx'] as const) { await page .locator('.agent-runtime-panel') .screenshot({ path: testInfo.outputPath(`queue-${ext}-collapsed.png`) }); + await page + .locator('.cui-footer') + .screenshot({ path: testInfo.outputPath(`queue-${ext}-320-composer-light.png`) }); await page.locator('.agent-queue-toggle').click(); await page.locator('.agent-queue-more').first().click(); await page @@ -108,6 +140,7 @@ for (const ext of ['docx', 'pptx'] as const) { .poll(() => page.locator('.cui-composer').evaluate((el) => el.scrollWidth - el.clientWidth)) .toBeLessThan(2); await page.locator('.agent-runtime-panel').screenshot({ path: testInfo.outputPath(`queue-${ext}-320-dark.png`) }); + await page.locator('.cui-footer').screenshot({ path: testInfo.outputPath(`queue-${ext}-320-composer-dark.png`) }); if (ext === 'docx') { await page.evaluate(() => { const win = window.__ooFrames.readyEditor() as unknown as { From 0d20080df7e7e1e68714cfa1d41d40e8846fb129 Mon Sep 17 00:00:00 2001 From: chaxus Date: Fri, 16 Oct 2026 19:47:50 +0800 Subject: [PATCH 3/4] docs(assistant): record live preview density acceptance --- .../plans/2026-10-16-ai-workspace-craft.md | 13 +++++++++++++ .../specs/2026-10-16-ai-workspace-craft-design.md | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/docs/superpowers/plans/2026-10-16-ai-workspace-craft.md b/docs/superpowers/plans/2026-10-16-ai-workspace-craft.md index 6d3ba6a88..b85026fb6 100644 --- a/docs/superpowers/plans/2026-10-16-ai-workspace-craft.md +++ b/docs/superpowers/plans/2026-10-16-ai-workspace-craft.md @@ -127,3 +127,16 @@ Standalone reference budgets: 20 MiB compressed input, 2,000 ZIP entries, 32 MiB | Safe task Undo | 7 | Exact owned history-tail tests; later edit disables Undo with explanation | | Google connection readiness | 8 | Unconfigured state and setup guide; real OAuth remains externally pending | | Minimal visual hierarchy | 2, 8 | Screenshots of narrow/light/dark/state matrix; single primary action, progressive details | + +## Chunk 6: Live-preview composer proportions + +The latest owner feedback continues the approved compact design. Implementation uses the existing checkout and PR #274. + +- [x] Walk through the deployed preview: enable/open, draft/navigation, optional connector failure, structured artifact import/edit/save version; inspect desktop and narrow composition. +- [x] Add browser geometry assertions for matching 32-pixel desktop send/stop/source/voice lanes, single focus border and no overflow. Exercise a docked narrow panel and small windows; retain coarse-pointer 44-pixel targets. Run before styling to demonstrate the regression. +- [x] In `styles/assistant.css`, consolidate host composer geometry: 12-pixel token inset, 32-pixel execution controls, consistent toolbar labels/lanes, no narrow-panel enlargement, single neutral focused boundary. At widths up to 360 pixels use 4-pixel toolbar gaps and public select inset variables so a busy state never reduces the language label to an empty chevron. Keep existing coarse-pointer override and keyboard affordances. +- [x] Run composer, queue, result hierarchy, sidebar/settings, source/artifact and error recovery browser suites, lint/types/format; inspect real screenshots. Prepare the same-PR delivery; retain the remote preview/CI/review gates below. + +Local evidence at code commit `6e22dad`: the initial 12 browser scenarios passed, followed by 10 narrow-language/voice scenarios and a final 35-scenario integration run across ten UI suites. Full lint/types, format/1,511 immutable archive checks and production build passed. Both meaningful regressions were observed before their CSS fixes: desktop controls enlarged to 40 pixels, and a busy 320-pixel language label had zero available text width. Spec and quality reviews approved the final changes. Root inspected actual light/dark/short/busy screenshots. + +Required remote gates: push the acceptance record, verify the new deployed preview, wait for all checks on the latest head and review the final increment. Record the authoritative result in PR #274, preserving the distinction between direct preview navigation/artifact checks and controlled model/voice responses. diff --git a/docs/superpowers/specs/2026-10-16-ai-workspace-craft-design.md b/docs/superpowers/specs/2026-10-16-ai-workspace-craft-design.md index 51dccf5a3..2bd1e33d0 100644 --- a/docs/superpowers/specs/2026-10-16-ai-workspace-craft-design.md +++ b/docs/superpowers/specs/2026-10-16-ai-workspace-craft-design.md @@ -48,3 +48,13 @@ Adopt existing queue/review/provider/store owners and small explicit contracts. Every listed outcome has focused red-green tests and browser verification in light/dark/narrow states. Preserve keyboard/IME, clipboard content, reference budgets, document freshness and explicit consent. Every error preserves the user's draft and offers an honest recovery. Inspect actual screenshots, not only computed CSS. Full coverage/type/lint/format/build and latest PR checks must pass; repeat overall review after CI. Report live OAuth as unverified until real deployment configuration exists, with actionable setup documentation. Implementation budgets and the per-outcome acceptance map are normative in the companion plan’s “Binding limits and acceptance map” section. They bound structured storage, standalone extraction and capability-based fallbacks before implementation. + +## Live-preview density correction + +The owner requested another live walkthrough of PR #274 and identified the composer send button as visually oversized. Actual preview inspection at desktop sidebar and full-width narrow layouts confirmed that the host's narrow-container rule enlarges the execution controls from 36 to 40 pixels while adjacent microphone controls stay at 32 pixels. The focused composer also combines its border with an extra primary-colored shadow. + +Continue the already approved compact direction: use the existing 32-pixel control token for desktop send, stop, source and voice toolbar lanes at all panel widths, keeping the existing 44-pixel coarse-pointer send/stop targets. At panel widths up to 360 pixels, reduce toolbar gaps and source horizontal padding to the existing 4-pixel token; use the select component's public padding/item/arrow variables to keep its selected language readable when Stop is present. Preserve normal-weight labels, dropdown ellipsis for longer languages and accessible full values. Use a single visible neutral focus border with no additional shadow, a 12-pixel content inset and the existing 8-pixel toolbar gap. Keep icons and copy readable, all names, keyboard focus, draft/IME behavior and request ownership intact. This is host styling only; shared ChatView contracts and runtime behavior do not change. + +Alternatives considered: retaining 36/40 pixels leaves the toolbar mismatch; shrinking only the arrow leaves the heavy button silhouette. A shared desktop control size addresses the cause without adding menus or hiding actions. + +Acceptance: real browser bounds at 320/360-pixel windows and a docked 320-pixel panel must show matching execution controls and no overflow. Coarse-pointer targets remain 44 pixels. Inspect light/dark, empty/one-line/multiline, busy/queued, focus and disabled states. Live preview checks can cover navigation, drafts, optional-connector feedback and artifact versions without credentials; generated responses and queues use controlled test providers, with that distinction stated explicitly. From db8f6e2e4af388b91aa09fbc4bf030c6fb692a65 Mon Sep 17 00:00:00 2001 From: chaxus Date: Fri, 16 Oct 2026 19:50:37 +0800 Subject: [PATCH 4/4] docs(assistant): clarify follow-up delivery after merge --- docs/superpowers/plans/2026-10-16-ai-workspace-craft.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/superpowers/plans/2026-10-16-ai-workspace-craft.md b/docs/superpowers/plans/2026-10-16-ai-workspace-craft.md index b85026fb6..a3f6c69cc 100644 --- a/docs/superpowers/plans/2026-10-16-ai-workspace-craft.md +++ b/docs/superpowers/plans/2026-10-16-ai-workspace-craft.md @@ -130,13 +130,13 @@ Standalone reference budgets: 20 MiB compressed input, 2,000 ZIP entries, 32 MiB ## Chunk 6: Live-preview composer proportions -The latest owner feedback continues the approved compact design. Implementation uses the existing checkout and PR #274. +The latest owner feedback continues the approved compact design. PR #274 merged during the walkthrough, so the density correction is delivered as a small follow-up branch based on the updated main. The transplanted source tree is identical to the locally verified tree. - [x] Walk through the deployed preview: enable/open, draft/navigation, optional connector failure, structured artifact import/edit/save version; inspect desktop and narrow composition. - [x] Add browser geometry assertions for matching 32-pixel desktop send/stop/source/voice lanes, single focus border and no overflow. Exercise a docked narrow panel and small windows; retain coarse-pointer 44-pixel targets. Run before styling to demonstrate the regression. - [x] In `styles/assistant.css`, consolidate host composer geometry: 12-pixel token inset, 32-pixel execution controls, consistent toolbar labels/lanes, no narrow-panel enlargement, single neutral focused boundary. At widths up to 360 pixels use 4-pixel toolbar gaps and public select inset variables so a busy state never reduces the language label to an empty chevron. Keep existing coarse-pointer override and keyboard affordances. -- [x] Run composer, queue, result hierarchy, sidebar/settings, source/artifact and error recovery browser suites, lint/types/format; inspect real screenshots. Prepare the same-PR delivery; retain the remote preview/CI/review gates below. +- [x] Run composer, queue, result hierarchy, sidebar/settings, source/artifact and error recovery browser suites, lint/types/format; inspect real screenshots. Prepare the follow-up delivery; retain the remote preview/CI/review gates below. Local evidence at code commit `6e22dad`: the initial 12 browser scenarios passed, followed by 10 narrow-language/voice scenarios and a final 35-scenario integration run across ten UI suites. Full lint/types, format/1,511 immutable archive checks and production build passed. Both meaningful regressions were observed before their CSS fixes: desktop controls enlarged to 40 pixels, and a busy 320-pixel language label had zero available text width. Spec and quality reviews approved the final changes. Root inspected actual light/dark/short/busy screenshots. -Required remote gates: push the acceptance record, verify the new deployed preview, wait for all checks on the latest head and review the final increment. Record the authoritative result in PR #274, preserving the distinction between direct preview navigation/artifact checks and controlled model/voice responses. +Required remote gates: push the acceptance record, verify the new deployed preview, wait for all checks on the latest head and review the final increment. Record the authoritative result in the follow-up PR, preserving the distinction between direct preview navigation/artifact checks and controlled model/voice responses.