Replacing the static screenshots in the large “Study tools” cell with tiny looping illustrations — themeable, responsive, no baked-in macOS chrome. Same playbook as the science cell: prototype idea + motion + copy here, then decide native-SwiftUI vs webview and build for real. Doing the tools one by one. The four below are shipped in-app; three remain to design (Video clips · Send to Miro · Ingest) and no screenshots long-term — a baked PNG is not an option for any of them.
The cell previews below now carry the shipped copy and CTA labels verbatim (they used to show the mock's proposals, which lost). Two motion differences are deliberate: standalone loops here vs the WelcomeBaton's one-animation-at-a-time rhythm in-app — the baton owns the tempo, so a cell plays once per turn then holds rather than looping. Enum is WelcomeIllustration (renamed from ScienceIllustration, 2 Aug 2026); each case is rendered by illustrationView in WelcomeHomeView.swift. Tempo (14 Aug 2026): the Turn column below is the authored base — the shipped turn is base × WelcomeTempo stretch (group 60% speed × a local pace; the tool webviews carry ×1.3) plus a 3 s rest on the opening frame and 3 s on the finished frame, so real turns run ~2–2.5× the base. This page's standalone loops still play at authored speed.
| Slot (title in-app) | Case | View | Height cap | Turn | In-app play |
|---|---|---|---|---|---|
| AutoCode | .autocode | AutoCodeIllustrationView | 160 | 12 s | once per turn (random quote), then hold |
| Codebooks | .manualTags | ManualTagsIllustrationView | 176 | 13 s | one group per turn, alternating across turns |
| Tag | .tag | TagIllustrationView | 160 | 12 s | once per turn, then hold |
| Star & hide | .starHide | StarHideIllustrationView | 190 | 14 s | once per turn (two beats), then hold |
| Connect an AI agent | .agentChat | AgentChatIllustrationView | 160 | 13 s | once per turn, then hold — no mock section (built in-app; bespoke Claude Code chrome, no report CSS to sync). Full-width normal flow since 14 Aug 2026 (was centre-scaled) |
| Video clips | .clips | ClipsIllustrationView | 100 | 14 s | loops while holding the baton (Export-menu row clicked → clips land, filenames unit by unit → hold) — native SwiftUI, no mock section |
| Send to Miro | .miro | MiroIllustrationView | 148 | 9 s | once per turn (stickies pop in order), then hold — no mock section (sticky text + hand-set wrapping verbatim from a board capture) |
| Ingest | .ingest | IngestIllustrationView | 196 | 12 s | loops while holding the baton (icons blink in, names type out) — native SwiftUI, no mock section |
SlotItem.primaryDestination (a plain tag, not a closure — SettingsWindow is @MainActor), rendered by SlotRotator.slotView.illustrationNaturalHeight is passed as maxHeight and every illustration self-scales below it when the golden cell is short — geometry stays fixed, content bends. The min-height:172 on this page's .cellframe is a mock convenience and is not the shipped cap.reduce = reduceMotion || !active folded into the webview's .id(), so losing the baton reloads to the still (a brief fade — the known v1 tradeoff of duration-declared handoff).Copy: “Let AutoCode propose tags across every quote — you Accept or Deny.” · link AI helps tag →. Sequence: the quote types out (LLM/fast-human) → the Satisfaction sentiment + a proposed code flash in → the [✗ ✓] action pill is offered → accept is clicked, the code goes solid (accept-flash). Tag + card CSS are the shipped classes; the final frame is a real blockquote.quote-card.
p1 · Participant and the Satisfaction sentiment settle first — AutoCode’s contribution is the code, so it flashes in proposed (dashed, pulsing, real .badge-proposed), offers the real [✗ ✓] pill, and on accept solidifies with the real badge-accept-flash.QUOTES.WelcomeBaton owns the tempo, so it plays once per turn then holds and the burst loop is unused. Keep the burst here — it's what makes the standalone page legible.+ pulse when the code lands? Show the deny path occasionally, or always accept? Stream both cards stacked in the big cell vs cycle one?The human counterpart to AutoCode. Two groups build by hand — title + description + codes typed via the real + → type → commit flow — one at a time, then slide away. A/B homepage trial = a designed experiment; Switching costs = a commercial dive from the brief (the participant uses a rival). Auto-coding can’t originate either: it wasn’t in the room when the research purpose was set. Group colours are the real codebook OKLCH sets (ux 250 / opp 75).
+ → type → commit → chip (real tag-input + .badge-user), in the group’s real codebook colour.The most basic move in Bristlenose. Sequence: the pointer moves in and clicks to select (the real .bn-selected blue tint + bar) → a raised keycap (t) rises and presses → a code types itself in character-by-character (same typeInto grammar as the Codebooks cell) and commits to a real .badge-user chip. Keycap = skin B “Raised” from design-keycaps.md (faked — the primitive is unbuilt).
handleQuoteTextClick), so the pointer lands on the card’s left rule, where a plain click focuses + single-selects together (Finder-style, handleCardClick). Colours are the shipped tokens: --bn-selection-bg #eef4fc + a full accent-blue --bn-selection-border (#007aff) + the --bn-focus-shadow lift.Two keys, one idea: curate. The pointer focuses a quote → s stars it (the star + left rule pick up the starred tint, body weight bumps) → then focuses another → h hides it (the real .bn-hiding collapse) and the hidden count ticks up by one. Star, hide, and hidden-count CSS copied verbatim from atoms/toggle.css, quote-actions.css, hidden-quotes.css.
--bn-colour-starred is #999 / #ccc — a tint differential (star + left rule + weight), not a gold fill. The old roadmap line “fills gold” was wrong about the code. The open question (match the product, or make the illustration louder than it?) resolved in favour of matching: the in-app cell ships the real differential plus a star-pop, so the illustration never promises a state the product doesn't have..bn-hiding animates max-height/opacity/margins to 0 (300ms), then .bn-hidden (display:none); the sibling slides up. The N hidden chip bumps +1 — the quote isn’t gone, it’s parked.handleCardClick); clicking the words would open trim/edit instead. s/h then act on that quote; the hide button only appears on focus (real behaviour).190 height cap (toolbar + two cards). The two-up won — the sequenced fallback (star card A, then card B slides in to be hidden) was not needed and is not built.The cell is a SQUARE that scales with the viewport — that's the constraint everything here has to survive, so resize the window and watch these two panels. Arithmetic from WelcomeHomeView.swift: the spiral is framed width: w, height: w / 1.618, and GoldenSplit(.horizontal) hands the major cell width: (w − 8) × 0.618; its height is the spiral's full height, 0.618w. Both ≈ 0.618w → square, growing linearly with width. (These side-by-side panels are each ~half the page, so slightly smaller than the real cell, which is ~62% of the whole content width — the proportions are faithful, the absolute size is conservative.)
The proposed rule. The illustration shares the cell's content edge — 16pt left, 16pt right (the real welcomeCell(large:) padding, the edge STUDY TOOLS already sits on) — runs the full width, stays leading so that when it hits a max width the slack opens on the right, and grows downwards to fill the depth it's given. The CTA then lands on the bottom instead of floating mid-cell. The φ-geometry does not move — the cell is the same fixed square either way; only the content inside it bends, which is the Parthenon rule working rather than being broken. The fake tool below (Trace a theme) is a fixture, not a roadmap item — it exists to prove the mechanism, and it fills its box by measuring it.
scrollHeight > clientHeight), re-running on resize — so the same illustration shows 2 cards in a short cell and 4 in a tall one. Contrast today's illustrations, which are fit-and-centre: the four flow webviews are body{display:flex; align-items:center} and the one still-scaled one (Signals) clamps at Math.min(0.9, …), so handing either a taller box just adds air, never content. Relaxing the frame alone changes nothing on screen — each illustration has to be rebuilt to stretch. That's the content-design half.Learn → link, and the fix scoped the tap to the disk band alone, bottom-aligned, commented “hit area = disk band only (dots row level, clear of the link above)”. Stopping the CTA above the nav row keeps exactly that clearance, still reads as “on the bottom”, and needs no change to the chevrons. The red marker stays in this fixture as the reason the CTA stops where it does.Each replaced one screenshot — the last three (Video clips, Send to Miro, Ingest) on 14 Aug 2026, built straight in-app like the agent cell. We locked a shared visual grammar on AutoCode and held the rest to it. Build target was per tool — themed SwiftUI or webview, both used. A screenshot was never the target.
How it plugs in. Each animation replaced the image: screenshot inside its studyTools slot (WelcomeHomeView.swift), rendered by the SlotRotator one item at a time; with the last three drawn (14 Aug 2026) the image: mechanism itself was deleted from SlotItem/slotView. Freezes to a legible still under prefers-reduced-motion. Build target fell per tool, as predicted: native SwiftUI where the system's own glyphs and type are the material (Ingest, Video clips — SF Symbols + SF Pro), webview where bespoke chrome or exact foreign type is (agent terminal, Miro stickies).