The whole interview, participants only: every participant turn, in its scene colour, on the timeline's own track colour. The gaps are where the moderator spoke.
4.5 px tall, a quarter of the open timeline's participant clips (18 px). Track corner 1.5 px, clip corner min(1 px, half the clip), clips inset 0.5 px each side, as the open timeline insets them. On a 2× screen that is 9 device pixels and crisp; on a 1× screen the half pixel blurs the bottom edge slightly, so the product may want 4 or 5 px there.
Clips at 60% (NEW --bn-tp-preview-opacity) over the full-strength track, so a column of strips reads as quiet texture rather than a second chart. The morph lifts them to full strength in its first 200 ms.
Keyline 0.5 px at the shipped ink (rgb(0 0 0 / 0.25)), down from the open timeline's 0.75 px. On a 2× screen it is one device pixel, the thinnest line the display can draw. On a 1× screen nothing thinner than a pixel exists, so the browser draws it as a faint full pixel.
No text and no tooltip. The strip starts where the open timeline's tracks start, 82 px from the grid's edge (the lane-label gutter), and ends 14 px from the right edge, so on open it lines up with the participant lane it becomes.
It sits inside the row, so the row's hover wash covers it. A click on the strip opens the timeline; a click anywhere else on the row still goes to the transcript (commit 729033bb). In the product the strip carries data-own-click.
Scale, closed and open
Closed, each row's strip spans the full width (your spec), so an 18-minute session and a 37-minute one are the same length. Open, the timeline uses the shipped shared scale (fitScale: the shortest session fills the width, 1.0 s/px at this width), so on open #1 keeps its length and #2 and #3 stretch to about twice the width and scroll. A session shorter than the one that sets the scale would shrink on open; Talismanic has none. One scale above shows the alternative: every strip at the longest session's scale, so the strips also show relative length. That costs width on short sessions, and it is a cheap switch to flip later.
Several participants: merged
When a session has more than one participant, the strip draws the union of their turns as one lane, each turn in its own scene colour. Overlaps draw the later turn on top. The open timeline keeps its shipped lanes. Illustration only, made from #1's turns split between two invented participants:
Hover: a tint, nothing else
The track takes 10% of the text ink (NEW --bn-tp-preview-track-hover, the same move as the timeline popover's 4% hover), and the pointer becomes a hand. Decided 17 Oct 2026: a playhead on a strip too small to read is too much, and a strip that grows under the pointer is web-page manners, not Mac ones.
The open animation
The strip becomes the participant lane. The participant clips spring from the strip's position and size (4.5 px, closed scale, 60%) to the lane's (18 px, open scale, full strength) in 320 ms on a spring curve (cubic-bezier(0.34, 1.56, 0.64, 1), about 8% overshoot). The strip itself fades as the clips leave it.
The drawer opens in 220 ms (the shipped grid-rows fold, ease-out).
Everything else lands in a left-to-right domino: each mark's delay is 60 ms plus its x across the visible width × 150 ms, so the whole wave crosses in about 0.15 s and every mark lands within about 0.45 s of the click. The participant names are not carried by the morph (they would squash with it); they fade in along the lane once it has landed, the last by 0.75 s. Section flags drop 7 px, moderator clips grow out from their left edge, sentiment bars grow from the axis (up for positive, down for negative), surprise dots pop, theme chips rise 5 px, and lane labels and ticks fade.
The journey's words become the flags' labels. The grid chain is each user's first arrivals, in the report's section order (as it ships, server/journey.py): the simple version, for comparing across the grid, each word a jump to that first moment. The open timeline flags every visit, the messy real path, as the transcript does. So each word flies to its first-visit flag, matched by name (taking the flag's size and ink, 380 ms, on that flag's domino beat), and a return visit's flag, which has no word to catch, fades in 120 ms after its neighbours. The chain and the flags are ordered differently by design, so words cross where a user's path left the report's order. In #2, Ravi goes Shopping Bag → Cross-sell Modal → back to Shopping Bag, while the chain reads Cross-sell Modal → Shopping Bag: the two words cross, Shopping Bag lands on the first Shopping Bag flag, and the second fades in. The arrows fade where they stand; the chain keeps its place (empty) while the timeline is open, so nothing reflows under the flight. Closing flies the words home. A flag past the visible width takes its word off the right edge (#2's sections start 18 minutes in, so at this zoom its whole flight leaves to the right; #1 shows it best).
Built 17 Oct 2026 (80d5c745, 0645c6f4): the strip, the motion, and a timeline flag per visit (section_visits in routes/tapestry.py). The chain stays as it is. The live record is experiments/session-tapestry/README.md § The strip and the open motion.
Closing is quiet: the words fly home, the content fades in 100 ms, the drawer folds and the strip comes back. Reduced motion makes all of it instant. Slow ×5 above is for inspecting it.
Marks past the visible width carry the last delay; they are off-screen until the reader scrolls.
What is new, and what is reused
Reused
Every grid and timeline style (inlined above), the timeline's track colour, scene colours, clip insets and corners, the accent playhead, the grid-rows fold, the shared scale.
New
The strip (4.5 px, clips at 60%, 0.5 px keyline), --bn-tp-preview-track-hover, the morph, the domino keyframes and the spring curve. No new strings: the strip has no text. As built, the strip is hidden from assistive technology and out of the tab order: the existing disclosure ("Timeline for session N") is the keyboard and assistive route to the same timeline.
Open questions
Should a click on the strip also aim the open timeline at the moment clicked (scrolled into view, playhead there)? It would make the strip a seek control as well as a disclosure. The mockup does not do it.
Keep the chevron? It stays here as the keyboard route and the shipped disclosure. With the strip as well, two controls open the same thing.
Commentary
What the strip is
--bn-tp-preview-opacity) over the full-strength track, so a column of strips reads as quiet texture rather than a second chart. The morph lifts them to full strength in its first 200 ms.rgb(0 0 0 / 0.25)), down from the open timeline's 0.75 px. On a 2× screen it is one device pixel, the thinnest line the display can draw. On a 1× screen nothing thinner than a pixel exists, so the browser draws it as a faint full pixel.729033bb). In the product the strip carriesdata-own-click.Scale, closed and open
Closed, each row's strip spans the full width (your spec), so an 18-minute session and a 37-minute one are the same length. Open, the timeline uses the shipped shared scale (
fitScale: the shortest session fills the width, 1.0 s/px at this width), so on open #1 keeps its length and #2 and #3 stretch to about twice the width and scroll. A session shorter than the one that sets the scale would shrink on open; Talismanic has none. One scale above shows the alternative: every strip at the longest session's scale, so the strips also show relative length. That costs width on short sessions, and it is a cheap switch to flip later.Several participants: merged
When a session has more than one participant, the strip draws the union of their turns as one lane, each turn in its own scene colour. Overlaps draw the later turn on top. The open timeline keeps its shipped lanes. Illustration only, made from #1's turns split between two invented participants:
Hover: a tint, nothing else
The track takes 10% of the text ink (NEW
--bn-tp-preview-track-hover, the same move as the timeline popover's 4% hover), and the pointer becomes a hand. Decided 17 Oct 2026: a playhead on a strip too small to read is too much, and a strip that grows under the pointer is web-page manners, not Mac ones.The open animation
cubic-bezier(0.34, 1.56, 0.64, 1), about 8% overshoot). The strip itself fades as the clips leave it.server/journey.py): the simple version, for comparing across the grid, each word a jump to that first moment. The open timeline flags every visit, the messy real path, as the transcript does. So each word flies to its first-visit flag, matched by name (taking the flag's size and ink, 380 ms, on that flag's domino beat), and a return visit's flag, which has no word to catch, fades in 120 ms after its neighbours. The chain and the flags are ordered differently by design, so words cross where a user's path left the report's order. In #2, Ravi goes Shopping Bag → Cross-sell Modal → back to Shopping Bag, while the chain reads Cross-sell Modal → Shopping Bag: the two words cross, Shopping Bag lands on the first Shopping Bag flag, and the second fades in. The arrows fade where they stand; the chain keeps its place (empty) while the timeline is open, so nothing reflows under the flight. Closing flies the words home. A flag past the visible width takes its word off the right edge (#2's sections start 18 minutes in, so at this zoom its whole flight leaves to the right; #1 shows it best).80d5c745,0645c6f4): the strip, the motion, and a timeline flag per visit (section_visitsinroutes/tapestry.py). The chain stays as it is. The live record isexperiments/session-tapestry/README.md§ The strip and the open motion.What is new, and what is reused
--bn-tp-preview-track-hover, the morph, the domino keyframes and the spring curve. No new strings: the strip has no text. As built, the strip is hidden from assistive technology and out of the tab order: the existing disclosure ("Timeline for session N") is the keyboard and assistive route to the same timeline.Open questions