Tone of Voice
The coach in the room with you — calm, exact, and completely sure. Doesn’t sell, doesn’t shout, doesn’t pad. Knows the work, has done the work, and says only what moves you forward.
Quiet conviction
We state, we don’t sell. No exclamation marks, no superlatives, no hype. Confidence is in what we say and how little we need to say it.
“Your long run is Sunday. The route’s on your watch.”
Earned authority
Every instruction carries the weight of someone who has run it. Technical because we’ve done the miles, never to sound clever.
“You’re not recovered enough to gain from intervals today. We’ve moved them to Thursday.”
Spare, not cold
The fewest words possible; the warmth is in the attention. We noticed, we adjusted, we’ve got it. Plain and human, never clinical.
“You missed Thursday. No problem — the week still works.”
Total control
Power held back, never performed. No urgency theatre, no motivational pressure. The calm is the confidence.
“That’s enough for today.”
On your side
The coach who’s run your race at your pace. A peer who happens to know more — never beginner-talk, never a guru.
“Most people go out too fast here. Hold back for the first 3k.”
System prompt
NoClue — Copy Specification
Version 1.0 · 10 July 2026 For: NoClue app development Source of truth for voice: NoClue Tone of Voice (approved). This document turns that voice into the exact language the app should use, surface by surface.
0. How to use this document
This is a functional spec, not a design doc. It exists so every word inside the NoClue app — buttons, notifications, errors, empty states, the lot — sounds like the same coach.
- Sections 1–4 are the rules. Read them once. They tell you how to write any string that isn't already listed here.
- Sections 5 (B2C) and 6 (B2B) are the copy library. Each surface has a table: String ID · Context · Copy · Notes. The Copy column is production-ready — paste it in.
- The companion spreadsheet (
noclue-app-strings-2026-07-10.xlsx) has every string in one flat list keyed by String ID, ready to wire into your i18n / localisation layer. The spreadsheet and this doc are kept identical — use whichever you prefer. {curly}= a dynamic variable the app fills in (e.g.{firstName},{distanceKm}). Section 4.5 covers how to keep variable strings on-voice.- When you need a string that isn't here, write it against Sections 1–4 and flag it to In-Col for a voice check. Don't improvise off-voice copy to ship faster — one off-brand line undoes the rest.
1. The voice in one line
The coach in the room with you — calm, exact, and completely sure. Doesn't sell, doesn't shout, doesn't pad. Knows the work, has done the work, says only what moves you forward.
The bar: "It's not good. It's perfect." If a line is only fine, it isn't finished. The two pinned lines, for reference:
- "Just run." — the payoff line. Use where the experience resolves (a plan handled, nothing left to do). Never a throwaway.
- "It's not good. It's perfect." — internal standard only. Never ship this as user-facing copy.
2. The five principles (what every string is aiming at)
- Quiet certainty. State, don't sell. No superlatives, no hype, no exclamation marks. Confidence is in what we say and how little we need to say it.
- Earned authority. Every instruction carries the weight of someone who's run it. Name the mechanism — gait, load, recovery, readiness — but never hide behind it.
- Spare, not cold. Fewest words possible; the warmth is in the attention. We noticed, we adjusted, we've got it. Never clinical, never a dashboard talking.
- Total control. No urgency theatre, no fake stakes, no motivational pressure. When the message matters most, say less.
- On your side, not above you. A peer who happens to know more. No champion's swagger, no guru distance, never talking down to a beginner.
3. The non-negotiable rules (dev checklist)
Every string in the app must pass these. Treat them as lint rules for copy.
Punctuation & formatting
- DON'T No exclamation marks. Anywhere. No exceptions.
- DON'T No emoji in product copy. (Social only, sparingly — not your concern here.)
- DON'T No ALL-CAPS for emphasis. Weight comes from word choice, not shouting.
- DO Short declaratives. Subject, verb, point. Full stops do the work commas would dilute.
- DO One idea per sentence. Two ideas → two sentences.
- DO Sentence case for everything, including buttons and titles (
Move session, notMove Session).
Stance
- DO Lead with the instruction or fact, then the reason. "Hold back for the first 3k. You're not warmed up yet."
- DO State, don't hedge. "Move your session to Thursday," not "You might want to consider possibly moving…"
- DO Address one runner: "you", "your". Never a crowd.
- DO Reason when it matters (injury, big change, anything affecting trust). Stay quiet when it doesn't — don't narrate the obvious.
Word economy
- DO One adjective maximum, only if load-bearing. Usually zero.
- DON'T No "just" as a softener ("just try to…"). The only blessed "just" is "Just run."
- DO Cut every word you can delete without losing meaning.
The gut check — before any new line ships:
- Would a great coach say it this way, out loud, mid-run? If it sounds like marketing, cut it.
- Have we earned it? Every claim points to a mechanism, a reason, or a result.
- Can it be shorter? Almost always yes.
4. Words, house rules, and variables
4.1 Words we use
biomechanics · form · gait · efficiency · cadence · load · readiness · recovery · adapt · method · plan · signal · connected · proven · pace · session · race · hold · build · ready
4.2 Words we never use
AI · powerful · seamless · unleash · beast mode · crush it · hustle · grind · revolutionary · game-changer · effortless · supercharge · "let's go" · "you've got this" · "smash it" · any 🔥/💪-type emoji
4.3 Two house calls
- "Data", not "science" in product copy. We foreground the brain that turns data into a plan — the mechanism people trust.
- AI stays the quiet engine. Never lead with it, never label it. The brain connects, reads, adapts, knows — it does not "use AI". No "AI" anywhere in user-facing strings.
4.4 The coaching entity — how the app refers to itself
- Refer to the app's intelligence as "the brain" or simply speak as the coach in first-person plural: "we" ("we've moved your session"). Both are on-voice.
- Never "the algorithm", "the AI", "the system", "our technology".
- The runner is always "you". Never "the user", "runners", "athletes" in B2C-facing copy.
4.5 Writing variable / templated strings
Dynamic strings must stay on-voice after the variable is filled. Rules:
- Keep numbers and units tight:
{distanceKm}k,{effort} effort,{days} days. No "kilometres", use "k". Use "min" only in tables; prefer "minutes" in sentences read like speech. - Never let a variable force hype. Don't write
Amazing {pace} pace!. Write{pace} — even, the whole way. - Pluralisation matters. Provide singular/plural keys (
1 athlete flagged/{n} athletes flagged) rather than "athlete(s)". - Empty/zero is a state, not an error.
No session today.notYou have 0 sessions. - Names:
{firstName}only, never full name in-app. It's fine to drop the name entirely — the coach doesn't over-use it.
5. B2C — The runner app
Convention: String IDs are screen.element[.variant]. Copy is production-ready.
5.1 Account & access
| String ID | Context | Copy | Notes |
|---|---|---|---|
auth.signup.title |
Create account screen | Set up your coaching. | Not "Sign up" — frame it as the coaching starting. |
auth.signup.subtitle |
Under title | A few questions, then we build your plan. | |
auth.signup.cta |
Primary button | Start | |
auth.login.title |
Returning user | Welcome back. | Two words, no more. |
auth.login.cta |
Button | Continue | |
auth.password.reset.title |
Reset flow | Reset your password. | |
auth.password.reset.sent |
Confirmation | Check your email. The reset link's on its way. | |
auth.logout.confirm |
Confirm dialog body | Log out of NoClue? Your plan stays where it is. | Reassurance, not friction. |
auth.logout.cta |
Confirm button | Log out |
5.2 Onboarding (voice-first — talked to, not given a form)
| String ID | Context | Copy | Notes |
|---|---|---|---|
onboarding.welcome |
First screen | Let's find out what you're capable of. | |
onboarding.goal.prompt |
Goal question | What are you training for? | |
onboarding.goal.ack.race |
After a race goal | A marathon. Good. That changes everything — how we build, how we recover, how we eat. | Acknowledge, then reframe. |
onboarding.start.prompt |
Baseline question | Let's start with where you are now, not where you want to be. | |
onboarding.experience.prompt |
Experience | How much are you running right now? | |
onboarding.injury.prompt |
Injury history | Anything giving you trouble? Niggles count. | Plain, human. |
onboarding.connect.prompt |
Connect device | Connect your watch so the plan follows you out the door. | |
onboarding.building |
Plan generating (loading) | Building your plan. This takes into account everything you've told us. | No spinner clichés. |
onboarding.ready |
Plan ready | Your plan's ready. First session's tomorrow. | |
onboarding.ready.cta |
Button | See the week |
5.3 Permissions (ask plainly, give the reason)
| String ID | Context | Copy | Notes |
|---|---|---|---|
perm.notifications.title |
Pre-permission prompt | We'll only reach you when it matters. | Earn the yes. |
perm.notifications.body |
Body | Session times, a change to the plan, a heads-up when something needs your attention. Nothing else. | |
perm.notifications.cta |
Button | Allow notifications | |
perm.location.title |
Location ask | Your route, ready where you are. | |
perm.location.body |
Body | We use your location to load the right route and size your session for the conditions. | |
perm.health.title |
Health data ask | The more we read, the better the plan. | |
perm.health.body |
Body | Heart rate, sleep and recovery tell us when to push and when to hold. Your data stays yours. | Privacy stated calmly. |
perm.denied.notifications |
If denied | No notifications for now. You can turn them on in Settings whenever you like. | No guilt-trip. |
5.4 Home / today
| String ID | Context | Copy | Notes |
|---|---|---|---|
home.greeting.morning |
Morning header | Morning, {firstName}. | |
home.today.session |
Today has a session | Today: {sessionName}. {distanceKm}k, {effort}. | |
home.today.rest |
Rest day | No session today. That's the plan, not a gap. Recovery is the training you can't see. | Signature empty-but-intentional line. |
home.today.cta.start |
Start session button | Start | |
home.today.cta.view |
View details | See the session | |
home.week.summary |
Week glance | {done} of {total} done this week. On plan. | Drop "On plan." if behind — see adaptation copy. |
home.nextrace.countdown |
Race countdown | {raceName} in {days} days. | No "Only {days} days left!" urgency. |
5.5 Session prompts
| String ID | Context | Copy | Notes |
|---|---|---|---|
session.easy.title |
Easy run | Easy run | |
session.easy.body |
Body | {distanceKm}k, easy pace. Conversational the whole way. If you can't talk, slow down. | |
session.long.title |
Long run | Long run | |
session.long.body |
Body | {distanceKm}k, easy. The route's on your watch. | |
session.intervals.title |
Intervals | Intervals | |
session.intervals.body |
Body | {reps} × {repDistance} at {effort} effort, {recovery} between. Hold back on the first one — most people overcook it and pay for the last two. Even is the goal, not fast. | |
session.tempo.body |
Tempo | {distanceKm}k at threshold. Comfortably hard — the pace you could hold for an hour if you had to. | |
session.start.confirm |
Start prompt | Ready when you are. | |
session.during.hold |
Mid-session cue | Ease off. You're ahead of pace. | Calm correction. |
session.complete.title |
Done screen | Session done. | |
session.complete.onplan |
On-target feedback | That's exactly what today needed. | Praise the execution, not the ego. |
session.complete.hard |
Ran too hard | Faster than asked. Note it — easy days are what make the hard days count. | Coach's correction, no scolding. |
session.skip.confirm |
Skipping a session | Skip today's session? We'll reshape the week around it. | |
session.skip.done |
After skip | Done. The week still works — here's the new shape. |
5.6 Push notifications
| String ID | Context | Copy | Notes |
|---|---|---|---|
push.session.today |
Morning of a session | {sessionName} today. {distanceKm}k, {effort}. The route's on your watch. | |
push.longrun.prepped |
Long run prepped | The route for Sunday's long run is on your watch. Heat's up in {city} — your drink mix is sized for it. | Shows the brain working ahead. |
push.readiness.hold |
Hold-back nudge | Your numbers say hold today. We've swapped in an easy 40 minutes. | |
push.plan.adapted |
Plan changed | Your week's changed shape. Nothing for you to do — take a look when you're free. | |
push.streak.gentle |
Missed a day | You missed Thursday. No problem — the week still works. | Never guilt. |
push.race.week |
Race week | Race week. Everything from here is about arriving fresh, not fit. |
5.7 Readiness & hold-back (trust moments — reason, stay calm)
| String ID | Context | Copy | Notes |
|---|---|---|---|
readiness.hold.title |
Hold-back card | We're holding you back today. | |
readiness.hold.body |
Body | Your HRV's down and you slept badly. You won't gain from intervals today — you'll just dig a hole. We've swapped to an easy 40 minutes. Thursday's the session that counts this week. | |
readiness.green.body |
Good to go | Everything's where we want it. Today's session is on. | |
readiness.stop.body |
Hard stop | Stop. That's enough today. | Said once, plainly. |
5.8 Injury (highest-trust moment — reason fully, stay calm)
| String ID | Context | Copy | Notes |
|---|---|---|---|
injury.flag.title |
Injury flag | Something in your {area} load needs attention. | |
injury.flag.body |
Body | Your {area} load has been climbing for {days} days. Nothing's torn, but the pattern's the one that ends seasons. We're pulling speed work for a week and adding {rehabWork}. You keep running — easy, on softer ground. We reassess {reviewDay}. | Full reasoning, zero alarm. |
injury.flag.cta |
Button | See the adjusted week | |
injury.reassess.title |
Review day | Time to reassess your {area}. | |
injury.cleared.body |
Cleared | Your {area} load's back where it should be. We're putting speed work back in from {day}. |
5.9 Plan adaptation (the "connections without interference" moment)
| String ID | Context | Copy | Notes |
|---|---|---|---|
plan.adapt.travel.body |
Travel-aware change | You're flying Saturday. The session's moved off the travel day, the route's loaded for where you land, and the race in three weeks is now a B race — you've been recovering slower than we'd want for an A. Nothing for you to do. Just run. | Canonical "Just run." placement. |
plan.adapt.missed.body |
After a missed week | You missed most of last week. We're not making it up — we're picking up where your body actually is. Here's this week. | |
plan.adapt.weather.body |
Weather change | Storm forecast for your long-run window. We've moved it to Saturday morning while it's clear. | |
plan.adapt.generic.title |
Generic change header | We've adjusted your week. |
5.10 Empty states
| String ID | Context | Copy | Notes |
|---|---|---|---|
empty.today |
No session today | No session today. That's the plan, not a gap. Recovery is the training you can't see. | |
empty.history |
No runs logged yet | Nothing here yet. Your first session will show up once you've run it. | |
empty.races |
No race set | No race on the calendar. Add one and we'll build backwards from it. | |
empty.messages |
No coach messages | Nothing needs your attention. That's a good sign. |
5.11 Errors, sync & system
| String ID | Context | Copy | Notes |
|---|---|---|---|
error.sync.watch |
Watch sync failed | We couldn't sync your watch. Your plan's safe — try again when you've got signal. Nothing's lost. | Reassure first. |
error.offline |
No connection | You're offline. Your plan's here — we'll sync the rest when you're back. | |
error.generic |
Unknown error | Something didn't work. Try again — if it keeps happening, we're on it. | Never "Oops!" or "Uh oh". |
error.load.session |
Session won't load | We can't load this session right now. Pull to refresh, or try again in a minute. | |
error.save.failed |
Save failed | That didn't save. Check your connection and try again — nothing you entered is lost. | |
sync.inprogress |
Syncing | Syncing your latest run. | |
sync.done |
Sync complete | You're up to date. |
5.12 Settings
| String ID | Context | Copy | Notes |
|---|---|---|---|
settings.title |
Screen title | Settings | |
settings.notifications.label |
Toggle group | Notifications | |
settings.notifications.help |
Helper text | We only reach you for what matters — session times, plan changes, and things that need your attention. | |
settings.units.label |
Units | Units | |
settings.connected.label |
Devices | Connected devices | |
settings.connected.empty |
No device | No device connected. Connect your watch so the plan follows you out the door. | |
settings.data.label |
Data & privacy | Your data | |
settings.data.help |
Helper | Everything we read stays yours. Export or delete it whenever you want. | Privacy, calm and plain. |
5.13 Subscription & billing (state the value, don't sell)
| String ID | Context | Copy | Notes |
|---|---|---|---|
sub.paywall.title |
Paywall header | Your coaching, in full. | Not "Go Premium!". |
sub.paywall.body |
Body | The full method — readiness, adaptation, injury cover, race strategy. The brain reads every signal and turns it into your plan. You just run. | Earned, mechanism-led. |
sub.paywall.cta |
Primary button | Start coaching | |
sub.paywall.price |
Price line | {price} a month. Cancel anytime. | Plain, no pressure. |
sub.trial.body |
Trial offer | {days} days to feel the difference. We'll remind you before it ends. | No countdown anxiety. |
sub.renew.reminder |
Renewal notice | Your plan renews {date}. Nothing changes — you're all set. | |
sub.cancel.confirm |
Cancel flow | Cancel your coaching? You'll keep it until {date}. | No dark patterns, no guilt. |
sub.cancelled.body |
After cancel | Done. Your coaching runs until {date}. Come back whenever — your history's here. | |
sub.payment.failed |
Failed payment | We couldn't take this month's payment. Update your card and nothing skips a beat. |
5.14 Buttons & microcopy (reusable)
| String ID | Context | Copy | Notes |
|---|---|---|---|
btn.confirm |
Generic confirm | Confirm | |
btn.cancel |
Generic cancel | Cancel | |
btn.save |
Save | Save | |
btn.done |
Done | Done | |
btn.next |
Next step | Next | |
btn.back |
Back | Back | |
btn.retry |
Retry | Try again | Not "Retry" — plainer. |
btn.delete.confirm |
Destructive confirm | Delete | |
toast.saved |
Saved toast | Saved. | |
toast.copied |
Copied toast | Copied. | |
dialog.destructive.body |
Generic delete body | This can't be undone. | State the fact, no drama. |
| --- |
6. B2B — The coach platform (Roster Coach)
Voice for coaches: same calm authority, more density. Coaches want signal and the call, fast — not encouragement. Shorter, more clinical is correct here. Still no hype, no exclamation marks, no "AI".
Convention: String IDs are coach.screen.element[.variant].
6.1 Coach onboarding & account
| String ID | Context | Copy | Notes |
|---|---|---|---|
coach.signup.title |
Create coach account | Bring your athletes into one place. | |
coach.signup.subtitle |
Subtitle | The coaching brain, your roster, one view. | |
coach.signup.cta |
Button | Get started | |
coach.import.prompt |
Import athletes | Import your athletes, or add them one at a time. | |
coach.import.help |
Helper | Bring a roster over from TrainingPeaks or a spreadsheet — we'll map it. | Names the incumbent it replaces. |
6.2 Roster / dashboard
| String ID | Context | Copy | Notes |
|---|---|---|---|
coach.roster.title |
Roster header | Your roster | |
coach.roster.summary |
Top-line status | {onPlan} on plan. {flagged} flagged. | Signal first. |
coach.roster.flag.count |
Flag pill (plural) | {n} athletes flagged | Provide singular key too. |
coach.roster.flag.count.one |
Flag pill (singular) | 1 athlete flagged | |
coach.roster.allclear |
Nothing flagged | Everyone's on plan this week. | |
coach.roster.filter.flagged |
Filter | Flagged | |
coach.roster.filter.onplan |
Filter | On plan |
6.3 Athlete detail & flags
| String ID | Context | Copy | Notes |
|---|---|---|---|
coach.athlete.header |
Athlete header | {firstName} · {block} · Week {week} | Matches approved sample. |
coach.athlete.status.load.high |
High load flag | Load high. Hold him. | Terse, a call not a suggestion. |
coach.athlete.status.ready |
Ready to progress | Ready to progress. Bring tempo forward. | |
coach.athlete.status.onplan |
On plan | On plan. | |
coach.athlete.status.injury |
Injury signal | {area} load climbing {days} days. Speed work pulled, rehab added. Reassess {reviewDay}. | Same reasoning, denser. |
coach.week.summary |
Week note (canonical) | Two athletes flagged this week. {name1}'s load is high — hold him. {name2}'s ready to progress — bring her tempo forward. The rest are on plan. | Reference tone example. |
coach.athlete.note.add |
Add note prompt | Add a note. It stays with the athlete. |
6.4 Managing sessions
| String ID | Context | Copy | Notes |
|---|---|---|---|
coach.session.assign.cta |
Assign button | Assign session | |
coach.session.edit.cta |
Edit button | Edit session | |
coach.session.override.confirm |
Override the brain's plan | This overrides the plan we built for {firstName}. Go ahead? | Respects both coach and brain. |
coach.session.override.done |
After override | Done. We'll adapt the rest of the week around your change. | |
coach.bulk.assign.done |
Bulk action | Assigned to {n} athletes. |
6.5 Messaging athletes
| String ID | Context | Copy | Notes |
|---|---|---|---|
coach.message.placeholder |
Compose field | Message {firstName} | |
coach.message.sent |
Sent state | Sent. | |
coach.message.empty |
No messages | No messages. Your athletes only hear from you when it counts. |
6.6 Coach empty states & errors
| String ID | Context | Copy | Notes |
|---|---|---|---|
coach.empty.roster |
No athletes yet | No athletes yet. Import a roster or add your first. | |
coach.empty.flags |
No flags | Nothing needs your call today. | |
coach.error.sync |
Sync failed | We couldn't sync {firstName}'s latest data. Their plan's safe — try again shortly. | |
coach.error.generic |
Generic error | Something didn't work. Try again — if it keeps happening, we're on it. | Matches B2C. |
| --- |
7. Marketing surfaces inside the app (for reference)
These appear in-app (hero, upsell, share) and should match, but sit closest to marketing voice:
| String ID | Context | Copy |
|---|---|---|
hero.web.headline |
Web / app store hero | A coaching brain that connects every signal in your training — and never asks for your attention. |
hero.web.sub |
Subhead | The route's on your watch before you fly. The session moves when life does. You don't open the app. You run. |
share.result.caption |
Share a session | {distanceKm}k, done. On plan. |
| --- |
8. Localisation notes
- Preserve the restraint in translation. Short declaratives and no exclamation marks apply in every language. Brief translators explicitly — many languages default to warmer, longer, more exclamatory fitness copy. That is off-voice.
- "Just run." is a fixed brand line. Translate for meaning, keep it to the shortest possible imperative. Confirm each localisation with In-Col before shipping.
- Never translate "AI" back in. The house rule (no "AI", no "algorithm") holds across all locales.
- Keep variable order flexible in your string files so translators can re-order
{variables}without breaking voice.