---
title: "Engagement"
description: "Quests, XP, streaks, levels, achievements, notifications, haptics and motion per moment."
source: "design/engagement.md"
---

> Part of [Brand and design](/design/llms.txt). Every page: [llms.txt](/llms.txt).

# Engagement system — "a bit addictive", within limits

The core loop is already the best hook. You say something, get **one** concrete thing to change, say it again, and *see* the difference (the before/after block). Everything below amplifies that loop. Nothing below replaces it. All mechanics follow ADR-001 D4 (XP, streak, levels) and D10 (allowed / not allowed).

## Mechanics

| Mechanic | Where | Rule (source) | Design |
|---|---|---|---|
| **Daily quest** | Home ink card | One article per day in a practice language, plan theme applied (`today_quest`) | Lime XP badge, 3-step path (read / retell / compress) showing the real stage, focus chip "Last advice: Conclusion first", one coral CTA. Completed state shows the round 1 → 2 result (CCR-2) and offers free practice. |
| **XP** | Quest badge, completion, level bar | 15 / 20 / 10 / +10 improve; daily cap 165; 50 % on repeats within 7 days; diagnostic once (D4) | Completion lists every line, including the bonus you *didn't* get (struck through: "when take two scores higher"), so the rule is learnable rather than a slot machine. |
| **XP bar fill** | Completion level card | `level_before → level_after` | Lime fill on an ink capsule, 900 ms emphasized ease, 600 ms delay after the trophy. Striped "gain" segment. Selection haptic tick every 10 XP (max 8 ticks). |
| **Level-up** | Full-screen overlay | thresholds L2 100 … L10 2700, titles per D4 | Ink overlay, lime badge pops (spring), 8 rays, "You’re now a Concise Speaker", XP to next. Heavy impact → success haptic. Shown once per settlement (`replayed` suppresses it). |
| **Streak** | Home pill, completion tile, Profile | +1 on the first *completed* session of a local day; ≥ 20 h guard (D4) | Amber flame, outline when not done today, filled when done. Completion tile shows "+1" with a rigid haptic. |
| **Streak freeze** | Streak-risk card, notification | 1 per 7-day milestone, max 2, consumed automatically (D4). Freeze *UI* is v1.1 (D7). | v1 only *mentions* it to remove pressure ("If today doesn’t work out, you still have 1 streak freeze") and confirms after use (CCR-8). No buying, no repairing. |
| **Streak at risk** | Home after 20:00, one notification | `streak.at_risk` | Calm amber card: "Not yet today — 10 minutes is enough". Never "You'll lose…". |
| **Before/after block** | Every completion | `round1_overall → round2_overall`, durations, `overall_delta` | The signature moment: ink block, round 2 in lime with a count-up from the round-1 value. When there's no improvement it stays neutral white. "Shorter but lost points" adds the missed point in amber. Session delta and **ability** change are separate tiles, so a +11 session never looks like +11 ability. |
| **"You did it ✓"** | Top of feedback | `achieved_previous_action` (D9) | First thing on screen, green, soft haptic. This is the progression hook: advice → attempt → recognition. |
| **Achievements** | Completion sheet, Growth row | D10 list: First Win, Conclusion First ×3, 3-Day, 7-Day, 10 Sessions, Efficiency +10, Compressed ×5 | Lime tile, sheet after the trophy (queued, one at a time), locked ones show progress "4 / 5". |
| **Weekly quests** | Profile, completion card | retell 5 / compress 3 / follow advice 3; Monday reset in user tz | Progress bars with XP. Counting rules are spelled out on the weekly screen. XP semantics need CCR-10. |
| **Micro-skill mastery** | Growth grid | achieved / suggested over the last 10 sessions (D9) | Pips per suggestion (green = done). The current focus is outlined. Untried skills are visible but empty: curiosity, not failure. |
| **"Next trend point" hook** | Growth chart | `growth.next_unlock` + partial current week (CCR-5) | Dashed ring where this week's point will appear, plus a lock pill "Train tomorrow to unlock this week’s point". |
| **14-day plan** | Plan, paywall calendar, quest tag | D9 | "14-day plan · Day 3" tag on the quest; day 7 review, day 14 re-test against the starting score. |
| **Language variety** | Library, Growth chips | D13 | Practising in a second language is framed as a choice ("Practice languages"), never as an upsell or a separate streak. |

## Notifications (max **one** per local day, total)

