diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css
index 512b592a..a558e1aa 100644
--- a/frontend/src/app/globals.css
+++ b/frontend/src/app/globals.css
@@ -357,7 +357,7 @@ button:disabled { cursor: default; opacity: .45; }
.birth-time-assessment-progress > span { font-size: var(--type-body-sm); line-height: 1.55; text-wrap: pretty; word-break: auto-phrase; }
.onboarding-inline-error { margin-left: 0; }
-.message-assistant { align-items: flex-start; justify-content: flex-start; }
+.message-assistant { align-items: flex-start; justify-content: flex-start; gap: var(--space-3); }
.message-user { justify-content: flex-end; }
.message-user .message-content { display: flex; flex-direction: column; align-items: flex-end; }
.message-assistant .message-content { width: auto; max-width: none; flex: 1; }
@@ -1055,10 +1055,9 @@ button:disabled { cursor: default; opacity: .45; }
.rectification-entry-error { grid-column: 1 / -1; margin: 0; }
.message-list {
- /* The assistant reply starts at the column edge now; the 32px avatar and
- its gap are gone, so anything that used to line up beside it (follow-up
- chips, the run timeline) lines up with the text itself. */
- --assistant-content-inset: 0px;
+ /* Follow-up chips and the run timeline line up with the assistant's text,
+ which starts past the avatar and its gap. */
+ --assistant-content-inset: calc(32px + var(--space-3));
margin: 0 auto;
width: min(900px, 100%);
padding: var(--space-8) var(--space-8) var(--space-16);
@@ -1071,6 +1070,8 @@ button:disabled { cursor: default; opacity: .45; }
color: var(--color-ink-secondary);
}
.message { display: flex; padding: var(--space-2) 0; }
+/* Restored after real-device review; see chat-message-row.tsx for why. */
+.agent-avatar { width: 32px; height: 32px; display: block; flex: 0 0 32px; margin-top: var(--space-2); border-radius: 50%; background: var(--color-canvas) url("/jyotish-logo.png") center / contain no-repeat; box-shadow: 0 0 0 1px var(--ring-hairline); }
.message-content { min-width: 0; max-width: min(80%, 680px); }
.message-bubble { overflow: hidden; border: 0; padding: var(--space-3) var(--space-4); border-radius: var(--radius-lg); background: var(--color-canvas-muted); }
.message-assistant .message-bubble { border-radius: 0; background: transparent; padding: var(--space-3) 0; }
@@ -2240,6 +2241,14 @@ button.nav-rail-identity:hover { background: var(--sidebar-accent); }
gap: var(--space-4);
}
+.conversation:not(.is-empty):not(.is-rectification) .message-assistant .agent-avatar {
+ width: 30px;
+ height: 30px;
+ flex-basis: 30px;
+ margin-top: 2px;
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-border) 82%, transparent);
+}
+
.conversation:not(.is-empty):not(.is-rectification) .message-assistant .message-bubble {
padding: 0 0 var(--space-2);
@@ -2531,9 +2540,9 @@ button.nav-rail-identity:hover { background: var(--sidebar-accent); }
The pending state keeps the same box for the same reason. */
.rectification-workspace__chat {
--rectification-timeline-height: 64px;
- /* Same token as `.message-list`. The bar sits outside the transcript, so it
- cannot inherit that custom property from there. */
- --assistant-content-inset: 0px;
+ /* Same token as `.message-list`: avatar 32px + gap. The bar sits outside the
+ transcript, so it cannot inherit that custom property from there. */
+ --assistant-content-inset: calc(32px + var(--space-3));
}
.rectification-timeline {
height: var(--rectification-timeline-height);
@@ -2614,7 +2623,10 @@ button.nav-rail-identity:hover { background: var(--sidebar-accent); }
width: 100%;
height: 11px;
border-radius: 2px;
- background: var(--color-action-soft);
+ /* Was --color-action-soft, which is a tint for text backgrounds: on the
+ canvas it left the range band all but invisible. This is the one mark
+ that has to read at a glance. */
+ background: color-mix(in srgb, var(--color-action-strong) 32%, var(--color-canvas));
transform-origin: left center;
will-change: transform;
transition: transform 420ms cubic-bezier(.32, .72, .24, 1);
diff --git a/frontend/src/components/chat-message-row.tsx b/frontend/src/components/chat-message-row.tsx
index 9f6677da..bf367ee3 100644
--- a/frontend/src/components/chat-message-row.tsx
+++ b/frontend/src/components/chat-message-row.tsx
@@ -31,6 +31,14 @@ function motionPreferred() {
if (motionPreferred()) prefetchOnIdle(loadGsap);
+/* Restored after real-device review: R3 removed it on the grounds that it was
+ the most repeated element in a long transcript, but without it the assistant
+ turn has no visual owner — in the rectification surface especially, where
+ narration, a question and its options all stack as plain text. */
+export function AgentAvatar() {
+ return ;
+}
+
export const ChatMessageRow = memo(function ChatMessageRow({
message,
showActivity = message.state !== "settled",
@@ -123,6 +131,7 @@ export const ChatMessageRow = memo(function ChatMessageRow({
className={`message message-${message.role}`}
aria-label={message.role === "assistant" ? assistantLabel : "你"}
>
+ {message.role === "assistant" &&