The app-premise embodied. She is drawn only in the design tokens on the tokens page,
ships static, and appears on a closed list of surfaces — possession of an asset is not permission to deploy it.
Her canon is the accepted-image corpus plus its prompt manifests in product/mascot/assets/mai/; the ruling ledger is
DESIGN.md §5.4c and it wins any disagreement with this page. Read it →
The copy still speaks as MaiSay, not as Mai — "MaiSay's voice", MaiSay Pro. She is a face the app wears on her surfaces, never a narrator, and never the grader. An open question is with the product team: the pitch to open the lesson-loop surfaces →
Chibi glasses girl: big round glasses, warm smile, long ink-black hair, lilac vest, white collar, peach skin — flat geometric register, solid fills, no gradients. Every pixel of her is an existing token; she never introduces a colour.
Scene backgrounds must contrast her wardrobe, solved within the surface's own pastel. Her lilac vest dissolved into a lilac panel on the door's first pass; the fix was rose — the door is a speaking claim — never a pastel reassigned for contrast's sake.
A surface that has earned her may be redesigned around her — she is layout, not a sticker — but the complete figure stays inside its semantic container, respects the content grid, and never straddles two regions.
Every gated still in the corpus, each regenerable from its prompt manifest. The note says what it is for; where it says marketing-only the asset exists but no v1 screen may carry it — possession is not permission, and the closed list below is the permission. The register table in the mascot README rules what may be drawn next: raised-arms is marketing-only, and the thumbs-up is the in-app celebration gesture of record.
The idle every other asset is generated from. Vector; usable as the idle on any approved surface.

Hand cupped behind the ear, head tilted. The hearing pose — also the say-from-memory gloss band.

Thumbs-up — the in-app celebration gesture of record. Celebrates the completed fact, never the limit.

Genuine zero-result only, never a failure. An error surface carries no character at all.

One warm wave serves all four time states — the hour is carried by the copy, not by a second drawing.

Cross-legged over an open book. The quiet rooms, and the shelf head where she reads. Floor line at y995.

The studying seat redrawn with a pencil — same floor, same register, different verb. Left page ruled, right page blank.

The desk, deep focus. The home-path header's daytime pose at 132px.

Headphones on, eyes drawn closed. This is the headphones pose — mai-listening is the ear-cup, and they get confused.

The home-path header past about ten. Drowsy, never guilty.

The library with no result. Centred at 178px, no crop.

Never-guilt is the law here: it must read “good stopping point”, never “you stopped”.

A pose at ease. The path header took mai-lofi-study instead — a companion should carry a verb. Kept as the identity input most edits start from.

The companion in motion, for journey and transition moments.

The scoring-wait companion. Dies the moment it reads as worry — a character waiting beside a verdict is the risk.

Open palm, her eyes on her own hand — teach-before-test puts the word where she is looking.

The contour gesture. Framed left on purpose: the canvas keeps ~250px of clear air for the contour to be drawn into.

Forming the character, not taking notes. A scene — wants 190px, not a bust seat.

“You already knew this.” Recognition shared, not victory celebrated — the open-mouthed cut was rejected for crossing into cheering.

Three on the hand — a teacher's count, not a cheer. Took four rounds; the model drew a peace sign three times.

The first meeting. Role-play cannot use wardrobe — the wardrobe is the character — so formality lives in the bowed head and folded hands.

Over tea — goal 1's scene. Full body at master framing; the layout does the cut.

Her turn. On screen while TTS plays 你是哪国人?— which is why the closed mouth is a gate, not a style note.

The price scene — 多少钱. Anchored on role-tea, not on greeting: a scene pose inherits its outline weight from the scene pose beside it. The cast stops at four.

The raised card breaks the silhouette above the lap where studying and writing are all lap-and-floor; the deck on the floor is what makes it flashcards and not a note.

Every box is ticked, so it says done — right for a zero-state, wrong beside "here's what's due". A due-today twin needs unticked boxes and is a different asset.

Listening with you, not waiting on a verdict. Same expression as mai-music by design — the difference is the bust framing, which crops into a panel where a floor scene cannot.

First cut read as a selfie at 288px and was killed. Phone at chest, face square to the viewer, screen paper-white — the corpus had only ever drawn the phone's back.

The one deliberate exception to the long-hair canon: a compact bob, because the hair-mass law wins at an edge seat.

Half of the no-Lottie speaking pair. Pixel-registered against its twin — the diff between the two files is the mouth and nothing else.

The other half. Swap the two on a timer where Lottie is unavailable.

The companion mark for chips, rows and rail headers. Gated at 24px — it has to survive being tiny.

The same mark, winking. Rare-delight moments only — routine use spends the effect.

Raised arms. Marketing-only in v1 by ruling — in-app celebration is the thumbs-up.

Pointing right, for store and ad compositions.

The mirrored twin, so a composition never has to flip her and break the parting.

Open mouth. In-app she is drawn beside TTS audio, never producing it, so this stays out of the app.

Never in-app: a surprised face next to a learner's result reads as the grader's reaction.