Priority if several qualify on the same day: trial-ending (a billing notice the user must get) > result-ready (only if the user left processing) > plan/daily reminder > streak-risk. Once one is sent, the rest are dropped for that day, so a trial-ending day sends no reminder. Quiet hours 22:00–08:00 (except result-ready within 10 min of submit). Time zone follows `me.timezone`.

| Trigger | Time | en | zh-Hans | Key |
|---|---|---|---|---|
| Daily reminder | `settings.reminder_time` | Today’s 10 minutes: {article title} | 今天的 10 分钟：{title} | `notif.daily.body` |
| Plan day | `reminder_time` while a plan is active | Plan day {n} · today: {skill} | 计划第 {n} 天 · 今天练「{skill}」 | `notif.daily.body.plan` |
| Focus | `reminder_time`, no plan | Try “{skill}” today: {instruction} | 今天试试「{skill}」：{instruction} | `notif.daily.body.focus` |
| Streak risk | 20:00 if not done and the daily wasn't sent | {n} days in a row. There’s still time today — 10 minutes is enough. | 连续 {n} 天了。今天还有时间，10 分钟就够。 | `notif.risk.body` (+ `.freeze`) |
| Result ready | when assessed, if the user left processing | Your feedback is ready: {summary} | 反馈好了：{summary} | `notif.result_ready.body` |
| Trial ending | day before billing, 10:00 | Your trial ends tomorrow… cancel today… you won’t be charged. | 试用明天结束… | `notif.trial_ending.body` |

Rules: never a notification on the day of a completed session (except result-ready). No "we miss you" re-engagement series in v1. Two ignored streak-risk notifications in a row → stop streak-risk until the next completed session.

## Haptics and motion per moment

| Moment | Motion (reduced-motion variant) | Haptic |
|---|---|---|
| Record start | button colour → coral 160 ms; rings start breathing 1600 ms (rings static) | impact medium |
| Pause / resume | button swap 160 ms | impact light |
| Auto-stop | sheet slides up 400 ms (cross-fade) | warning |
| Assessment ready | processing → feedback push; ring arc draws 1200 ms (instant) | success |
| "You did it ✓" banner | fade + 8 pt rise 240 ms (fade) | soft impact |
| Evidence tap | row tint 160 ms; playback | selection |
| Diagnostic reveal | score counts 0 → N 1200 ms, dimensions stagger 80 ms (instant) | success at the end |
| Trophy | pop: scale .4 → 1, rotate −20° → −6°, spring 900 ms; 6 rays (fade only) | success |
| Round-2 count-up | r1 → r2 over 1200 ms after 500 ms (instant) | — |
| XP fill | 900 ms emphasized after 600 ms (instant) | selection every 10 XP, max 8 |
| Streak +1 | "+1" fades in after the XP fill | impact heavy (once) |
| Level-up | overlay fade 240 ms, badge spring, 8 rays (fade only) | heavy → success +120 ms |
| Achievement sheet | sheet 400 ms after the level-up is dismissed (fade) | success |
| Paywall slider detent | projection number cross-fade 160 ms; bar slides 400 ms (instant) | selection |
| Purchase success | toast spring | success |

All celebrations are **skippable** (tap anywhere completes the animation) and **never block** the CTA for more than 1.2 s.

## Dark patterns we explicitly do NOT use

1. **Guilt or shame copy.** No "Your streak will die", "You're falling behind", "Don't give up on yourself".
2. **Fake scarcity and countdowns.** None on the paywall or anywhere else.
3. **Decoy pricing,** crossed-out reference prices, or "most popular" claims without data.
4. **More than one notification a day,** re-engagement spam, or badge-count manipulation.
5. **Loss-framed streak repair purchases** (pay to restore a streak). Freezes are earned, never sold.
6. **Variable-ratio rewards.** XP is deterministic and fully itemised. No loot boxes or random bonuses.
7. **False praise.** The headline is conditional on the verdict. "Shorter but lost points" gets no trophy. A regression is called normal, not failure.
8. **Hidden or delayed close.** The hard paywall is openly hard: no fake close button. Every other sheet is dismissible immediately.
9. **Confirmshaming** on "Not now", cancel, or delete. All are neutral verbs.
10. **Infinite feed / autoplay.** After a session, the next step is a choice (Progress / Home). We never auto-start another article.
11. **Social comparison leaderboards.** Benchmarks appear only on the paywall, labelled, with sample sizes, and never rank individuals.
12. **Dark defaults.** Notifications need explicit opt-in. Analytics never include content. Audio retention defaults to 30 days, not "forever".
