The 600px / 380px breakpoints were @container docpane queries but
the email reader isn't inside a docpane container — they never
fired and the cluster wrapped to 3+ rows at narrow widths. Added
container-type:inline-size + container-name:emailreader on
.email-reader-header and switched the queries to that container,
so the 2-row cap now actually applies.
Three-step shrink:
1. > 600px pane: cluster sits in col 2 as 1 row of 6
2. 380-600px pane: cluster capped at 3-icon width so wrapping
stops at 3 + 3 (max 2 rows) — chips share width with the 2-row
cluster instead of multiplying into 3+ rows
3. < 380px pane: cluster snaps to absolute overlay with left-edge
box-shadow, still capped at 3-icon width so it's the same 2-row
shape but floating above the truncated chips
Grid tracks now:
- col 1: minmax(60px, 250px) — chip natural width capped at 250px,
with the 60px (4 char) floor enforced on From / To / Cc alike
- col 2: minmax(48px, 1fr) — takes the rest, shrinks first when
the pane narrows
Removed the hard max-width on the action cluster so on wide panes
it stays as one row of 6. Once col 2 shrinks below the 1-row width,
flex-wrap kicks in and the icons re-stack to 3+3. Chips only start
to shrink past that point.
- Action cluster's max-width is calc(48*3 + 4*2) so the 6 icons
always lay out as 3 top / 3 bottom by default.
- When the pane narrows the chips in col 1 shrink first (with 60px
min so 4 chars + ellipsis stay visible).
- At <380px the cluster snaps to absolute overlay with a left-edge
box-shadow so it reads as floating above the truncated chip.
Two-step shrink behavior:
1. As the pane narrows, the action cluster (max-width:50% of meta)
wraps to a 2-row icon stack first
2. Then the recipient chip span starts overflow-scrolling, but
keeps a 60px min-width (~4 chars) so the first chars of the
sender/recipient name stay visible
Previously only the From row affected the action cluster's column
width — To/Cc detail rows spanned both columns and ignored the
cluster. Now:
- meta-details lives in col 1 only so the To/Cc chips shrink
together with the From chip when the pane narrows
- action cluster spans rows 1 and 2 so its width is set by the
widest col-1 content; a long To/Cc list triggers the wrap to a
2-row icon stack just like a long From sender does
Meta switched to CSS grid in undocked mode:
- row 1, col 1: From row (label + chip + chevron)
- row 1, col 2: action cluster
- row 2, span: To/Cc details
The cluster shrinks alongside the chip and flex-wraps into a 2-row
icon stack before crowding the chip. At very narrow pane widths
(< 380px via @container docpane) it snaps back to absolute overlay
so From: still fits.
Docked mode overrides meta back to flex column so the cluster
flows naturally last — under From, and under To/Cc when expanded.
Was rendering as a transparent ghost — From chip / sender text bled
through the gaps between icons. Added a left-fading gradient
backed by var(--bg) so the cluster reads as an opaque overlay
while chips poking out from underneath blend smoothly into its
left edge.
- Window-level recipient-chip click handler now bails if the chip
is inside .email-reader-meta — the per-reader handler still
toggles the expanded-address view on click.
- The from-sender (magnifying glass) search button SVG is now
tinted with var(--accent-primary) so it stands out as a deliberate
search action against the neutral Reply / Forward / etc icons.
Moved the action cluster out of the From row to a sibling of meta
inside .email-reader-meta. Undocked: cluster is absolute-positioned
top-right of the header so it overlays the From line as before.
Docked: cluster is in-flow at the bottom of the meta column, so it
sits below the From row when collapsed and below the To/Cc rows
when the user expands the recipient details via the chevron.
Cluster is now in-flow with margin-left:auto and flex-wrap:wrap so
when the chip text grows long enough to crowd it, the buttons split
to a second row of icons before they have to cover the chip. The
absolute-overlay behavior kicks back in at very narrow pane widths
(<380px via @container docpane) so From: still fits on one row when
the pane is truly cramped.
Pulled the From row's negative margin from -8 to -4 so the whole
row (From: label AND chip) sits 4px lower together. Action cluster
below now justifies flex-end so the icons sit at the right edge
of the row instead of left-aligned.
Sometimes the user lands in chat mode without realizing — surface the
mode the message went out on as a small uppercase pill right after the
timestamp in the role header.
- roleTimestamp(when, mode) gains an optional mode arg. Agent renders
in accent; Chat renders in muted/neutral. Other values render
nothing (back-compat for older history without the field).
- The three roleTimestamp call sites pass metadata?.mode through.
- chat.js writes mode into the user-message metadata at send time and
into the assistant metadata when the active-stream render lands,
reading toggleState.mode so research/agent overrides upstream still
flow through correctly.
Historical messages from before this change just don't show the pill —
graceful fallback, no migration needed.
When the modal is docked there's no room to overlay the actions on
the From line. Now:
- From row gets flex-wrap so the action cluster drops to its own
row below the From label + chevron
- Action cluster goes position:static, flex-basis:100%, no gradient
fade, no padding-left, left-aligned
- Whole From row pulled up 8px to claim back vertical space
- Header min-height drops back to 0 since buttons no longer
overlap
Also bumped the gap from From to To/Cc details by 2px (-8 → -6).
Bumped header min-height to 60px and padding-top to 8px so the
44px-tall action buttons (absolutely positioned inside the From
row) have room without overflowing the header. From row gets
min-height:44px on desktop so the buttons fit cleanly inside it.
Dropped the now-redundant negative margin nudges on the From row
and the strong label.
Pinned .email-reader-actions-inline to absolute top:0 right:0 of the
From row with a gradient fade. When the window narrows the cluster
stays on the From line and the recipient-chips span scrolls under
it, so users can swipe/drag to reveal recipients tucked behind the
buttons instead of seeing From: jump above the action row.
- Desktop (>= 769px): From row gets margin-top -4px so the whole
From + action cluster sits 4px higher in the header.
- Mobile @media block untouched.
- To/Cc gap bumped 4px → 6px for slight breathing room.
Strong labels reserve min-width:36px so the chips after each label
start at the same x — From, To, Cc all line up. Killed the
docked/docpane grid-stack overrides that were splitting label and
chips onto separate rows, since chips already scroll horizontally
inside each row when there are too many.
- Docked: From row + action cluster nudged up 4px
- Chevron pulled 4px left so it sits tight to the From chip
- To/Cc detail block pulled up 8px to hug the From row
- 4px gap between To and Cc rows (was 2px)
Found the culprit — the docked-modal CSS forced .email-reader-meta-row
into a single-column grid, which collapsed the From row into a
vertical stack and pushed the action buttons below it.
Fix:
- Merged the primary + secondary action rows into one flat
.email-reader-actions-inline cluster inside the From row
- Made the cluster flex-wrap so it stays inline when undocked and
wraps below the chip when truly cramped (docked, narrow tab)
- Excluded .email-reader-meta-from from the docked-modal and
narrow-docpane grid-stack rules — those overrides now only
apply to the To/Cc detail rows
Restructured the DOM so the Reply / Reply-all / Forward row lives
INSIDE the email-reader-meta-from div (after the chips span), and
the Summary / AI / More row sits directly below as a sibling of
From inside the meta. Killed the outer email-reader-actions
wrapper that kept letting the buttons drift out of position.
CSS now pushes the primary row right via margin-left:auto on the
From row and right-aligns the secondary row below it.
Reorganized the action cluster into two visible rows so each fits
the available width:
- Top row (on the From line): Reply / Reply-all / Forward
- Bottom row (under it): Summary / AI reply / More
Action cluster goes back to flex-direction:column, the row
wrappers are flex rows again (no more display:contents flatten).
align-items: flex-start on the header keeps the action cluster
locked to the From line when the user expands the To/Cc details
— previously it drifted to vertical center as the meta grew taller.
After the toolbar reshuffle the action block is now two stacked rows
(Summary/More above Reply/Forward/AI), making it taller than the meta
block. The mobile header rule was align-items:center, which then pulled
the From:/To: rows down into the vertical middle of the header — the
'From: is in the middle' symptom. Switch to flex-start so meta sticks
to the top edge where the user expects it.
With the meta collapsed to a single visible From row + chevron,
there is room to put the action cluster on that same row as a
right-aligned sibling. Dropped the absolute positioning and
gradient-fade overlap — actions now flex-end via margin-left:auto
so From sits on the left and Reply / Reply-all / Forward / AI /
Summary / More all sit on the right of the same row.
Also moved the chevron inside the recipient-chips span so it sits
adjacent to the sender chip instead of wrapping onto a second line.
Only the From row shows by default. When the email has To and/or
Cc recipients, a small chevron sits next to the From chip — click
it to inline-expand the To/Cc rows below (rotates 180deg open).
Trims the header to a single visible row in the common case,
leaving the action cluster plenty of vertical headroom to stay
on a single row.
Reverted the single-row meta strip — misread the user's ask. Each
meta field gets its own row (From / To / Cc stacked), label sits
tight to the chips on the same line, recipient chips inside the
row still scroll horizontally so long lists slide under the
floating action cluster.
Three stacked meta rows wasted vertical space — From, To, Cc now
share one horizontal strip with each label tight to its chips. The
strip itself scrolls horizontally so the action cluster (still
floating top-right) can cover the right edge and the user can drag
to reveal recipients hidden underneath.
This also gives the actions a single shared row, since the meta
no longer dictates a multi-row header height.
The primary/secondary row wrappers were still creating nested flex
containers — even with parent flex-direction:row the two row divs
sized to content and could stack visually. Switching the wrappers
to display:contents collapses them entirely so all 6 buttons
become direct flex children of .email-reader-actions and lay out
on a single row guaranteed.
Reply / Reply all / Forward / AI / Summary / More now flow inline
on one row instead of being split into a primary (Summary+More) and
secondary (Reply group) stack. Mobile + docked overrides also
flipped from column to row.
From/To/Cc back on the left, action cluster (Reply / Reply-all /
Forward / AI / Summary / More) absolute-positioned top-right with a
gradient fade so chips that overflow slide cleanly underneath. The
recipient-chips lists no longer wrap — they scroll horizontally,
matching the account-chip strip pattern, so users can drag/swipe
to reveal recipients hidden under the action cluster.
Mobile (@media max-width:768px) gets the same row+absolute layout
instead of the previous column with actions on top. The narrow
container query (docpane max-width:460px) still falls back to
in-flow column so it doesn't overlap on very narrow panes.
Was: from/to/cc/date meta on the left, action cluster (Reply / Reply
all / Forward / AI reply / Summary / More) pinned to the right of
the header. Now: actions stretch across the top in their two existing
sub-rows, the from/to/cc meta sits below.
Pure CSS — no template restructure. The .email-reader-header flexbox
flips to flex-direction:column, .email-reader-actions gets order:-1
to render first, and the existing flex-end aligned action-row rules
swap to flex-start so buttons read left-to-right across the top
toolbar. Mobile media query overrides bend the same way so the
layout is consistent across breakpoints.
Restructure the action cluster so it stays as two visible rows inside
.email-reader-actions instead of flattening via display:contents:
- Top row: Summary, More
- Bottom row: Reply, Reply all (conditional), Forward, AI reply
Dropped the Search button — wasn't part of the requested layout.
CSS: .email-reader-actions becomes flex column with both rows
right-aligned; .email-reader-actions-row becomes a real flex row
(no more display:contents flattening) so each row stays on its own
line. Whole block continues to sit beside the From/To meta inside
.email-reader-header.
- .email-reader-actions flex-wrap nowrap → wrap so when the cluster
exceeds the room next to a tall multi-recipient meta block, the
buttons wrap within the actions area instead of pushing the whole
block onto its own row below From/To.
- New rule: .email-reader-more-wrap gets order:99 so the More kebab
sits at the far right of the flattened flex row instead of in the
middle (its source order put it ahead of the secondary row's AI
Reply / Summary buttons after display:contents flattening).
The All/Unread/Favorites/etc selector was a native <select>, which
can't render SVG inside <option>. Replace it with a custom picker
that:
- Keeps the existing <select id="email-lib-filter"> as the value
store (hidden via display:none). All existing 'change' listeners
keep working — the picker just dispatches a change event after
updating the select's value.
- Renders a styled button + drop-out menu built from the select's
options (preserves optgroup labels like 'Tags').
- Each option carries an SVG icon: lines for All, ringed dot for
Unread, star for Favorites, empty checkbox for Undone, bell for
Reminders, reply arrow for Unanswered/Reply-soon, clock for
Pending, calendar-x for Stale, exclamation-triangle for Urgent,
ban for Spam, newsletter and megaphone for the marketing tags.
- Icons use var(--accent) so they pick up the user's theme color.
- Click outside / Esc closes the menu (Esc handler is capture-phase
+ stopPropagation so it doesn't bubble to the modal-close listener
and shut the whole email window).
CSS scoped under .email-filter-picker.
Old rule fixed the loading wrap at min-height:180px so the spinner
landed near the top of the email-list section. Switch to
position:absolute inset:0 over the grid (with #email-lib-grid set to
position:relative) so the whirlpool + 'Loading emails' label center
within the entire visible email area regardless of section height.
- 'Marking done' / 'Marking read' / 'Marking unread' label was 2px low
vs. the whirlpool spinner inside the Actions button. The existing
loading-label CSS only scoped to #email-lib-bulk-delete; extend it
to also cover #email-lib-bulk-actions and bump top from 0 to -2px.
- 'All' checkbox label was inline-styled top:2px so the box + text sat
lower than the surrounding bulk-action items. Reset to top:0 to
match memory + skills select-all rows.