Looking up out of frame — for bubble-acknowledgment icon compositions.
All programmatic — no animator, no Rive; every one is built by tools/make_motion.py and regenerable.
Two rules hold across the whole set. Frame 0 renders identical to the shipped still, so reduced-motion and first paint resolve with no pop — checked by
tools/check_frame0.py at each loop's walked size, not asserted. And the rig transforms the whole image, never a cut-out part: a moving cutout either
tears at its seam or ghosts against the static copy beneath, and the artifact is as large as the motion. Where a loop is marked unseated the file is ready and the surface is a §5.4c
decision — motion never expands the surface list, and never touches a lesson or score surface.
The desk. Breath and two blinks on an irregular beat — a metronome blink is what catches peripheral vision.
She reads. One page turn (not two — a second in ten seconds reads as skimming), two blinks, breath. The turn is cut from the drawing's own page silhouettes.
She writes. Three grey marks draw onto the blank right page on a trim path, then clear. Same 10s, breath and blink beat as its sibling — deliberately no page turn.
Three dots pop in along her drawn up-gaze and fade together. A dot appearing against empty canvas is the strongest contrast change available — the pupil glance was declined, because moving a pupil means redrawing one without its highlight crescent.
The screen lights in bands down the handset's own axis — measured at 14.06° by least squares, because PCA answers 10.1° once the fingers eat the lower half. Nothing translates; only opacity moves.
Breath and two blinks, and nothing else — this drawing has no contrast beat in it. The raised hand rises with her because it is one transform on one image; that is the breath, not a wave.
Shallower than the house breath on purpose: a deep breath at a paywall reads as a sigh, and a sigh beside an offer is a comment on the learner's decision. One −1.3° lean, tilting toward the completed facts and away from the purchase.
The slowest in the library: 8.6 breaths/min against the clock loops' 12, plus one lean. No blink — her eyes are drawn closed. Steam was declined; the 160px above the mug is all chin and collar.
A slow sway, one movement per ~4s. Nodding in time, not bouncing. No blinks — her eyes are drawn closed.
Breath at bust amplitude plus a drowsy lean. The one-shot yawn was cut — a beat that happens once and never again reads as a glitch.
Breath and two blinks while the learner reads. The hand settle is the breath — one transform on one image, so the hand stays registered to her body.
Four tones as labelled markers in one file — goToAndPlay('tone3', true). The hand holds and the contour draws out of her fingertip; the shape is what a learner needs, not the travel.
The cue card's speaking state — mouth cycle only, nothing else moves.
Breath and a measured blink over the ear-cup pose, hand held.
The goal-complete arrival: settles after the coin's moment and ends on the static frame.
The scoring wait — a slow rise anchored at the flat crop edge, nothing else. Dies if it reads as worry.
A little hop landing on the encouraging stance — the gesture of record. Raised-arms is discouraged in-app, so it lands on the thumbs-up.
Breath + blink on the embedded canonical. The cheapest possible sign of life; the door's minimal idle.
Pure shape layers, no raster at all — pupils and brows become animatable, so she breathes, blinks and glances with a brow lift.
Kept because deleting them loses the reason they exist, and because someone will otherwise draw them again. Nothing here goes on a screen. Each note says what replaced it.

A base drawn into the artwork is a second, softer edge floating near the panel's real one, and it lines up only at the offset it was authored for. Crops live in the container — use the master.

Same ruling. Seat mai-listening and let the panel make a straight horizontal cut.
Drawn as inventory. The intended beat is after a correct recognition tap — whether that is close enough to a verdict surface is an open adjudication, and not the mascot seat's to make.
Thought dots along her gaze, on a verbatim uncompressed embed — nine times the 110KB ceiling. Kept as a taste test only; a shippable cut would need re-quantizing.
Sound arcs beside the cupped hand, same uncompressed embed. Superseded for real use by mai-listening-loop at 72KB.
An early reaction one-shot from the first motion ladder. Never seated; mai-goal-settle is the shipped arrival beat.
The negative twin of the above — and the reason it will not ship: she is never the one who tells a learner they were wrong.
Four surfaces, each with its one mapped expression. Each card links the built mock. Expressions not mapped here exist in the corpus and are marketing-only in v1.
The greeter. 你好 is her line, in a paper bubble, on the speaking-rose scene panel.

She cups her ear beside the headline — right where the copy asks for your voice.

She celebrates the completed fact, never the limit. The offer row is character-free.

Genuine zero-result states only — never a failure. An error surface carries no vibe.
Where she never appears: lesson, recording-result and score surfaces — a character beside a verdict reads as the grader's mood — and the paywall's offer row. On any money or streak surface she is never sad, disappointed, pleading, alarmed, or gesturing toward the purchase. Marketing, store and ad compositions obey this same list.
There is no illustrator. Every asset is generated from the canonical by an identity-preserving
edit model, gated at ~15% of a 1080×1920 frame, and promoted only with a manifest recording its prompt, seed, hashes and permitted surfaces —
the corpus plus the manifests is the character sheet. The copies under src/mai/ here derive from
product/mascot/assets/mai/; pipeline, playground and plan of record live in product/mascot/. One file in the corpus is not a card above and never will be: mai-canonical-source.svg is the un-flattened source the shipped mai-canonical.svg is exported from — a build input, not an asset.