diff --git a/app/src/main/java/app/closer/MainActivity.kt b/app/src/main/java/app/closer/MainActivity.kt index 865e4091..8df9ff4e 100644 --- a/app/src/main/java/app/closer/MainActivity.kt +++ b/app/src/main/java/app/closer/MainActivity.kt @@ -316,7 +316,7 @@ private fun BiometricLockScreen(activity: AppCompatActivity, onUnlocked: () -> U } ).authenticate( BiometricPrompt.PromptInfo.Builder() - .setTitle("Unlock Closer") + .setTitle("Unlock Closer Couples") .setSubtitle("Verify it's you to continue") .setAllowedAuthenticators( BiometricManager.Authenticators.BIOMETRIC_STRONG or diff --git a/app/src/main/java/app/closer/ui/brand/CloserCopy.kt b/app/src/main/java/app/closer/ui/brand/CloserCopy.kt index 7ff53892..d531b279 100644 --- a/app/src/main/java/app/closer/ui/brand/CloserCopy.kt +++ b/app/src/main/java/app/closer/ui/brand/CloserCopy.kt @@ -36,11 +36,11 @@ object CloserCopy { object Paywall { const val headline = "Go deeper together" - const val subhead = "Unlock everything Closer has built for couples." + const val subhead = "Unlock everything Closer Couples has built for you." const val choosePlanTitle = "Choose your plan" const val coupleShared = "One subscription covers you both — treat your partner." const val thankYouTitle = "You're all set" - const val thankYouBody = "Thank you for supporting Closer." + const val thankYouBody = "Thank you for supporting Closer Couples." } object Subscription { @@ -53,9 +53,9 @@ object CloserCopy { * error was triplicated verbatim. Two disclaimer variants are intentionally distinct * (brief on sign-up, with-reason on the DOB step) — kept separate, not unified. */ object AgeGate { - const val disclaimer = "Closer is an 18+ app." - const val disclaimerWithReason = "Closer is an 18+ app. We ask once to confirm your age." + const val disclaimer = "Closer Couples is an 18+ app." + const val disclaimerWithReason = "Closer Couples is an 18+ app. We ask once to confirm your age." const val dobRequired = "Please enter your date of birth." - fun ageError(minAge: Int) = "You must be at least $minAge to use Closer." + fun ageError(minAge: Int) = "You must be at least $minAge to use Closer Couples." } } diff --git a/app/src/main/java/app/closer/ui/pairing/InviteShareText.kt b/app/src/main/java/app/closer/ui/pairing/InviteShareText.kt index a8a1b730..d3d928f2 100644 --- a/app/src/main/java/app/closer/ui/pairing/InviteShareText.kt +++ b/app/src/main/java/app/closer/ui/pairing/InviteShareText.kt @@ -10,7 +10,7 @@ import app.closer.core.navigation.JoinLink object InviteShareText { fun build(code: String): String { val pretty = code.chunked(3).joinToString(" ") - return "Let's try Closer together 💜 It's our own private space to actually talk. " + + return "Let's try Closer Couples together 💜 It's our own private space to actually talk. " + "Tap to join me: ${JoinLink.webUrl(code)}\n\n" + "Or enter this code in the app: $pretty" } diff --git a/app/src/main/java/app/closer/ui/pairing/PairPromptScreen.kt b/app/src/main/java/app/closer/ui/pairing/PairPromptScreen.kt index 1acfa849..95671b42 100644 --- a/app/src/main/java/app/closer/ui/pairing/PairPromptScreen.kt +++ b/app/src/main/java/app/closer/ui/pairing/PairPromptScreen.kt @@ -102,7 +102,7 @@ fun PairPromptScreen( Spacer(Modifier.height(16.dp)) Text( - text = "Closer is built for two. Connect your partner to unlock everything worth unlocking:", + text = "Closer Couples is built for two. Connect your partner to unlock everything worth unlocking:", style = MaterialTheme.typography.bodyLarge, color = AuthMuted, textAlign = TextAlign.Center diff --git a/app/src/main/java/app/closer/ui/recap/WeeklyRecapShareCard.kt b/app/src/main/java/app/closer/ui/recap/WeeklyRecapShareCard.kt index 487b44db..7fc09add 100644 --- a/app/src/main/java/app/closer/ui/recap/WeeklyRecapShareCard.kt +++ b/app/src/main/java/app/closer/ui/recap/WeeklyRecapShareCard.kt @@ -34,7 +34,7 @@ object WeeklyRecapShareCard { else -> "We answered $q questions" } val reveals = if (r > 0) " and opened $r reveal${if (r == 1) "" else "s"}" else "" - return "$answered$reveals together this week on Closer 💜" + return "$answered$reveals together this week on Closer Couples 💜" } /** Renders the card to a bitmap using plain Canvas (no Compose-capture fragility). */ diff --git a/app/src/main/java/app/closer/ui/settings/SettingsScreen.kt b/app/src/main/java/app/closer/ui/settings/SettingsScreen.kt index 8ab7f68c..ff0a8837 100644 --- a/app/src/main/java/app/closer/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/app/closer/ui/settings/SettingsScreen.kt @@ -499,7 +499,7 @@ fun SettingsScreen( SettingsRow( icon = CloserGlyphs.Palette, label = "Appearance", - subtitle = "Make Closer feel comfortable to open", + subtitle = "Make Closer Couples feel comfortable to open", onClick = { onNavigate(AppRoute.APPEARANCE) } ) } @@ -534,7 +534,7 @@ fun SettingsScreen( SettingsRow( icon = ImageVector.vectorResource(R.drawable.glyph_delete_account), label = "Delete account", - subtitle = "Permanently remove your Closer account", + subtitle = "Permanently remove your Closer Couples account", onClick = { onNavigate(AppRoute.DELETE_ACCOUNT) }, tint = SettingsDanger ) diff --git a/app/src/main/java/app/closer/widget/TodayWidget.kt b/app/src/main/java/app/closer/widget/TodayWidget.kt index a6c69015..9f172628 100644 --- a/app/src/main/java/app/closer/widget/TodayWidget.kt +++ b/app/src/main/java/app/closer/widget/TodayWidget.kt @@ -92,9 +92,9 @@ private fun TodayWidgetContent(context: Context, snapshot: TodayWidgetSnapshot) /** Content-free headline derived only from state flags. */ internal fun headlineFor(s: TodayWidgetSnapshot): String = when { - !s.signedIn -> "Open Closer" + !s.signedIn -> "Open Closer Couples" !s.paired -> "Invite your partner to begin" - s.appLockEnabled -> "Something's waiting in Closer" + s.appLockEnabled -> "Something's waiting in Closer Couples" else -> when (s.dailyState) { "USER_ANSWERED_PARTNER_PENDING" -> "Waiting for your partner" "PARTNER_ANSWERED_USER_PENDING" -> "Your turn — answer to reveal" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b13332d2..6a87229d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -72,8 +72,8 @@ Streak and shared wins Your couple\'s streak count and challenge completions are shared between both of you. No data export - Closer does not currently offer a data export. Your answers and history are deleted along with your account. - Closer is built on one rule: answers stay private until both of you have answered. Here\'s exactly what that means. + Closer Couples does not currently offer a data export. Your answers and history are deleted along with your account. + Closer Couples is built on one rule: answers stay private until both of you have answered. Here\'s exactly what that means. Anonymous usage data Share anonymous usage data Helps us see which features couples use — never what you write. Your answers, messages, and prompts are end-to-end encrypted and can\'t be in this data even if we wanted them. diff --git a/app/src/test/java/app/closer/widget/TodayWidgetTest.kt b/app/src/test/java/app/closer/widget/TodayWidgetTest.kt index 97a18421..ae2192ce 100644 --- a/app/src/test/java/app/closer/widget/TodayWidgetTest.kt +++ b/app/src/test/java/app/closer/widget/TodayWidgetTest.kt @@ -15,7 +15,7 @@ class TodayWidgetTest { @Test fun `signed-out shows a neutral open prompt`() { - assertEquals("Open Closer", headlineFor(snap(signedIn = false))) + assertEquals("Open Closer Couples", headlineFor(snap(signedIn = false))) } @Test @@ -26,7 +26,7 @@ class TodayWidgetTest { @Test fun `app-lock hides the daily state behind a generic headline`() { val locked = headlineFor(snap(appLock = true, state = "BOTH_ANSWERED")) - assertEquals("Something's waiting in Closer", locked) + assertEquals("Something's waiting in Closer Couples", locked) // The specific state must NOT leak when the lock is on. assertFalse(locked.contains("reveal", ignoreCase = true)) } diff --git a/docs/NameChange.md b/docs/NameChange.md index 3bef7860..c0ff17c5 100644 --- a/docs/NameChange.md +++ b/docs/NameChange.md @@ -1,7 +1,15 @@ -# App rename: "Closer" → "Closer Couples" (store / marketing tier only) +# App rename: "Closer" → "Closer Couples" > Audit + decision record for renaming the product's public name to **"Closer Couples"**. > Status: **executed in repo.** Store-console changes remain external owner steps. +> +> **2026-07-09 extension — in-app prose now renamed.** The original pass was store/marketing only +> (in-app copy deliberately stayed "Closer"). Owner decision to extend: **in-app *prose* copy now uses +> "Closer Couples"** across Android + iOS (settings, pairing, invite share, widget headlines, paywall +> subhead/thank-you, age gate, privacy body, iOS settings/onboarding/pairing). The **launcher label and +> wordmark logos still stay "Closer"** (owner kept these — truncation under the icon, and the logo is a +> brand mark). See "What still stays 'Closer'" below. No technical identifiers touched; Android compiles + +> unit tests green (`TodayWidgetTest` assertions updated). ## Context @@ -17,12 +25,20 @@ After auditing every occurrence of the name across Android, iOS, images, and doc Renaming any of these breaks builds / Firebase / the Play listing: `namespace app.closer` · `applicationId closer.app` · package `app.closer.*` + `class CloserApp` · deep-link host `closer.app` + `closer://` scheme · Firebase `closer-app-22014` (`google-services.json`, `GoogleService-Info.plist`) · iOS `PRODUCT_BUNDLE_IDENTIFIER app.closer.iphone` + `bundleIdPrefix app.closer` + product/target/scheme `Closer` · SPM package `Closer` · Gradle `rootProject.name "Closer"` · resource filenames (`closer_launcher_*`, `ic_notification_closer`, `ic_launcher*`) · XML styles `Theme.Closer[.Splash]` · entitlement id `closer_premium` · crypto AAD constants. -## What does NOT change (per the chosen tier) +## What still stays "Closer" (after the 2026-07-09 prose extension) -- **Android launcher label** — `app/src/main/res/values/strings.xml` → `app_name` stays **"Closer"**. +- **Android launcher label** — `app/src/main/res/values/strings.xml` → `app_name` stays **"Closer"** (14-char "Closer Couples" truncates under the icon). - **iOS home-screen label** — `iphone/Closer/Info.plist` → `CFBundleDisplayName` stays **"Closer"** (`CFBundleName`/`PRODUCT_NAME` untouched). -- **All ~25 in-app copy strings** stay "Closer" (paywall, onboarding/age-gate, settings, widget, invite share text, privacy `strings.xml:95-96`, share card, iOS onboarding, etc.). -- **Wordmark logos** (onboarding, auth alt-text, widget, share card) stay "Closer" → their asserting tests stay green, untouched. +- **Wordmark logos** (onboarding `OnboardingScreen`/iOS `OnboardingViews`, auth alt-text `AuthVisuals`, `TodayWidget` header, `WeeklyRecapShareCard` drawText) stay "Closer" → the logo is a brand mark; their asserting tests (`FirstRunRenderSmokeTest` contentDescription, recap/widget tests) stay green. +- **Deliberate copy exceptions that stay "Closer":** the **"Grow Closer"** onboarding headline (intimacy pun, not the app name); the privacy-rotator slogan *"…so Closer cannot read them"* and iOS *"not even Closer's servers"* (entity references; the rotator line also has a hard 64-char display cap that "Closer Couples" would exceed — `CloserBrandCopyTest`); the iOS **"Settings > Closer"** help path (must match the kept `CFBundleDisplayName`); and the **"Closer Premium"** tier label (matches the RevenueCat entitlement display name). +- **Internal engineering/brand-system docs** using "Closer" as codename/design-system name (`DEVELOPMENT_LOG.md`, `SECURITY.md`, `Future.md`, `Engineering_Reference_Manual`, `Claude*.md`, brand-system docs). + +## In-app prose renamed to "Closer Couples" (2026-07-09) + +The ~26 user-facing strings where "Closer" names the app in a sentence now read "Closer Couples": + +- **Android (9 files):** `CloserCopy` (paywall subhead reworded to avoid "Couples…couples", thank-you, age-gate ×3), `InviteShareText`, `PairPromptScreen`, `SettingsScreen` (appearance + delete-account subtitles), `TodayWidget` (Open / "Something's waiting" headlines — header wordmark kept), `MainActivity` biometric prompt, `WeeklyRecapShareCard` caption (drawText wordmark kept), `strings.xml` (`privacy_no_export_body`, `privacy_rule_intro`). `TodayWidgetTest` exact-match assertions updated. +- **iOS (2 files):** `SettingsViews` (profile/account/data-export/legal/help/premium-eligibility/paywall subhead), `PairingViews` explore line. - **Internal engineering/brand-system docs** using "Closer" as codename/design-system name (`DEVELOPMENT_LOG.md`, `SECURITY.md`, `Future.md`, `Engineering_Reference_Manual`, the `Claude*.md` reports, `docs/brand/asset-system.md` "Closer Artwork Asset System", `docs/brand/generated-art/glyphs/README.md` "Closer G-set"). ## Changes to make (in-repo) diff --git a/iphone/Closer/Pairing/PairingViews.swift b/iphone/Closer/Pairing/PairingViews.swift index 1db869fe..dabb7f1a 100644 --- a/iphone/Closer/Pairing/PairingViews.swift +++ b/iphone/Closer/Pairing/PairingViews.swift @@ -28,7 +28,7 @@ struct PairPromptView: View { ) .closerPadding() - Text("You can keep exploring Closer, but shared reveals and partner activity start after your person joins.") + Text("You can keep exploring Closer Couples, but shared reveals and partner activity start after your person joins.") .font(CloserFont.footnote) .foregroundColor(.closerTextSecondary) .multilineTextAlignment(.center) diff --git a/iphone/Closer/Settings/SettingsViews.swift b/iphone/Closer/Settings/SettingsViews.swift index ca5ef75d..238f512d 100644 --- a/iphone/Closer/Settings/SettingsViews.swift +++ b/iphone/Closer/Settings/SettingsViews.swift @@ -28,7 +28,7 @@ struct SettingsView: View { SettingsProfileHeader( initials: initials, name: appState.currentUser?.displayName ?? "You", - detail: "Create an account to save your Closer space", + detail: "Create an account to save your Closer Couples space", imageUrl: appState.currentUser?.photoUrl ) } @@ -54,7 +54,7 @@ struct SettingsView: View { SettingsLinkRow( icon: "heart.fill", title: "Connected with \(partner.displayName.isEmpty ? "Partner" : partner.displayName)", - subtitle: "Your shared Closer space is active", + subtitle: "Your shared Closer Couples space is active", tint: .closerPrimary, imageUrl: partner.photoUrl, showsChevron: false @@ -124,7 +124,7 @@ struct SettingsView: View { SettingsLinkRow( icon: "square.and.arrow.up", title: "Export Data", - subtitle: "Download a copy of your Closer data" + subtitle: "Download a copy of your Closer Couples data" ) } .buttonStyle(.plain) @@ -150,7 +150,7 @@ struct SettingsView: View { SettingsLinkRow( icon: "doc.text", title: "Terms of Service", - subtitle: "The agreement for using Closer" + subtitle: "The agreement for using Closer Couples" ) } .buttonStyle(.plain) @@ -177,7 +177,7 @@ struct SettingsView: View { SettingsLinkRow( icon: "envelope", title: "Contact Us", - subtitle: "Get help from the Closer team" + subtitle: "Get help from the Closer Couples team" ) } .buttonStyle(.plain) @@ -207,7 +207,7 @@ struct SettingsView: View { SettingsLinkRow( icon: "trash", title: "Delete Account", - subtitle: "Permanently remove your Closer account", + subtitle: "Permanently remove your Closer Couples account", tint: .closerDanger, isDestructive: true, showsChevron: false @@ -407,7 +407,7 @@ struct DataExportView: View { .font(CloserFont.title2) .foregroundColor(.closerText) - Text("Download a copy of all your Closer data including answers, memories, and preferences.") + Text("Download a copy of all your Closer Couples data including answers, memories, and preferences.") .font(CloserFont.callout) .foregroundColor(.closerTextSecondary) .multilineTextAlignment(.center) @@ -580,7 +580,7 @@ struct PairingHelpView: View { BulletPoint("Codes expire after a set time — generate a new one if needed") BulletPoint("Each account can only be in one couple at a time") - BulletPoint("Both of you need a Closer account first") + BulletPoint("Both of you need a Closer Couples account first") } .closerPadding() } @@ -704,7 +704,7 @@ struct PaywallView: View { .foregroundColor(.closerText) .multilineTextAlignment(.center) - Text("Unlock everything Closer has built for couples.") + Text("Unlock everything Closer Couples has built for you.") .font(CloserFont.callout) .foregroundColor(.closerTextSecondary) .multilineTextAlignment(.center)