# MOTION ADDENDUM — DESIGN ROUND 3
**Additions to the R2 `MOTION-SPEC.md` (which stands in full) · what Flutter/Flame must render · July 7, 2026**

The R2 rule is unchanged: **motion is weather, not reward.** Everything below is interruptible, has a static fallback, and uses the R2 timing tokens (`settle 180ms · overlay 250ms · object 350ms · flip 500ms · drift 800–1400ms · hold 1000ms · seal 600ms`) plus two new ones: **`voice 120ms`** (barge-in halt) and **`crossing 1400ms`** (portal entry/exit).

---

## 1. Voice presence

- **Session open:** the composer morphs into the voice band — field collapses, waveform strip fades in (250ms `easeOutQuart`). The ground warms +3% brightness for the whole session (a presence, not an event; low-pass, no pulse). Session close reverses; nothing else moves.
- **Listening waveform:** camel, 24px tall, amplitude from the mic, 60fps but ≤2% GPU (a 32-bar strip, not FFT art). Silence = a resting hairline, never a dead flat "off" look.
- **The forming words — in the band, not the Thread:** interim transcript renders italic at `--fg-soft` inside the voice band itself, small and peripheral; word-groups refine in place as recognition firms up. Nothing is written into the Thread while the utterance is in flight — the fold means the Thread has no forming state to show. **On utterance close,** the settled text spawns directly as a new member bubble in the Thread with the standard R2 claim-settle (8px rise + fade, 180ms); it appears already resolved, it does not slide up from the band. Corrections from the recognizer re-render the band's live text in place; **never** re-layout something already in the Thread, because nothing is in the Thread until it's finished.
- **Ally speaking:** her text streams word-grouped in sync with audio (server-timed cues, same mechanism as room strikes). Beside her name, the ember breathes at speech cadence — brightness only, no scale. No mouth, no orb, no ring.
- **Barge-in:** on member voice onset, ally audio ducks to zero in **120ms** and her stream halts; the unspoken remainder collapses to a dim `—` (180ms fade). No error state, no "interrupted" copy. If she resumes, she resumes as a new bubble.
- **Channel switch:** keyboard rise is the platform's own; the voice band docks above it (250ms). The half-spoken sentence appears in the composer already selected-at-end, caret ready. Flame renders nothing here — this one is pure Flutter.

## 2. Fuller UI open / dismiss (the sheet grammar)

- **Open:** the door card lifts 4px (150ms), then the sheet rises from it, `object 350ms easeOutQuart`; the Thread dims to 0.85 and scales 0.97 (the R2 deck/lens values — one grammar, everywhere).
- **The return seam:** the quoted sentence-you-left sits in the sheet header at `--fg-dim`; it does not animate, ever. It is furniture.
- **Dismiss:** swipe down or tap the seam; sheet recedes `overlay 250ms easeInCubic`; Thread returns to 1.0.
- **The return mark:** the message you left gains a camel hairline left-edge (opacity 0 → 0.6 → 0, over 2s). Anything done inside lands beneath it as a receipt/artifact line with the standard claim-settle. **Flame renders nothing; this is Flutter.** Static fallback: hairline at 0.6 for one frame, then gone.

## 3. The treasury send (Flame, explanatory motion)

The flagship of "the engine's everyday job is explanatory motion":

- One gold token (a filled 8px disc with a 2-frame trail) detaches from the source shelf's balance figure, arcs (single quadratic, apex ~40px above the midline), and lands on the recipient line; the landing is a soft settle (scale 1.15 → 1.0, 150ms), no bounce.
- **Ledger honesty rule:** the source figure decrements the moment the token *detaches*; the destination increments the moment it *lands*. During flight the money is visibly in neither place — in-flight is a real state and the animation is its UI. Flight time 600ms fixed regardless of amount.
- Amount rides the token as a small chip (`40 $FAN`) only when >1 significant token concept is on screen; otherwise the sentence already said it.
- Failure (rail rejects): the token returns along its own arc at 2× speed and the figure restores — the same grammar as R2's abandoned signature. No red, no shake; the ally says why.
- Budget: part of the existing ≤1 shader pass + particles envelope. Static fallback: both figures update instantly, no token.

## 4. Portal entry / exit (Flame, constitutive)

- **Entry ("the crossing"):** triggered by a **tap** on the portal card (not a hold — the signature gesture is reserved for acts with consequence, and entering has none by design) — `crossing 1400ms easeInOutCubic`, one continuous shot, no cut, starting the instant the tap registers: the Thread recedes downward into the ground (scale → 0.92, brightness → 0.6); the ground's breathing amplitude rises; the isometric world **rises out of the ground plane** as a diorama (tiles lift 24→0px with 30ms stagger, characters fade in last). The crossing itself is the deliberation window — see Exit below for the abort path. The thread never unmounts — it is *beneath* the world, and pulling down from the top edge peeks it (the Contract gesture still works inside training).
- **Inside:** training grounds breathe on a **~20s cycle** (vs 60s real) — the secondary "slightly too alive" tell. Actor routine paths draw as dotted lines only while an actor is observed (tap); otherwise paths are implied by movement.
- **The narrative tell, before the material one:** the first sim wallet a member touches is handed to them in the fiction, by name, before this render layer is even relevant — an actor's line (`Harl's crew pays in foundry scrip`) gives the practice economy a name and a giver. The hollow-gold rule below is the confirmation, not the only signal.
- **Hollow gold rendering:** in any sim-flagged scene the gold material renders **stroke-only** (2px, `--kid-sun-gold`, no fill, no glow shadow). This is a material swap at the render layer, not a per-widget style — Flame must expose `goldMaterial: filled | hollow` as a scene-level uniform so no training screen can accidentally fill it. Practice signatures stamp a hollow seal (same 600ms seal timing, outline only, **zero ember particles** — embers are real-world only) — this is the one place inside training that still uses press-and-hold, because teaching the gesture is the exercise.
- **Real-channel moments inside training** (the Telegram exercise): the outbound message panel renders with the *real* materials — filled sky send, real avatar — and its send plays the standard R2 code-travel motion. The contrast is the design; do not tint or dampen it.
- **Exit:** the crossing reversed (1400ms); what happened prints into the Thread as claim-settles with a `in Foundry Row` provenance chip. **Aborting mid-entry** is a second tap anywhere on screen while the crossing plays (there is no hold to release) — it rolls back at 2× and returns you to the exact sentence you tapped the portal from, the R2 signature-abandon rule adapted to a tap trigger.

## 5. The grant gesture

- The person's marker drags along the four-rung ladder with direct manipulation (finger-attached, no snap until release; magnetic within 12px of a rung).
- **Widening** (toward secret): on release the marker sits at the rung *unlit*; press-and-hold (standard 1000ms radial gold) commits — this is a signature. **Narrowing** (toward public): commits on release instantly, marker settles 180ms. Asymmetry is the point: retracting trust must always be lighter than extending it.
- The **personal rung** renders as a sealed ring; a drag toward it hard-stops 16px short with a 40ms resistance curve (no rubber-band past it — it is a wall, not a spring). No copy, no toast.
- The consequence sentence under the ladder re-renders on every rung change with a fade-swap (150ms, the Vigil's working-set swap).
- Commit prints one record line in the thread on return (per the sheet grammar, §2).

## 6. Money handoff (the cream sheet)

- The handoff card's button opens the web surface as a sheet in **cream material** (`--kin-cream-white` ground, espresso type): rises `object 350ms`; the Thread beneath dims to 0.85 as usual. The material change is the message; no "leaving the app" interstitial, no browser chrome pantomime.
- While the web side works, the seam shows the ally's holding line (`waiting on the web…` in `--fg-dim`) — the one legal "waiting" indicator, because the member chose to wait here.
- **Return:** sheet recedes; the receipt line lands with claim-settle + a single gold seal dot (filled — this is real money) + the camel return-hairline. If the member dismissed early, the receipt line lands whenever the rail completes, marked as arriving late; no re-summon of the sheet.
- **The return contract (interim — specify now, ahead of R4 committing the web surface's visual design).** The web side hands the app exactly what it needs to render the receipt line and nothing it has to guess at: `{ act: "sent" | "received" | "authorized" | "declined", amount, currency, counterparty, rail, timestamp, record_id, status: "settled" | "pending" | "failed" }`. The app never re-derives a number from the web side's UI — it renders only from this payload, cites it by `record_id`, and that citation is what makes the receipt line answerable later from the record (§8 of `UX-SPEC-R3.md`). If the web surface's design changes in R4, this contract is what stays fixed.

## 7. Budget & fallbacks, restated

Everything above fits the R2 envelope: ≤1 shader pass + particles, idle <5% GPU mid-Android. New static fallbacks: voice band renders final transcript only; token-send updates figures instantly; portal crossing becomes a 300ms crossfade; grant ladder loses magnetism, keeps rungs. Reduced-motion renders every final frame; nothing above carries meaning that its final frame doesn't.
