diff --git a/apple/BrandAssets.xcassets/Contents.json b/apple/BrandAssets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/apple/BrandAssets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apple/BrandAssets.xcassets/FastSharedIcon.imageset/Contents.json b/apple/BrandAssets.xcassets/FastSharedIcon.imageset/Contents.json new file mode 100644 index 0000000..2336ba2 --- /dev/null +++ b/apple/BrandAssets.xcassets/FastSharedIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icon@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apple/BrandAssets.xcassets/FastSharedIcon.imageset/icon@1x.png b/apple/BrandAssets.xcassets/FastSharedIcon.imageset/icon@1x.png new file mode 100644 index 0000000..46759e4 Binary files /dev/null and b/apple/BrandAssets.xcassets/FastSharedIcon.imageset/icon@1x.png differ diff --git a/apple/BrandAssets.xcassets/FastSharedIcon.imageset/icon@2x.png b/apple/BrandAssets.xcassets/FastSharedIcon.imageset/icon@2x.png new file mode 100644 index 0000000..080ad62 Binary files /dev/null and b/apple/BrandAssets.xcassets/FastSharedIcon.imageset/icon@2x.png differ diff --git a/apple/BrandAssets.xcassets/FastSharedIcon.imageset/icon@3x.png b/apple/BrandAssets.xcassets/FastSharedIcon.imageset/icon@3x.png new file mode 100644 index 0000000..f1d54f1 Binary files /dev/null and b/apple/BrandAssets.xcassets/FastSharedIcon.imageset/icon@3x.png differ diff --git a/apple/BrandAssets.xcassets/FastSharedMark.imageset/Contents.json b/apple/BrandAssets.xcassets/FastSharedMark.imageset/Contents.json new file mode 100644 index 0000000..fe9d05e --- /dev/null +++ b/apple/BrandAssets.xcassets/FastSharedMark.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "mark@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "mark@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "mark@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apple/BrandAssets.xcassets/FastSharedMark.imageset/mark@1x.png b/apple/BrandAssets.xcassets/FastSharedMark.imageset/mark@1x.png new file mode 100644 index 0000000..cc108f1 Binary files /dev/null and b/apple/BrandAssets.xcassets/FastSharedMark.imageset/mark@1x.png differ diff --git a/apple/BrandAssets.xcassets/FastSharedMark.imageset/mark@2x.png b/apple/BrandAssets.xcassets/FastSharedMark.imageset/mark@2x.png new file mode 100644 index 0000000..fe65c1f Binary files /dev/null and b/apple/BrandAssets.xcassets/FastSharedMark.imageset/mark@2x.png differ diff --git a/apple/BrandAssets.xcassets/FastSharedMark.imageset/mark@3x.png b/apple/BrandAssets.xcassets/FastSharedMark.imageset/mark@3x.png new file mode 100644 index 0000000..b2d0aef Binary files /dev/null and b/apple/BrandAssets.xcassets/FastSharedMark.imageset/mark@3x.png differ diff --git a/apple/FastShared.xcodeproj/project.pbxproj b/apple/FastShared.xcodeproj/project.pbxproj index fec79e6..485d2aa 100644 --- a/apple/FastShared.xcodeproj/project.pbxproj +++ b/apple/FastShared.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 333DC1C558F01A914C916FC9 /* BrandLockup.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA90874696B4F367CD6E19A9 /* BrandLockup.swift */; }; 3499E7DF9879858A2D659BEB /* FastSharedApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A90C315E4E42DAF559921EF /* FastSharedApp.swift */; }; 36AD6810F21FDF82745E3372 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9F98BBB924186324802FF0 /* OnboardingView.swift */; }; + 3B9A13A56DD62A1B43DCA79E /* BrandAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A0AD72942AB4148EB1A56AA7 /* BrandAssets.xcassets */; }; 3C769382012F6EAE207DA9E8 /* MacMenuBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B053BEF38363D40385EBB074 /* MacMenuBarView.swift */; }; 3D2894115A0C33074CCE1DD0 /* UploadServiceLocator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E8E3BF7E89828611589104 /* UploadServiceLocator.swift */; }; 3EE6650FD00693859C9C5E60 /* PaywallTierCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14D78B3635A5613DE5C407BE /* PaywallTierCard.swift */; }; @@ -34,6 +35,7 @@ 761F9E35C73774C1AEBE12FB /* BrandPalette+Redesign.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E51E9CDA15F39DD65F1B780 /* BrandPalette+Redesign.swift */; }; 793E126371555702F56E9427 /* ExpiryTier.swift in Sources */ = {isa = PBXBuildFile; fileRef = A784E8C45E6D1B42C8023031 /* ExpiryTier.swift */; }; 84FF9E49748CEA8AAA0FEA99 /* FastSharedCore in Frameworks */ = {isa = PBXBuildFile; productRef = C2CBC13B53DE31A50DB265C2 /* FastSharedCore */; }; + 896D8B129024E22D14EF11D0 /* BrandAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A0AD72942AB4148EB1A56AA7 /* BrandAssets.xcassets */; }; 8EE5F08B37A75483180A48EC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 5C97B518FAD14B7B537E32ED /* PrivacyInfo.xcprivacy */; }; 9100732A0A506E8744505E7B /* FastSharedLiveActivity.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 988DD6EAF3C4A04479959EE7 /* FastSharedLiveActivity.appex */; platformFilters = (ios, ); settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 96154FACD6E84B2142CC1241 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F3145FBF97AEBC20F81E2B6 /* SignInView.swift */; }; @@ -45,6 +47,7 @@ B344D60EC4D57DB7A482CD56 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 4EC203896546B3EFAB76E7A4 /* PrivacyInfo.xcprivacy */; }; B507DC7676BADCEA4146D08A /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36556BBD8D23476FEC5103D0 /* LibraryView.swift */; }; B78A3F22F722FF3F5CC7C428 /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F0CCBFF2A59244E0D7C422 /* SplashView.swift */; }; + B8F27A6F5DA84DD133F4029F /* BrandAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A0AD72942AB4148EB1A56AA7 /* BrandAssets.xcassets */; }; BE7E9F8527D7E2680B50039D /* FileGlyph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A6D87C839A504A9F3CE8BE /* FileGlyph.swift */; }; C0FD4798F5836A2B38F2205F /* FastSharedUploadLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652830B367D5AF398175B07E /* FastSharedUploadLiveActivity.swift */; }; C258F5ACFB74C3090F857B4F /* UploadCoordinatorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3153F01945E303D20D1F29E0 /* UploadCoordinatorViewModel.swift */; }; @@ -145,6 +148,7 @@ 97AD5286ABB21E895C3AE32E /* MacDropHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacDropHandler.swift; sourceTree = ""; }; 988DD6EAF3C4A04479959EE7 /* FastSharedLiveActivity.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = FastSharedLiveActivity.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 9955F5B072E458EFAAEC2814 /* MacCompanionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacCompanionView.swift; sourceTree = ""; }; + A0AD72942AB4148EB1A56AA7 /* BrandAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = BrandAssets.xcassets; sourceTree = ""; }; A784E8C45E6D1B42C8023031 /* ExpiryTier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpiryTier.swift; sourceTree = ""; }; B053BEF38363D40385EBB074 /* MacMenuBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacMenuBarView.swift; sourceTree = ""; }; B11B370818419C58E3A881BB /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; @@ -200,6 +204,7 @@ 1E4906F8F703B17F99EB4108 /* FastSharedLiveActivity */, D05F942AF656E5F9F23D6124 /* FastSharedShareExt */, E4E058FC2C390CBC0EBAB91D /* Packages */, + A0AD72942AB4148EB1A56AA7 /* BrandAssets.xcassets */, 332150A0273107F7D84B42E8 /* Products */, ); sourceTree = ""; @@ -503,6 +508,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3B9A13A56DD62A1B43DCA79E /* BrandAssets.xcassets in Resources */, B344D60EC4D57DB7A482CD56 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -512,6 +518,7 @@ buildActionMask = 2147483647; files = ( 6A73ABB1BE0CCE55F605302A /* Assets.xcassets in Resources */, + B8F27A6F5DA84DD133F4029F /* BrandAssets.xcassets in Resources */, 70104ECD469C6AA74BAEC127 /* FastShared.storekit in Resources */, 8EE5F08B37A75483180A48EC /* PrivacyInfo.xcprivacy in Resources */, ); @@ -521,6 +528,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 896D8B129024E22D14EF11D0 /* BrandAssets.xcassets in Resources */, E18BB847D70C19CA5CA2D39A /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/appstore-1024.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/appstore-1024.png index c7f6d20..14f4ef7 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/appstore-1024.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/appstore-1024.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-1024-dark.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-1024-dark.png index 4ebb9ed..14f4ef7 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-1024-dark.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-1024-dark.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-1024-tinted.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-1024-tinted.png index 63969b1..14f4ef7 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-1024-tinted.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-1024-tinted.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-20@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-20@2x.png index 25f34c4..1af59b1 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-20@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-20@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-20@3x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-20@3x.png index 725b23a..22d622c 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-20@3x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-20@3x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-29@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-29@2x.png index db890a8..8330866 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-29@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-29@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-29@3x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-29@3x.png index ba04994..af0f467 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-29@3x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-29@3x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-40@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-40@2x.png index 4e29070..97cd8e0 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-40@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-40@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-40@3x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-40@3x.png index 8832865..99946a9 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-40@3x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-40@3x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-60@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-60@2x.png index 1ec020b..99946a9 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-60@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-60@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-60@3x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-60@3x.png index f5bbcf0..9cf81da 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-60@3x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ios-60@3x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-20@1x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-20@1x.png index aee1164..55194c3 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-20@1x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-20@1x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-20@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-20@2x.png index e69304c..1af59b1 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-20@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-20@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-29@1x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-29@1x.png index f7506f7..00d4be3 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-29@1x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-29@1x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-29@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-29@2x.png index d37dff7..8330866 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-29@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-29@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-40@1x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-40@1x.png index e69304c..1af59b1 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-40@1x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-40@1x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-40@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-40@2x.png index b7fd415..97cd8e0 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-40@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-40@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-76@1x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-76@1x.png index 3f6263a..f3ab8fb 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-76@1x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-76@1x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-76@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-76@2x.png index 8d81d60..f920a2a 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-76@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-76@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-83.5@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-83.5@2x.png index 324ab1c..1630d94 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-83.5@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/ipad-83.5@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-128@1x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-128@1x.png index c6051cc..a24344b 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-128@1x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-128@1x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-128@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-128@2x.png index 448fc76..e722930 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-128@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-128@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-16@1x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-16@1x.png index bece372..0b2ec02 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-16@1x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-16@1x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-16@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-16@2x.png index 6fe8ceb..7bbc42c 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-16@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-16@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-256@1x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-256@1x.png index 448fc76..e722930 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-256@1x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-256@1x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-256@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-256@2x.png index 88517be..58fec84 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-256@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-256@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-32@1x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-32@1x.png index 6fe8ceb..7bbc42c 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-32@1x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-32@1x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-32@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-32@2x.png index 87dbc30..a80dd26 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-32@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-32@2x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-512@1x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-512@1x.png index 88517be..58fec84 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-512@1x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-512@1x.png differ diff --git a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-512@2x.png b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-512@2x.png index c7f6d20..14f4ef7 100644 Binary files a/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-512@2x.png and b/apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/mac-512@2x.png differ diff --git a/apple/FastSharedApp/Components/BrandLockup.swift b/apple/FastSharedApp/Components/BrandLockup.swift index 7a21351..1a5922d 100644 --- a/apple/FastSharedApp/Components/BrandLockup.swift +++ b/apple/FastSharedApp/Components/BrandLockup.swift @@ -1,16 +1,14 @@ import SwiftUI import FastSharedCore -/// BrandLockup — horizontal mark + wordmark, per `design/screens.jsx` → `BrandLockup`. +/// BrandLockup - app icon tile + visual wordmark. /// -/// The canonical in-app brand signature: a bare `PlaneArcMark` followed by the -/// wordmark `fastshared` with a single amber period as the only ornament. +/// Textual metadata uses "FastShared"; this component renders the lowercase +/// brand glyph used in app chrome. /// /// BrandLockup(markSize: 26, textSize: 15) // Hub header /// BrandLockup(markSize: 20, textSize: 13) // Share-ext sheet header /// -/// The mark stays unframed (`framed: false`) so the lockup reads as a single -/// wordmark glyph — frames are reserved for hero moments (onboarding, success). struct BrandLockup: View { /// Edge length of the leading `PlaneArcMark` in points. var markSize: CGFloat = 22 @@ -28,10 +26,8 @@ struct BrandLockup: View { } var body: some View { - // Brand accent unified on violet. PlaneArcMark default is violet - // (BrandPalette.accent.hot); the wordmark period matches. HStack(spacing: 8) { - PlaneArcMark(size: markSize) + PlaneArcMark(size: markSize, framed: true) .accessibilityHidden(true) ( @@ -41,9 +37,7 @@ struct BrandLockup: View { .foregroundStyle(BrandPalette.accentHot) ) .font(.system(size: textSize, weight: .bold)) - // -0.02em tracking at this size rounds to a fraction of a point — - // we approximate with a small negative tracking value. - .tracking(-textSize * 0.02) + .tracking(0) } .accessibilityElement(children: .combine) .accessibilityLabel("fastshared") diff --git a/apple/FastSharedApp/Components/PlaneArcMark.swift b/apple/FastSharedApp/Components/PlaneArcMark.swift index c8cf069..8dcc808 100644 --- a/apple/FastSharedApp/Components/PlaneArcMark.swift +++ b/apple/FastSharedApp/Components/PlaneArcMark.swift @@ -1,185 +1,92 @@ import SwiftUI import FastSharedCore -/// PlaneArc — the v3 FastShared brand mark. +/// Raster-backed FastShared brand mark. /// -/// Ported 1:1 from `project/design/plane-arc.jsx` → `PA_Refined` (the variant -/// the team shipped). A single gradient arc (`fade@0 → hot@0.5 → soft@1`) in a -/// 140-unit viewBox with a dart-shaped plane tucked into its terminus (81, 66). -/// -/// The mark always renders inside a `size × size` frame and maps the -/// 140-unit design coordinates onto that frame via `GeometryReader`-driven -/// scaling, so a 22pt chip and a 260pt hero draw from the same geometry. -/// -/// Parameters honour the redesign brief: -/// -/// - `size`: outer square edge length in points. -/// - `accent`: fallback stroke color (also used for the ambient halo); -/// defaults to the semantic `BrandPalette.accent.hot` -/// (violet under the redesign). -/// - `gradient`: the arc stroke gradient; defaults to `.brandArc` -/// (fade→hot→soft) which reads the current `accent`. -/// - `planeColor`: dart fill; defaults to `BrandPalette.text` (milk). -/// - `framed`: when true, paints the 140x140 surface tile (surface0 fill, -/// `lineStrong` stroke, 32pt rounded corner) behind the mark. -/// When false, mark only — transparent background. -/// - `ambient`: optional radial halo for hero moments. -/// - `animated`: when true, plays the brand-intro motion on first appear. -/// Honours `@Environment(\.accessibilityReduceMotion)`. -/// -/// Swift-strict-concurrency note: this view is a pure value-type `View`; all -/// state is `@State` on the main actor. Animations run via `withAnimation` -/// on-appear — no custom timers / actors required. +/// The v2 logo depends on soft glow and shaded paper-plane detail, so the +/// canonical rendering is the shared asset catalog instead of hand-drawn +/// SwiftUI paths. The public initializer surface is intentionally kept close +/// to the old `PlaneArcMark` so existing call sites do not churn. struct PlaneArcMark: View { - /// Outer square edge length in points. var size: CGFloat = 140 - /// Fallback accent color for the ambient halo + default gradient fallback. var accent: Color = BrandPalette.accent.hot - /// Arc stroke gradient. Defaults to `.brandArc` (fade→hot→soft on the - /// current `BrandPalette.accent`). Pass a custom gradient to re-tint. var gradient: Gradient = .brandArc - /// Dart fill color. Defaults to `BrandPalette.text` (milk on ink). var planeColor: Color = BrandPalette.text - /// When true, paints the 140x140 surface tile (rx 32) behind the mark. var framed: Bool = false - /// Optional background override for the frame. Default: `surface0` (dark). var background: Color? = nil - /// When true, paints an amber radial glow bleeding past the mark edges. var ambient: Bool = false - /// When true, plays the brand-intro motion on first appear. var animated: Bool = false - @State private var arcProgress: CGFloat = 0 - @State private var planeIn: Bool = false - @Environment(\.accessibilityReduceMotion) private var reduceMotion + @State private var visible: Bool = false - var body: some View { - // viewBox-140 → local scale - let corner = size * (32.0 / 140.0) - let arcStrokeWidth = size * (9.0 / 140.0) + private var assetName: String { + framed ? "FastSharedIcon" : "FastSharedMark" + } + + private var shouldAnimate: Bool { + animated && !reduceMotion + } + var body: some View { ZStack { - // Ambient halo — radial-gradient accent fade, inset -35% around the mark. - // Matches plane-arc.jsx `radial-gradient(circle, ${t.hot}35 0%, transparent 65%)` - // with a 6pt blur. if ambient { Circle() .fill( RadialGradient( - colors: [accent.opacity(0.21), .clear], + colors: [ + accent.opacity(0.28), + Color.white.opacity(0.10), + .clear, + ], center: .center, startRadius: 0, - endRadius: size * 0.82 + endRadius: size * 0.92 ) ) - .frame(width: size * 1.7, height: size * 1.7) - .blur(radius: 6) + .frame(width: size * 1.72, height: size * 1.72) + .blur(radius: max(6, size * 0.08)) + .opacity(shouldAnimate ? (visible ? 1 : 0) : 1) + .scaleEffect(shouldAnimate ? (visible ? 1 : 0.82) : 1) .allowsHitTesting(false) .accessibilityHidden(true) } - // Frame - if framed { - let bg = background ?? BrandPalette.surface0 - RoundedRectangle(cornerRadius: corner, style: .continuous) - .fill(bg) - .overlay( - RoundedRectangle(cornerRadius: corner, style: .continuous) - .stroke(BrandPalette.lineStrong, lineWidth: 1) - ) + if framed, let background { + RoundedRectangle(cornerRadius: size * 0.22, style: .continuous) + .fill(background) .frame(width: size, height: size) } - // Arc — endpoint tucks under the plane tail. The butt cap prevents a 1px violet halo behind the dart. - // Gradient: fade@0 → hot@0.5 → soft@1 along bottom-left → top-right. - ArcPath() - .trim(from: 0, to: animated && !reduceMotion ? arcProgress : 1) - .stroke( - LinearGradient( - gradient: gradient, - startPoint: .bottomLeading, - endPoint: .topTrailing - ), - style: StrokeStyle(lineWidth: arcStrokeWidth, lineCap: .butt) - ) - .frame(width: size, height: size) - - // Plane dart — tucked into the terminus (81, 66) scaled ×1.1 in viewBox-140 units. - planeDart + Image(assetName) + .resizable() + .interpolation(.high) + .antialiased(true) + .scaledToFit() .frame(width: size, height: size) - .opacity(animated && !reduceMotion ? (planeIn ? 1 : 0) : 1) - .offset( - x: animated && !reduceMotion ? (planeIn ? 0 : -size * 0.05) : 0, - y: animated && !reduceMotion ? (planeIn ? 0 : size * 0.05) : 0 - ) + .clipShape(RoundedRectangle(cornerRadius: framed ? size * 0.22 : 0, style: .continuous)) + .shadow(color: framed ? Color.black.opacity(0.16) : .clear, radius: framed ? size * 0.12 : 0, y: framed ? size * 0.05 : 0) + .opacity(shouldAnimate ? (visible ? 1 : 0) : 1) + .scaleEffect(shouldAnimate ? (visible ? 1 : 0.94) : 1) + .blur(radius: shouldAnimate ? (visible ? 0 : 6) : 0) } .frame(width: size, height: size) .onAppear { - guard animated, !reduceMotion else { return } - withAnimation(.easeOut(duration: 1.1)) { - arcProgress = 1 + guard shouldAnimate else { + visible = true + return } - withAnimation(.easeOut(duration: 0.5).delay(0.85)) { - planeIn = true + withAnimation(.easeOut(duration: 0.72)) { + visible = true } } .accessibilityLabel("fastshared mark") } - - /// Dart plane — matches the `planeDart` path in plane-arc.jsx scaled by 1.1 - /// and translated to (81, 66) in the 140-unit viewBox. Includes the thin - /// 35%-opacity secondary stroke along the belly seam (M 4 4 L 14 10). - private var planeDart: some View { - GeometryReader { geo in - let s = geo.size.width / 140.0 - let transform = CGAffineTransform(translationX: 81 * s, y: 66 * s) - .scaledBy(x: 1.1 * s, y: 1.1 * s) - - ZStack { - Path { p in - p.move(to: CGPoint(x: 0, y: 0).applying(transform)) - p.addLine(to: CGPoint(x: 22, y: -26).applying(transform)) - p.addLine(to: CGPoint(x: 14, y: 10).applying(transform)) - p.addLine(to: CGPoint(x: 4, y: 4).applying(transform)) - p.addLine(to: CGPoint(x: 0, y: 18).applying(transform)) - p.addLine(to: CGPoint(x: -8, y: 4).applying(transform)) - p.closeSubpath() - } - .fill(planeColor) - - Path { p in - p.move(to: CGPoint(x: 4, y: 4).applying(transform)) - p.addLine(to: CGPoint(x: 14, y: 10).applying(transform)) - } - .stroke(planeColor.opacity(0.35), lineWidth: 1) - } - } - } -} - -/// The arc path from `PA_Refined`, trimmed to tuck under the plane tail in a 140-unit viewBox. -struct ArcPath: Shape { - func path(in rect: CGRect) -> Path { - let s = rect.width / 140.0 - var p = Path() - p.move(to: CGPoint(x: 26 * s, y: 110 * s)) - p.addQuadCurve( - to: CGPoint(x: 80 * s, y: 72 * s), - control: CGPoint(x: 60 * s, y: 98 * s) - ) - return p - } } -// MARK: - Gradient presets +// MARK: - Compatibility gradient presets extension Gradient { - /// Signature brand arc gradient: `fade@0 → hot@0.5 → soft@1`, reading the - /// semantic `BrandPalette.accent` (violet in the redesign). - /// - /// This is the gradient the mark uses by default. Pass a custom `Gradient` - /// to re-tint without changing the accent globally. static var brandArc: Gradient { let a = BrandPalette.accent return Gradient(stops: [ @@ -189,7 +96,6 @@ extension Gradient { ]) } - /// Amber variant for consumers that want the original warm brand look. static var brandArcAmber: Gradient { let a = BrandPalette.amberAccent return Gradient(stops: [ @@ -202,28 +108,16 @@ extension Gradient { // MARK: - Lockup -/// Horizontal lockup — mark + wordmark, per `plane-arc.jsx` → `LockupHoriz`. -/// -/// Renders `PlaneArcMark` at `size` and the wordmark `fastshared.` at a -/// scaled type size + tracking so the pair reads as a single glyph at any -/// presentation scale. The period is painted in the current accent hot. struct PlaneArcLockupHorizontal: View { - /// Edge length of the leading mark. The wordmark scales proportionally - /// (`size × 0.62`) and the gap is `size × 0.27` (≈14pt at the 52pt base). var size: CGFloat = 52 - /// Custom accent override — defaults to the semantic `BrandPalette.accent`. var accent: BrandPalette.Accent = BrandPalette.accent var body: some View { let gap = size * 0.27 let wordSize = size * 0.62 HStack(spacing: gap) { - PlaneArcMark( - size: size, - accent: accent.hot, - gradient: .brandArc - ) - .accessibilityHidden(true) + PlaneArcMark(size: size, accent: accent.hot, framed: true) + .accessibilityHidden(true) LockupWordmark(size: wordSize, accent: accent.hot) } @@ -232,10 +126,6 @@ struct PlaneArcLockupHorizontal: View { } } -/// The `fastshared.` wordmark — system rounded-bold (on-device stand-in for -/// Bricolage Grotesque) with tracking scaled to `-0.035 * size` so visual -/// density stays constant across sizes. The trailing period is painted in -/// the current accent hot. struct LockupWordmark: View { var size: CGFloat = 32 var accent: Color = BrandPalette.accent.hot @@ -249,37 +139,18 @@ struct LockupWordmark: View { .foregroundStyle(accent) ) .font(.system(size: size, weight: .bold, design: .rounded)) - .tracking(-size * 0.035) + .tracking(0) } } -#Preview("Sizes — dark") { +#Preview("Sizes") { VStack(spacing: 32) { PlaneArcMark(size: 64) PlaneArcMark(size: 96, framed: true) - PlaneArcMark(size: 140, framed: true, ambient: true) + PlaneArcMark(size: 140, framed: true, ambient: true, animated: true) PlaneArcLockupHorizontal(size: 52) } .padding(40) .background(BrandPalette.ground) .preferredColorScheme(.dark) } - -#Preview("Sizes — light") { - VStack(spacing: 32) { - PlaneArcMark( - size: 64, - planeColor: Color(red: 0.03, green: 0.01, blue: 0.09) - ) - PlaneArcMark( - size: 96, - planeColor: Color(red: 0.03, green: 0.01, blue: 0.09), - framed: true, - background: Color.white - ) - PlaneArcLockupHorizontal(size: 52) - } - .padding(40) - .background(Color(red: 0.96, green: 0.95, blue: 0.92)) - .preferredColorScheme(.light) -} diff --git a/apple/FastSharedApp/Scenes/SplashView.swift b/apple/FastSharedApp/Scenes/SplashView.swift index 5c3c181..82b7e2d 100644 --- a/apple/FastSharedApp/Scenes/SplashView.swift +++ b/apple/FastSharedApp/Scenes/SplashView.swift @@ -1,67 +1,51 @@ import SwiftUI import FastSharedCore -/// Launch splash — calm, elegant brand reveal. Variant A from the Splash -/// Preview design package (2026-04-19): mark only, no wordmark. +/// Launch splash for the raster-backed v2 brand mark. /// -/// Timeline (1.8s total): -/// 0.00 – 0.50s ambient glow fades up (easeOut) -/// 0.10 – 1.00s arc draws (easeOut, trim 0 → 1) -/// 0.55 – 1.30s plane slides along arc (spring, response 0.75) -/// 0.55 – 0.80s plane fades in (easeIn) -/// 1.60 – 1.80s whole mark fades out (easeInOut), then onComplete fires -/// -/// Reduced-motion behavior: skips the timeline, holds the end frame for -/// ~0.6s, then fades out and reports complete. The mark is still seen but -/// without the staged reveal — vestibular-safe. -/// -/// The caller owns what happens after the splash; we just animate alpha -/// and report `onComplete` at the 1.8s mark. +/// The old stroke-draw timeline does not match the new shaded/glow logo. +/// This reveal keeps motion simple: ambient light, mark scale/fade, then exit. struct SplashView: View { var onComplete: () -> Void = {} @Environment(\.colorScheme) private var colorScheme @Environment(\.accessibilityReduceMotion) private var reduceMotion - @State private var arcProgress: CGFloat = 0 - @State private var planeProgress: CGFloat = 0 @State private var glowOpacity: Double = 0 - @State private var planeOpacity: Double = 0 + @State private var markOpacity: Double = 0 + @State private var markScale: CGFloat = 0.94 + @State private var markBlur: CGFloat = 8 @State private var rootOpacity: Double = 1 - private let size: CGFloat = 140 + private let size: CGFloat = 148 var body: some View { ZStack { - // Splash background — HIG semantic, adapts to light/dark - // without hard-coding brand ground. Keeps the radial glow + - // mark as the only branded elements. Color.sysBackground .ignoresSafeArea() ZStack { - // Ambient amber glow behind the mark — warmer on light, cooler - // on dark to keep contrast with the night ground. Circle() - .fill(RadialGradient( - colors: [ - FriendlyPalette.accentHot.opacity(colorScheme == .dark ? 0.18 : 0.32), - .clear, - ], - center: .center, - startRadius: 0, - endRadius: size * 1.2)) - .frame(width: size * 2.4, height: size * 2.4) - .blur(radius: 20) + .fill( + RadialGradient( + colors: [ + FriendlyPalette.accentHot.opacity(colorScheme == .dark ? 0.24 : 0.34), + Color.white.opacity(colorScheme == .dark ? 0.06 : 0.18), + .clear, + ], + center: .center, + startRadius: 0, + endRadius: size * 1.25 + ) + ) + .frame(width: size * 2.5, height: size * 2.5) + .blur(radius: 22) .opacity(glowOpacity) .allowsHitTesting(false) - AnimatedPlaneArc( - size: size, - arcProgress: arcProgress, - planeProgress: planeProgress, - planeOpacity: planeOpacity, - planeColor: Color.sysLabel - ) + PlaneArcMark(size: size) + .opacity(markOpacity) + .scaleEffect(markScale) + .blur(radius: markBlur) } .opacity(rootOpacity) } @@ -70,14 +54,12 @@ struct SplashView: View { .accessibilityLabel("FastShared") } - // MARK: - Timeline - private func runTimeline() { guard !reduceMotion else { - arcProgress = 1 - planeProgress = 1 glowOpacity = 1 - planeOpacity = 1 + markOpacity = 1 + markScale = 1 + markBlur = 0 DispatchQueue.main.asyncAfter(deadline: .now() + 0.60) { withAnimation(.easeInOut(duration: 0.20)) { rootOpacity = 0 } } @@ -87,17 +69,14 @@ struct SplashView: View { return } - withAnimation(.easeOut(duration: 0.5)) { glowOpacity = 1 } - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.10) { - withAnimation(.easeOut(duration: 0.9)) { arcProgress = 1 } + withAnimation(.easeOut(duration: 0.45)) { + glowOpacity = 1 } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.55) { - withAnimation(.easeIn(duration: 0.25)) { planeOpacity = 1 } - withAnimation(.spring(response: 0.75, dampingFraction: 0.78)) { - planeProgress = 1 - } + withAnimation(.easeOut(duration: 0.72).delay(0.08)) { + markOpacity = 1 + markScale = 1 + markBlur = 0 } DispatchQueue.main.asyncAfter(deadline: .now() + 1.60) { @@ -109,118 +88,12 @@ struct SplashView: View { } } -// MARK: - Animated mark primitive - -/// Standalone animated version of PlaneArcMark — takes progress values so the -/// splash can orchestrate the draw + slide sequence externally without -/// pulling in the full PlaneArcMark component (which has its own intro). -private struct AnimatedPlaneArc: View { - let size: CGFloat - let arcProgress: CGFloat // 0…1 — stroke trim - let planeProgress: CGFloat // 0…1 — position along the arc - let planeOpacity: Double - let planeColor: Color - - var body: some View { - GeometryReader { geo in - let scale = geo.size.width / 140 - let s: (CGFloat) -> CGFloat = { $0 * scale } - let k: CGFloat = 1.1 * scale - - // Quadratic curve coordinates match PlaneArcMark.swift so the - // splash mark and the static brand lockup share visual identity. - let start = CGPoint(x: s(26), y: s(110)) - let control = CGPoint(x: s(60), y: s(98)) - let tip = CGPoint(x: s(80), y: s(72)) - let tuckOffset = CGPoint(x: s(1) * planeProgress, y: -s(6) * planeProgress) - - ZStack { - Path { p in - p.move(to: start) - p.addQuadCurve(to: tip, control: control) - } - .trim(from: 0, to: arcProgress) - .stroke( - LinearGradient( - stops: [ - .init(color: FriendlyPalette.accentFade.opacity(0), location: 0), - .init(color: FriendlyPalette.accentHot.opacity(0.95), location: 0.5), - .init(color: Color(red: 1.0, green: 0x5b / 255.0, blue: 0x1a / 255.0), location: 1.0), - ], - startPoint: .bottomLeading, - endPoint: .topTrailing), - style: StrokeStyle(lineWidth: s(9), lineCap: .butt)) - - let pos = quadPoint(start: start, control: control, end: tip, t: planeProgress) - let tan = quadTangent(start: start, control: control, end: tip, t: planeProgress) - let angle = atan2(tan.dy, tan.dx) - - PlaneDart(k: k, color: planeColor) - .rotationEffect(.radians(Double(angle - referenceAngle(start: start, end: tip))), - anchor: .topLeading) - .offset(x: pos.x + tuckOffset.x, y: pos.y + tuckOffset.y) - .opacity(planeOpacity) - } - } - .frame(width: size, height: size) - } - - /// PlaneArcMark's dart is drawn with its own internal angle, so we - /// normalize by the straight-line angle start→end; the rotation applied - /// is the delta between the live tangent and that baseline. - private func referenceAngle(start: CGPoint, end: CGPoint) -> CGFloat { - atan2(end.y - start.y, end.x - start.x) - } -} - -private struct PlaneDart: View { - let k: CGFloat - let color: Color - - var body: some View { - ZStack { - Path { p in - p.move(to: .zero) - p.addLine(to: CGPoint(x: 22 * k, y: -26 * k)) - p.addLine(to: CGPoint(x: 14 * k, y: 10 * k)) - p.addLine(to: CGPoint(x: 4 * k, y: 4 * k)) - p.addLine(to: CGPoint(x: 0, y: 18 * k)) - p.addLine(to: CGPoint(x: -8 * k, y: 4 * k)) - p.closeSubpath() - } - .fill(color) - - Path { p in - p.move(to: CGPoint(x: 4 * k, y: 4 * k)) - p.addLine(to: CGPoint(x: 14 * k, y: 10 * k)) - } - .stroke(color.opacity(0.35), lineWidth: 1) - } - } -} - -// MARK: - Quadratic Bézier helpers - -private func quadPoint(start: CGPoint, control: CGPoint, end: CGPoint, t: CGFloat) -> CGPoint { - let u = 1 - t - let x = u*u*start.x + 2*u*t*control.x + t*t*end.x - let y = u*u*start.y + 2*u*t*control.y + t*t*end.y - return CGPoint(x: x, y: y) -} - -private func quadTangent(start: CGPoint, control: CGPoint, end: CGPoint, t: CGFloat) -> CGVector { - let u = 1 - t - let dx = 2*u*(control.x - start.x) + 2*t*(end.x - control.x) - let dy = 2*u*(control.y - start.y) + 2*t*(end.y - control.y) - return CGVector(dx: dx, dy: dy) -} - -#Preview("Splash — light") { +#Preview("Splash - light") { SplashView() .preferredColorScheme(.light) } -#Preview("Splash — dark") { +#Preview("Splash - dark") { SplashView() .preferredColorScheme(.dark) } diff --git a/apple/FastSharedLiveActivity/FastSharedUploadLiveActivity.swift b/apple/FastSharedLiveActivity/FastSharedUploadLiveActivity.swift index e563732..dbdbe4d 100644 --- a/apple/FastSharedLiveActivity/FastSharedUploadLiveActivity.swift +++ b/apple/FastSharedLiveActivity/FastSharedUploadLiveActivity.swift @@ -339,9 +339,7 @@ private struct CompactTrailingView: View { .font(.system(size: 14)) case .completed: - Image(systemName: "paperplane.fill") - .font(.system(size: 12, weight: .medium)) - .foregroundStyle(FriendlyPalette.successGreen) + LAPlaneArc(size: 16) case .failed: Text("!") @@ -357,13 +355,9 @@ private struct MinimalView: View { var body: some View { switch state.phase { case .uploading: - Image(systemName: "paperplane.fill") - .font(.system(size: 14, weight: .medium)) - .foregroundStyle(FriendlyPalette.accentHot) + LAPlaneArc(size: 18) case .completed: - Image(systemName: "paperplane.fill") - .font(.system(size: 14, weight: .medium)) - .foregroundStyle(FriendlyPalette.successGreen) + LAPlaneArc(size: 18) case .failed: Image(systemName: "exclamationmark.triangle.fill") .font(.system(size: 13, weight: .medium)) @@ -422,72 +416,23 @@ private struct LockFileChip: View { } } -/// Plane+Arc mark — inline re-declaration for the Live Activity target -/// (widget extensions can't link the app-target `PlaneArcMark`). +/// Raster-backed brand mark for Live Activity surfaces. private struct LAPlaneArc: View { var size: CGFloat = 44 + var framed: Bool = false var body: some View { - let a = BrandPalette.violetAccent - let arcStrokeWidth = size * (9.0 / 140.0) - - ZStack { - LAArcPath() - .stroke( - LinearGradient( - stops: [ - .init(color: a.fade.opacity(0), location: 0), - .init(color: a.hot.opacity(0.95), location: 0.5), - .init(color: a.soft, location: 1), - ], - startPoint: .bottomLeading, - endPoint: .topTrailing - ), - style: StrokeStyle(lineWidth: arcStrokeWidth, lineCap: .butt) - ) - .frame(width: size, height: size) - - GeometryReader { geo in - let s = geo.size.width / 140.0 - let transform = CGAffineTransform(translationX: 81 * s, y: 66 * s) - .scaledBy(x: 1.1 * s, y: 1.1 * s) - ZStack { - Path { p in - p.move(to: CGPoint(x: 0, y: 0).applying(transform)) - p.addLine(to: CGPoint(x: 22, y: -26).applying(transform)) - p.addLine(to: CGPoint(x: 14, y: 10).applying(transform)) - p.addLine(to: CGPoint(x: 4, y: 4).applying(transform)) - p.addLine(to: CGPoint(x: 0, y: 18).applying(transform)) - p.addLine(to: CGPoint(x: -8, y: 4).applying(transform)) - p.closeSubpath() - } - .fill(BrandPalette.text) - Path { p in - p.move(to: CGPoint(x: 4, y: 4).applying(transform)) - p.addLine(to: CGPoint(x: 14, y: 10).applying(transform)) - } - .stroke(BrandPalette.text.opacity(0.35), lineWidth: 1) - } - } + Image(framed ? "FastSharedIcon" : "FastSharedMark") + .resizable() + .interpolation(.high) + .antialiased(true) + .scaledToFit() .frame(width: size, height: size) - } + .clipShape(RoundedRectangle(cornerRadius: framed ? size * 0.22 : 0, style: .continuous)) .frame(width: size, height: size) } } -private struct LAArcPath: Shape { - func path(in rect: CGRect) -> Path { - let s = rect.width / 140.0 - var p = Path() - p.move(to: CGPoint(x: 26 * s, y: 110 * s)) - p.addQuadCurve( - to: CGPoint(x: 80 * s, y: 72 * s), - control: CGPoint(x: 60 * s, y: 98 * s) - ) - return p - } -} - /// Retention badge pill — violet accentHot background. private struct LARetentionBadge: View { let text: String diff --git a/apple/FastSharedShareExt/ShareRootView.swift b/apple/FastSharedShareExt/ShareRootView.swift index 0887f19..8b1ca14 100644 --- a/apple/FastSharedShareExt/ShareRootView.swift +++ b/apple/FastSharedShareExt/ShareRootView.swift @@ -237,24 +237,15 @@ private struct SheetRing: View { } } -/// Inline plane-arc brand mark for the share extension. -/// -/// Mirrors `apple/FastSharedApp/Components/PlaneArcMark.swift` — but this -/// target can't import Components, so we re-declare the v3 `PlaneArc` render -/// inline. Same viewBox-140 coordinates, same dart path, same gradient stops. +/// Inline raster-backed brand mark for the share extension. private struct SheetPlaneArc: View { var size: CGFloat = 80 var framed: Bool = false var ambient: Bool = false - @Environment(\.colorScheme) private var colorScheme - - private var planeColor: Color { sysLabel } - var body: some View { - let corner = size * (32.0 / 140.0) - let arcStrokeWidth = size * (9.0 / 140.0) let a = BrandPalette.violetAccent + let assetName = framed ? "FastSharedIcon" : "FastSharedMark" ZStack { if ambient { @@ -273,74 +264,19 @@ private struct SheetPlaneArc: View { .accessibilityHidden(true) } - if framed { - let bg = sysTertiaryBackground - let borderColor = sysSeparator - RoundedRectangle(cornerRadius: corner, style: .continuous) - .fill(bg) - .overlay( - RoundedRectangle(cornerRadius: corner, style: .continuous) - .stroke(borderColor, lineWidth: 1) - ) - .frame(width: size, height: size) - } - - SheetArcPath() - .stroke( - LinearGradient( - stops: [ - .init(color: a.fade.opacity(0), location: 0), - .init(color: a.hot.opacity(0.95), location: 0.5), - .init(color: a.soft, location: 1), - ], - startPoint: .bottomLeading, - endPoint: .topTrailing - ), - style: StrokeStyle(lineWidth: arcStrokeWidth, lineCap: .butt) - ) + Image(assetName) + .resizable() + .interpolation(.high) + .antialiased(true) + .scaledToFit() .frame(width: size, height: size) - - GeometryReader { geo in - let s = geo.size.width / 140.0 - let transform = CGAffineTransform(translationX: 81 * s, y: 66 * s) - .scaledBy(x: 1.1 * s, y: 1.1 * s) - ZStack { - Path { p in - p.move(to: CGPoint(x: 0, y: 0).applying(transform)) - p.addLine(to: CGPoint(x: 22, y: -26).applying(transform)) - p.addLine(to: CGPoint(x: 14, y: 10).applying(transform)) - p.addLine(to: CGPoint(x: 4, y: 4).applying(transform)) - p.addLine(to: CGPoint(x: 0, y: 18).applying(transform)) - p.addLine(to: CGPoint(x: -8, y: 4).applying(transform)) - p.closeSubpath() - } - .fill(planeColor) - Path { p in - p.move(to: CGPoint(x: 4, y: 4).applying(transform)) - p.addLine(to: CGPoint(x: 14, y: 10).applying(transform)) - } - .stroke(planeColor.opacity(0.35), lineWidth: 1) - } - } - .frame(width: size, height: size) + .clipShape(RoundedRectangle(cornerRadius: framed ? size * 0.22 : 0, style: .continuous)) + .shadow(color: framed ? Color.black.opacity(0.16) : .clear, radius: framed ? size * 0.12 : 0, y: framed ? size * 0.05 : 0) } .frame(width: size, height: size) } } -private struct SheetArcPath: Shape { - func path(in rect: CGRect) -> Path { - let s = rect.width / 140.0 - var p = Path() - p.move(to: CGPoint(x: 26 * s, y: 110 * s)) - p.addQuadCurve( - to: CGPoint(x: 80 * s, y: 72 * s), - control: CGPoint(x: 60 * s, y: 98 * s) - ) - return p - } -} - /// Inline brand lockup for the share extension. private struct SheetBrandLockup: View { var markSize: CGFloat = 22 @@ -352,7 +288,7 @@ private struct SheetBrandLockup: View { var body: some View { HStack(spacing: 8) { - SheetPlaneArc(size: markSize) + SheetPlaneArc(size: markSize, framed: true) .accessibilityHidden(true) ( Text("fastshared") @@ -361,7 +297,7 @@ private struct SheetBrandLockup: View { .foregroundStyle(FriendlyPalette.accentHot) ) .font(.system(size: textSize, weight: .bold)) - .tracking(-textSize * 0.02) + .tracking(0) } .accessibilityElement(children: .combine) .accessibilityLabel("fastshared") @@ -855,7 +791,7 @@ private struct SuccessStage: View { VStack(spacing: 0) { Spacer(minLength: 0) - // Hero — PlaneArc mark with success badge overlay + // Hero brand mark with success badge overlay SheetPlaneArc(size: 100, framed: false, ambient: true) .overlay(alignment: .bottomTrailing) { ZStack { diff --git a/apple/Packages/FastSharedCore/Sources/FastSharedCore/Branding/BrandPalette.swift b/apple/Packages/FastSharedCore/Sources/FastSharedCore/Branding/BrandPalette.swift index 3aba57e..46c1e1d 100644 --- a/apple/Packages/FastSharedCore/Sources/FastSharedCore/Branding/BrandPalette.swift +++ b/apple/Packages/FastSharedCore/Sources/FastSharedCore/Branding/BrandPalette.swift @@ -86,7 +86,7 @@ public enum BrandPalette { // `BrandPalette.accent` (or the app-target shim `BrandPalette.accentHot`) // rather than reaching into a named accent palette directly. The // `amberAccent` palette is retained only for the urgency-warning gradient - // (`BrandPalette.arc`) and the `PlaneArcMark.brandArcAmber` preset. + // (`BrandPalette.arc`) and legacy compatibility presets. public static let accent: Accent = violetAccent // MARK: - Gradients diff --git a/apple/design-system/MASTER.md b/apple/design-system/MASTER.md index 7e2e622..95358d3 100644 --- a/apple/design-system/MASTER.md +++ b/apple/design-system/MASTER.md @@ -4,7 +4,7 @@ This file is the single source of truth for design decisions on the FastShared i **Sister scope (do not cross-apply):** the marketing site at `web/` runs on a different aesthetic locked in `web/.impeccable.md` (dark-only, ephemeral · opinionated · crafted). Friendly applies only to native app surfaces. -Last updated: 2026-04-19 +Last updated: 2026-04-26 --- @@ -13,7 +13,7 @@ Last updated: 2026-04-19 **Friendly Redesign.** Light-first, warm, human-centered, soft and spacious. Pivots away from the previous warm-amber-on-dark direction. Communicates "ephemeral sharing as everyday kindness" rather than "ephemeral link as technology." Brand DNA preserved from the previous redesign: -- Plane + Arc mark (v3) with optional ambient halo +- Raster paper-plane mark with violet flight arc and optional ambient halo - Bricolage Grotesque (display) + JetBrains Mono (mono) - Ephemerality as the core gesture (countdown, urgency, decay) diff --git a/apple/fastlane/screenshots/iap/en-US/01_APP_IPHONE_67_iap-pro-review.png b/apple/fastlane/screenshots/iap/en-US/01_APP_IPHONE_67_iap-pro-review.png index be1c7ba..0260e3e 100644 Binary files a/apple/fastlane/screenshots/iap/en-US/01_APP_IPHONE_67_iap-pro-review.png and b/apple/fastlane/screenshots/iap/en-US/01_APP_IPHONE_67_iap-pro-review.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/01_APP_IPAD_PRO_3GEN_129_share-flow.png b/apple/fastlane/screenshots/ios/en-US/01_APP_IPAD_PRO_3GEN_129_share-flow.png index 419e193..4154a50 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/01_APP_IPAD_PRO_3GEN_129_share-flow.png and b/apple/fastlane/screenshots/ios/en-US/01_APP_IPAD_PRO_3GEN_129_share-flow.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/01_APP_IPHONE_67_share-flow.png b/apple/fastlane/screenshots/ios/en-US/01_APP_IPHONE_67_share-flow.png index 67435e2..030a9bf 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/01_APP_IPHONE_67_share-flow.png and b/apple/fastlane/screenshots/ios/en-US/01_APP_IPHONE_67_share-flow.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/02_APP_IPAD_PRO_3GEN_129_retention-picker.png b/apple/fastlane/screenshots/ios/en-US/02_APP_IPAD_PRO_3GEN_129_retention-picker.png index 8e69f1a..fbd28c5 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/02_APP_IPAD_PRO_3GEN_129_retention-picker.png and b/apple/fastlane/screenshots/ios/en-US/02_APP_IPAD_PRO_3GEN_129_retention-picker.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/02_APP_IPHONE_67_retention-picker.png b/apple/fastlane/screenshots/ios/en-US/02_APP_IPHONE_67_retention-picker.png index b5bb681..e2b4d5f 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/02_APP_IPHONE_67_retention-picker.png and b/apple/fastlane/screenshots/ios/en-US/02_APP_IPHONE_67_retention-picker.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/03_APP_IPAD_PRO_3GEN_129_upload-progress.png b/apple/fastlane/screenshots/ios/en-US/03_APP_IPAD_PRO_3GEN_129_upload-progress.png index 8fdb350..1973d5b 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/03_APP_IPAD_PRO_3GEN_129_upload-progress.png and b/apple/fastlane/screenshots/ios/en-US/03_APP_IPAD_PRO_3GEN_129_upload-progress.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/03_APP_IPHONE_67_upload-progress.png b/apple/fastlane/screenshots/ios/en-US/03_APP_IPHONE_67_upload-progress.png index f8ecfbe..e1acb80 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/03_APP_IPHONE_67_upload-progress.png and b/apple/fastlane/screenshots/ios/en-US/03_APP_IPHONE_67_upload-progress.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/04_APP_IPAD_PRO_3GEN_129_history-revoke.png b/apple/fastlane/screenshots/ios/en-US/04_APP_IPAD_PRO_3GEN_129_history-revoke.png index 0892268..54f55ee 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/04_APP_IPAD_PRO_3GEN_129_history-revoke.png and b/apple/fastlane/screenshots/ios/en-US/04_APP_IPAD_PRO_3GEN_129_history-revoke.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/04_APP_IPHONE_67_history-revoke.png b/apple/fastlane/screenshots/ios/en-US/04_APP_IPHONE_67_history-revoke.png index c2d461a..a4473fa 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/04_APP_IPHONE_67_history-revoke.png and b/apple/fastlane/screenshots/ios/en-US/04_APP_IPHONE_67_history-revoke.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/05_APP_IPAD_PRO_3GEN_129_pro-paywall.png b/apple/fastlane/screenshots/ios/en-US/05_APP_IPAD_PRO_3GEN_129_pro-paywall.png index acd2e90..5b419a4 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/05_APP_IPAD_PRO_3GEN_129_pro-paywall.png and b/apple/fastlane/screenshots/ios/en-US/05_APP_IPAD_PRO_3GEN_129_pro-paywall.png differ diff --git a/apple/fastlane/screenshots/ios/en-US/05_APP_IPHONE_67_pro-paywall.png b/apple/fastlane/screenshots/ios/en-US/05_APP_IPHONE_67_pro-paywall.png index be1c7ba..0260e3e 100644 Binary files a/apple/fastlane/screenshots/ios/en-US/05_APP_IPHONE_67_pro-paywall.png and b/apple/fastlane/screenshots/ios/en-US/05_APP_IPHONE_67_pro-paywall.png differ diff --git a/apple/project.yml b/apple/project.yml index 1478f2b..a08cc68 100644 --- a/apple/project.yml +++ b/apple/project.yml @@ -42,6 +42,7 @@ targets: supportedDestinations: [iOS, macOS] sources: - path: FastSharedApp + - path: BrandAssets.xcassets dependencies: - package: FastSharedCore product: FastSharedCore @@ -125,6 +126,7 @@ targets: supportedDestinations: [iOS, macOS] sources: - path: FastSharedShareExt + - path: BrandAssets.xcassets dependencies: - package: FastSharedCore product: FastSharedCore @@ -162,6 +164,7 @@ targets: supportedDestinations: [iOS] sources: - path: FastSharedLiveActivity + - path: BrandAssets.xcassets dependencies: - package: FastSharedCore product: FastSharedCore diff --git a/backend/src/index.ts b/backend/src/index.ts index 2c108be..0eead04 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -88,6 +88,8 @@ const APP_PATH_PREFIXES = [ '/.well-known', '/.well-known/', '/og-image.png', + '/brand', + '/brand/', ]; function isAppPath(pathname: string): boolean { diff --git a/backend/src/lib/previewPage.ts b/backend/src/lib/previewPage.ts index ab6b989..0d47fa7 100644 --- a/backend/src/lib/previewPage.ts +++ b/backend/src/lib/previewPage.ts @@ -202,13 +202,21 @@ export function renderPreviewPage(args: RenderPreviewPageArgs): Response { margin-bottom: 24px; } .brand { - display: inline-block; + display: inline-flex; + align-items: center; + gap: 0.42em; font-size: 22px; font-weight: 700; letter-spacing: -0.025em; color: var(--charcoal); text-decoration: none; } + .brand img { + width: 1.35em; + height: 1.35em; + border-radius: 22%; + flex: none; + } .brand-dot { color: var(--violet-hot); } .viewport { border-radius: 20px; @@ -400,7 +408,7 @@ export function renderPreviewPage(args: RenderPreviewPageArgs): Response {
- fastshared. + fastshared.
${viewport}
@@ -591,13 +599,21 @@ export function renderPendingPage(args: RenderPendingPageArgs): Response { } header { margin-bottom: 48px; } .brand { - display: inline-block; + display: inline-flex; + align-items: center; + gap: 0.42em; font-size: 22px; font-weight: 700; letter-spacing: -0.025em; color: var(--charcoal); text-decoration: none; } + .brand img { + width: 1.35em; + height: 1.35em; + border-radius: 22%; + flex: none; + } .brand-dot { color: var(--violet-hot); } .stage { padding: 48px 24px; @@ -678,7 +694,7 @@ export function renderPendingPage(args: RenderPendingPageArgs): Response {
- fastshared. + fastshared.
@@ -961,13 +977,19 @@ export function renderBundlePreviewPage(args: RenderBundlePreviewPageArgs): Resp .brand { display: inline-flex; align-items: center; - gap: 4px; + gap: 0.42em; font-size: 16px; font-weight: 700; letter-spacing: -0.03em; color: var(--charcoal); text-decoration: none; } + .brand img { + width: 1.35em; + height: 1.35em; + border-radius: 22%; + flex: none; + } .brand-dot { color: var(--violet-hot); } .finder-bar { display: flex; @@ -1361,7 +1383,7 @@ export function renderBundlePreviewPage(args: RenderBundlePreviewPageArgs): Resp
- fastshared. + fastshared. ${safeRemaining}
@@ -1589,13 +1611,21 @@ export function renderGonePage(reason: 'expired' | 'revoked' | 'deleted'): Respo } header { margin-bottom: 32px; } .brand { - display: inline-block; + display: inline-flex; + align-items: center; + gap: 0.42em; font-size: 22px; font-weight: 700; letter-spacing: -0.025em; color: var(--charcoal); text-decoration: none; } + .brand img { + width: 1.35em; + height: 1.35em; + border-radius: 22%; + flex: none; + } .brand-dot { color: var(--violet-hot); } .card { padding: 40px 32px; @@ -1636,7 +1666,7 @@ export function renderGonePage(reason: 'expired' | 'revoked' | 'deleted'): Respo
- fastshared. + fastshared.

${safeTitle}

diff --git a/backend/src/routes/assetsPublic.ts b/backend/src/routes/assetsPublic.ts index 4312a30..df120df 100644 --- a/backend/src/routes/assetsPublic.ts +++ b/backend/src/routes/assetsPublic.ts @@ -1,22 +1,45 @@ import { Hono } from 'hono'; +import type { Context } from 'hono'; import type { AppBindings } from '~/env'; import { log } from '~/lib/logger'; // Public static assets served by the Worker so Slack/iMessage/Discord -// card-unfurlers hit this file without a Pages origin round-trip for every -// scrape. We proxy the bytes from the Pages origin once and cache at the -// Worker edge for a day. Plan C ships the actual PNG on the Pages origin. +// card-unfurlers and generated short-link pages can fetch brand files without +// a Pages origin round-trip on every scrape. We proxy the bytes from the Pages +// origin once and cache at the Worker edge for a day. const PAGES_ORIGIN = 'https://fastshared-web.pages.dev'; const CACHE_MAX_AGE_SECONDS = 86_400; export const assetsPublicRoutes = new Hono(); -assetsPublicRoutes.get('/og-image.png', async (c) => { +const PUBLIC_ASSETS: Record = { + '/og-image.png': 'image/png', + '/brand/appicon-1024.png': 'image/png', + '/brand/logo-mark.png': 'image/png', + '/brand/logo-horizontal.png': 'image/png', + '/brand/wordmark-horizontal-dark.png': 'image/png', + '/brand/wordmark-horizontal-light.png': 'image/png', + '/brand/wordmark-mark.png': 'image/png', + '/brand/og-image.png': 'image/png', + '/brand/appicon.svg': 'image/svg+xml; charset=utf-8', + '/brand/logo-mark.svg': 'image/svg+xml; charset=utf-8', + '/brand/logo-horizontal.svg': 'image/svg+xml; charset=utf-8', +}; + +async function proxyPublicAsset(pathname: string, c: Context) { // Cloudflare Workers exposes a per-colocated-edge cache as `caches.default` // (non-standard global). The `CacheStorage` lib type doesn't know about it, // so we widen to `any` at the call site only. + const contentType = PUBLIC_ASSETS[pathname]; + if (!contentType) { + return new Response('asset unavailable', { + status: 404, + headers: { 'content-type': 'text/plain; charset=utf-8' }, + }); + } + const cache = (caches as unknown as { default: Cache }).default; - const cacheKey = new Request(`${c.env.SHORT_LINK_HOST}/og-image.png`, { method: 'GET' }); + const cacheKey = new Request(`${c.env.SHORT_LINK_HOST}${pathname}`, { method: 'GET' }); const cached = await cache.match(cacheKey); if (cached) { // Copy so we can add a HIT marker without mutating the cached response. @@ -29,14 +52,15 @@ assetsPublicRoutes.get('/og-image.png', async (c) => { }); } - const origin = await fetch(`${PAGES_ORIGIN}/og-image.png`); + const origin = await fetch(`${PAGES_ORIGIN}${pathname}`); if (!origin.ok) { log.warn({ - msg: 'og_image_origin_miss', + msg: 'public_asset_origin_miss', + pathname, status: origin.status, requestId: c.get('requestId'), }); - return new Response('og-image unavailable', { + return new Response('asset unavailable', { status: 502, headers: { 'content-type': 'text/plain; charset=utf-8' }, }); @@ -45,7 +69,7 @@ assetsPublicRoutes.get('/og-image.png', async (c) => { const buf = await origin.arrayBuffer(); const body = new Uint8Array(buf); const h = new Headers(); - h.set('Content-Type', 'image/png'); + h.set('Content-Type', contentType); h.set('Cache-Control', `public, max-age=${CACHE_MAX_AGE_SECONDS}, immutable`); h.set('Content-Length', String(body.byteLength)); h.set('CF-Cache-Status', 'MISS'); @@ -53,4 +77,12 @@ assetsPublicRoutes.get('/og-image.png', async (c) => { // Clone before returning so the cache can consume the body independently. c.executionCtx.waitUntil(cache.put(cacheKey, res.clone())); return res; +} + +assetsPublicRoutes.get('/og-image.png', async (c) => { + return proxyPublicAsset('/og-image.png', c); +}); + +assetsPublicRoutes.get('/brand/:asset', async (c) => { + return proxyPublicAsset(`/brand/${c.req.param('asset')}`, c); }); diff --git a/brand/README.md b/brand/README.md index 6b4c704..8140b36 100644 --- a/brand/README.md +++ b/brand/README.md @@ -1,91 +1,58 @@ -# fastshared — brand v1.0 +# fastshared - brand v2.0 -Identity for a product whose entire value is brevity. Every upload is a countdown; the mark is the countdown in one frame. - -**Concept.** A warm origin sphere emits a single arc toward a ghost node. The arc fades as it approaches; where it lands, only particles remain. You are watching a link expire. +FastShared now uses a luminous paper-plane mark over a violet flight arc. The +PNG masters are the visual source of truth because the logo depends on glow, +soft alpha, and raster shading that should not be approximated differently per +platform. SVG files in this folder are simplified compatibility assets for +favicon, mask-icon, and vector download surfaces. ## Files -``` +```text brand/ -├── appicon.svg # 1024×1024, full-bleed. Master for Apple App Store. -├── logo-mark.svg # transparent bg, inherits currentColor for outlines -├── logo-horizontal.svg # mark + wordmark, transparent bg, 2400×600 -├── preview.html # brand guidelines page (open in browser) -├── export.sh # renders every Apple icon size from appicon.svg -└── README.md +├── source-mark.png # transparent source generated from the approved mark +├── source-lockup-dark.png # square dark reference lockup from the approved prompt +├── appicon-1024.png # App Store/web icon master, RGB, no alpha +├── logo-mark.png # transparent mark, 2048 x 2048 +├── logo-horizontal.png # transparent horizontal lockup, 2400 x 800 +├── wordmark-horizontal-dark.png +├── wordmark-horizontal-light.png +├── wordmark-mark.png +├── og-image.png # 1200 x 630 social card +├── appicon.svg # simplified vector fallback +├── logo-mark.svg # simplified vector fallback +├── logo-horizontal.svg # simplified vector fallback +├── clean_mark.py # alpha-speck cleanup for generated transparent PNGs +├── export.sh # regenerates PNG assets and Apple AppIcon sizes +└── preview.html # local brand preview ``` -Open `preview.html` in a browser to see the full system in motion. - ## Palette -| role | name | hex | -|---|---|---| -| ground | ink | `#070318` | -| surface 1 | nightshade | `#1d0d4b` | -| surface 2 | violet | `#3b1f86` | -| accent · primary | amber | `#ff9f47` | -| accent · soft | ember | `#ffc487` | -| accent · fade | farewell | `#ff4e7c` | -| particle | dust | `#ffe0b8` | -| typography | milk | `#fafaff` | - -No other colors ship. Amber is the pulse. Coral is the fade. Everything else is silence. - -## Type - -- **Display / body** — [Bricolage Grotesque](https://fonts.google.com/specimen/Bricolage+Grotesque), variable, `opsz 12–96`, weights 400–700. Tracking `-0.035em` on headlines. -- **Mono / technical** — [JetBrains Mono](https://fonts.google.com/specimen/JetBrains+Mono), weight 500 for captions. - -The wordmark is lowercase `fastshared` at weight 700 with tight tracking, followed by an amber dot. The dot is the only ornament. - -## Geometry - -All composition lives on a 1024-unit grid. +| role | hex | +|---|---| +| ink | `#070318` | +| night violet | `#12082d` | +| arc violet | `#9d7aff` | +| arc shadow | `#3b0f58` | +| arc soft | `#c7b4ff` | +| milk | `#fffdf8` | -| element | position | size | -|---|---|---| -| origin sphere | x 300, y 580 | r 124 | -| origin glow | concentric | r 240, 55% amber alpha | -| arc path | `C 420,300 · 620,620` | stroke-width 92 | -| ghost node | x 760, y 420 | r 52 (outline), r 14 (inner) | -| particles | x 794 → 922 | r 11 → 2 · opacity 0.92 → 0.2 | -| safe area | inset | 128 px on all sides | - -Mark ratio (origin radius : arc length) is approximately **0.618** — because proportion matters. - -## Export for the App Store +## Export ```bash -# one-time setup -brew install librsvg - -# generate every Apple icon size cd brand ./export.sh ``` -Output in `brand/out/` covers iPhone, iPad, Mac, and the App Store marketing icon at 1024×1024. Drop into `apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/` and update the `Contents.json` to map each size to its file. - -> Apple rounds the corners on-device. Do not pre-round. Do not add transparency. Export full-bleed. - -## Do - -- Let the origin sphere sit warm and luminous — it is the subject. -- Keep clear space equal to one sphere-diameter on every side. -- Use amber as a hot point only — sparingly, confidently, never as fill. -- Pair the wordmark on `#070318` first. Light backgrounds are print-only. -- Animate the arc (stroke-dashoffset) when introducing the brand for the first time in a new context. - -## Don't - -- Don't recolor the arc. The amber→coral fade carries the concept. -- Don't place the mark on photography or busy imagery. -- Don't rotate, skew, outline, or emboss the wordmark. -- Don't lowercase it further. `fastshared` is already the final form. -- Don't enclose the icon in a badge, ring, or chrome. +`export.sh` writes the public brand PNGs into `brand/` and every Apple icon +size into `brand/out/`. Copy `brand/out/*.png` into +`apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/`. -## License +## Usage Rules -Internal brand asset for fastshared. © 2026 Matheus Kindrazki. Not for use outside the product unless explicitly licensed. +- Use `appicon-1024.png` for app icons, nav chips, README badges, and Apple touch icons. +- Use `logo-horizontal.png` only on dark or controlled backgrounds where the app icon tile can sit cleanly. +- Use `wordmark-horizontal-dark.png` for press and social surfaces on ink backgrounds. +- Use `logo-mark.svg` only for favicon/mask/vector fallback contexts. +- Do not restore the old PlaneArc path artwork or stroke-draw animation; this mark is a raster/glow identity. diff --git a/brand/appicon-1024.png b/brand/appicon-1024.png new file mode 100644 index 0000000..14f4ef7 Binary files /dev/null and b/brand/appicon-1024.png differ diff --git a/brand/appicon.svg b/brand/appicon.svg index 533cc94..a0fc720 100644 --- a/brand/appicon.svg +++ b/brand/appicon.svg @@ -1,34 +1,46 @@ - - FastShared — Plane + Arc + FastShared app icon - - - - - - + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/brand/clean_mark.py b/brand/clean_mark.py new file mode 100644 index 0000000..9a06212 --- /dev/null +++ b/brand/clean_mark.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +"""Remove isolated alpha specks from the generated transparent mark.""" + +from __future__ import annotations + +import sys +from pathlib import Path + +from PIL import Image + + +def main() -> int: + if len(sys.argv) != 3: + print("usage: clean_mark.py ", file=sys.stderr) + return 2 + + src = Path(sys.argv[1]) + dst = Path(sys.argv[2]) + img = Image.open(src).convert("RGBA") + width, height = img.size + alpha = img.getchannel("A") + alpha_px = alpha.load() + seen = bytearray(width * height) + keep = bytearray(width * height) + + threshold = 3 + min_component_area = 100 + + for y in range(height): + for x in range(width): + idx = y * width + x + if seen[idx] or alpha_px[x, y] <= threshold: + continue + + stack = [(x, y)] + seen[idx] = 1 + coords: list[tuple[int, int]] = [] + + while stack: + cx, cy = stack.pop() + coords.append((cx, cy)) + for nx, ny in ((cx + 1, cy), (cx - 1, cy), (cx, cy + 1), (cx, cy - 1)): + if nx < 0 or ny < 0 or nx >= width or ny >= height: + continue + next_idx = ny * width + nx + if not seen[next_idx] and alpha_px[nx, ny] > threshold: + seen[next_idx] = 1 + stack.append((nx, ny)) + + if len(coords) >= min_component_area: + for cx, cy in coords: + keep[cy * width + cx] = 1 + + for _ in range(2): + expanded = bytearray(keep) + for y in range(height): + for x in range(width): + if not keep[y * width + x]: + continue + for nx in (x - 1, x, x + 1): + for ny in (y - 1, y, y + 1): + if 0 <= nx < width and 0 <= ny < height: + expanded[ny * width + nx] = 1 + keep = expanded + + px = img.load() + for y in range(height): + for x in range(width): + if keep[y * width + x]: + continue + r, g, b, _ = px[x, y] + px[x, y] = (r, g, b, 0) + + img.save(dst) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/brand/export.sh b/brand/export.sh index e145844..2e0cbf6 100755 --- a/brand/export.sh +++ b/brand/export.sh @@ -1,19 +1,107 @@ #!/usr/bin/env bash -# Export the master app icon to every PNG size Apple asks for. -# Requires: brew install librsvg +# Export FastShared brand assets from the new raster masters. +# Requires: ImageMagick. The SVG files in this folder are simplified fallbacks +# for favicon/mask/vector consumers; PNG is the visual source of truth. set -euo pipefail cd "$(dirname "$0")" -SRC=${SRC:-appicon.svg} + +MARK_SRC=${MARK_SRC:-source-mark.png} OUT=${OUT:-out} +FONT=${FONT:-/Library/Fonts/SF-Pro-Rounded-Bold.otf} + mkdir -p "$OUT" -if ! command -v rsvg-convert >/dev/null; then - echo "rsvg-convert missing — run: brew install librsvg" >&2 +if ! command -v magick >/dev/null; then + echo "ImageMagick missing. Install it with: brew install imagemagick" >&2 exit 1 fi -# Apple AppIcon.appiconset sizes (name px) +if [[ ! -f "$MARK_SRC" ]]; then + echo "Missing $MARK_SRC. Copy the transparent mark PNG into brand/ first." >&2 + exit 1 +fi + +if [[ ! -f "$FONT" ]]; then + FONT=$(magick -list font | awk '/Font: System-Font-Bold/{print $2; exit}') +fi + +MARK_CLEAN=.mark-clean.png +trap 'rm -f "$MARK_CLEAN" /tmp/fastshared-appicon-bg.png' EXIT +python3 clean_mark.py "$MARK_SRC" "$MARK_CLEAN" + +INK="#070318" +INK_2="#12082d" +MILK="#fffdf8" +CHARCOAL="#171323" +VIOLET="#9d7aff" + +make_dark_bg() { + local width=$1 + local height=$2 + local out=$3 + magick -size "${width}x${height}" "gradient:${INK_2}-${INK}" \ + \( -size "${width}x${height}" radial-gradient:"rgba(157,122,255,0.45)-rgba(157,122,255,0)" -resize "${width}x${height}!" \) \ + -gravity center -compose screen -composite -depth 8 -define png:color-type=2 "$out" +} + +render_horizontal_lockup() { + local width=$1 + local height=$2 + local bg=$3 + local text=$4 + local out=$5 + local text_x=760 + local text_y=495 + local dot_x=2054 + local mark_size=650 + + if [[ "$bg" == "none" ]]; then + magick -size "${width}x${height}" xc:none "$out" + elif [[ "$bg" == "light" ]]; then + magick -size "${width}x${height}" xc:"#fbf8f1" "$out" + else + make_dark_bg "$width" "$height" "$out" + fi + + if [[ "$bg" == "light" ]]; then + magick "$out" \ + \( appicon-1024.png -resize "${mark_size}x${mark_size}" \) -geometry +70+75 -composite \ + -font "$FONT" -pointsize 260 -fill "$CHARCOAL" -annotate +"${text_x}"+${text_y} "$text" \ + -font "$FONT" -pointsize 260 -fill "$VIOLET" -annotate +"${dot_x}"+${text_y} "." \ + -strip -define png:color-type=2 "$out" + else + magick "$out" \ + \( appicon-1024.png -resize "${mark_size}x${mark_size}" \) -geometry +70+75 -composite \ + -font "$FONT" -pointsize 260 -fill "$MILK" -annotate +"${text_x}"+${text_y} "$text" \ + -font "$FONT" -pointsize 260 -fill "$VIOLET" -annotate +"${dot_x}"+${text_y} "." \ + -strip "$out" + if [[ "$bg" != "none" ]]; then + magick "$out" -background "$INK" -alpha remove -alpha off -depth 8 -strip -define png:color-type=2 "$out" + fi + fi +} + +echo "Generating master PNG assets..." + +magick "$MARK_CLEAN" -resize 2048x2048 -strip logo-mark.png +magick "$MARK_CLEAN" -resize 1024x1024 -strip wordmark-mark.png + +make_dark_bg 1024 1024 /tmp/fastshared-appicon-bg.png +magick /tmp/fastshared-appicon-bg.png "$MARK_SRC" -gravity center -compose over -composite \ + -background "$INK" -alpha remove -alpha off -depth 8 -strip -define png:color-type=2 appicon-1024.png + +render_horizontal_lockup 2400 800 none fastshared logo-horizontal.png +render_horizontal_lockup 2400 800 dark fastshared wordmark-horizontal-dark.png +render_horizontal_lockup 2400 800 light fastshared wordmark-horizontal-light.png + +make_dark_bg 1200 630 og-image.png +magick og-image.png \ + \( logo-horizontal.png -resize 1000x333 \) -gravity center -geometry +0-10 -compose over -composite \ + -background "$INK" -alpha remove -alpha off -depth 8 -strip -define png:color-type=2 og-image.png + +echo "Generating Apple AppIcon set into $OUT/..." + SIZES=( "ios-20@2x 40" "ios-20@3x 60" @@ -43,24 +131,17 @@ SIZES=( "mac-512@1x 512" "mac-512@2x 1024" "appstore-1024 1024" + "ios-1024-dark 1024" + "ios-1024-tinted 1024" ) for row in "${SIZES[@]}"; do read -r name px <<<"$row" out="$OUT/${name}.png" - rsvg-convert -w "$px" -h "$px" "$SRC" -o "$out" - # WHY: App Store Connect rejects icons with an alpha channel (error 90717 - # "Invalid large app icon"). rsvg-convert always writes RGBA, so flatten - # every exported PNG onto the brand ground and drop the alpha channel. - # Uses ImageMagick when available; falls back to a warning if not. - if command -v magick >/dev/null; then - magick "$out" -background "#070318" -alpha remove -alpha off -define png:color-type=2 "$out.flat" \ - && mv "$out.flat" "$out" - else - echo " (warning) ImageMagick missing — $out still has an alpha channel." >&2 - fi + magick appicon-1024.png -resize "${px}x${px}" \ + -background "$INK" -alpha remove -alpha off -depth 8 -strip -define png:color-type=2 "$out" printf " %-20s %4dpx %s\n" "$name" "$px" "$out" done -printf "\nwrote %d icons to %s/\n" "${#SIZES[@]}" "$OUT" -printf "next: drop them into apple/FastSharedApp/Assets.xcassets/AppIcon.appiconset/\n" +printf "\nwrote %d app icons to %s/\n" "${#SIZES[@]}" "$OUT" +printf "copy web assets from brand/*.png and Apple icons from %s/*.png\n" "$OUT" diff --git a/brand/logo-horizontal.png b/brand/logo-horizontal.png new file mode 100644 index 0000000..3613db8 Binary files /dev/null and b/brand/logo-horizontal.png differ diff --git a/brand/logo-horizontal.svg b/brand/logo-horizontal.svg index 5c866ac..f5b3c7b 100644 --- a/brand/logo-horizontal.svg +++ b/brand/logo-horizontal.svg @@ -1,35 +1,41 @@ - - - FastShared + + fastshared. - - - - + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + - - fastshared. diff --git a/brand/logo-mark.png b/brand/logo-mark.png new file mode 100644 index 0000000..55f0c4b Binary files /dev/null and b/brand/logo-mark.png differ diff --git a/brand/logo-mark.svg b/brand/logo-mark.svg index df0d74b..5417bd8 100644 --- a/brand/logo-mark.svg +++ b/brand/logo-mark.svg @@ -1,25 +1,43 @@ - - - FastShared + + FastShared mark - - - - + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + diff --git a/brand/og-image.png b/brand/og-image.png new file mode 100644 index 0000000..a3b7776 Binary files /dev/null and b/brand/og-image.png differ diff --git a/brand/preview.html b/brand/preview.html index 16e7ee7..f6727e7 100644 --- a/brand/preview.html +++ b/brand/preview.html @@ -1,732 +1,114 @@ - - - - -fastshared · brand - - - - - - -
-
- -
-
fastshared / identity v1.0
-
- ephemeral bearer links - ios · ipados · macos - 2026 — 04 — 19 -
-
- - -
-
-
marktemporal link
-
originx:300 y:580 r:124
-
arcC 420,300 · 620,620
-
ghostx:760 y:420 r:52
-
particles5 · decay 0.7
-
safe area128 px
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
icon1024 × 1024
-
formatsvg / png
-
spacedisplay-p3
-
corneros rounded
-
typebricolage 600
-
mark ratio0.618
-
- -
-

fastshared.

-

A temporal link mark — an origin that sends, and a trail that fades. Built for the App Store, engineered for speed, deliberately brief.

-
-
- - -
-
- 01 / mark -

The mark
is a ritual,
not a logo.

-

A single arc between two nodes — the second dissolving into particles. Every upload is a countdown.

-
-
-
-
dark / primary#070318
- + + + + + fastshared brand v2 + + + +
+
+
+

fastshared.

+

+ Brand v2 uses the approved luminous paper-plane mark and violet + flight arc. PNG assets are canonical; SVGs are fallbacks. +

-
-
violet / deep#1d0d4b
- -
-
-
amber / accent#ff9f47
- -
-
-
cream / light#f5f2ea
- -
-
-
- - -
-
- 02 / sizing -

Legible at
every scale.

-

The composition collapses gracefully: at 16 px only the origin sphere remains — a confident hot point.

-
-
-
-
-
-
-
-
-
-
- - -
-
- 03 / wordmark -

Lowercase, never shouting.

-

Bricolage Grotesque at 600, optical size 96. Tight tracking (-0.045em). The accent dot is the only emphasis needed.

-
-
- - on ink / primary -
-
-
- - on cream / print -
-
- - -
-
- 04 / palette -

Ink, amber,
and a coral
farewell.

-

Deep nocturnal ink carries the stage; amber is the pulse; coral is the fade. No other colors ship.

-
-
-
-

ink

ground · 0
-
#070318
-
-
-

nightshade

surface · 1
-
#1d0d4b
-
-
-

violet

surface · 2
-
#3b1f86
-
-
-

amber

accent · primary
-
#ff9f47
-
-
-

ember

accent · soft
-
#ffc487
-
-
-

farewell

accent · fade
-
#ff4e7c
-
-
-

dust

particle
-
#ffe0b8
-
-
-

milk

typography
-
#fafaff
-
-
-
- - -
-
- 05 / typography -

Bricolage
+ JetBrains.

-

Two typefaces, one voice. Bricolage handles everything expressive. JetBrains Mono handles everything factual.

-
- -
-
share anything.
-
get a link.
-
watch it vanish.
-
- -
- display / 600 - Every link expires. - headline / 500 - 24 hours, then gone — no residue, no trace. - body / 400 - Uploads land in private R2, resolve through a bearer-token redirect, and are hard-deleted on schedule. The infrastructure forgets on purpose — and so does the interface, which pushes the next share before the last one finishes its farewell. - mono / 500 - expires_at = now + 86400s · retention=oneDay -
-
- - -
-
- 06 / usage -

Do & don't.

-

The mark does the talking. Keep the clear-space and keep the silence around it.

-
-
-
-

do

-
    -
  • Let the origin sphere sit warm and luminous — it is the subject.
  • -
  • Respect a clear-space equal to one sphere-diameter on every side.
  • -
  • Export the app icon full-bleed. The OS is responsible for the corners.
  • -
  • Use amber as a hot point only — sparingly, confidently, never as fill.
  • -
  • Pair the wordmark on ink first. Light backgrounds are for print only.
  • -
-
-
-

don't

-
    -
  • Do not place the mark on busy imagery or photographic backgrounds.
  • -
  • Do not recolor the arc. The amber→coral fade carries the concept.
  • -
  • Do not rotate, skew, or outline the wordmark.
  • -
  • Do not lowercase it further — "fastshared" is already the final form.
  • -
  • Do not enclose the icon in a second ring, badge, or chrome.
  • -
-
-
-
- - -
-
- 07 / export -

Ship it.

-

One master SVG generates every Apple icon size. Run the script and drop the output into Assets.xcassets.

-
-
-
-
-# from the repo root
-cd brand
-./export.sh
-# writes png sizes 16 → 1024
-# to ./out/ (copy to Assets.xcassets)
- -
-
-# one-off at 1024 (App Store marketing)
-rsvg-convert -w 1024 appicon.svg \
-  > appicon-1024.png
-# requires: brew install librsvg
-
-
- - - -
-
- - - - - - + fastshared. lockup on dark background +
+ +
+
+ App icon +
app icon
+
+
+ Transparent mark +
transparent mark
+
+
+ Transparent horizontal lockup +
horizontal
+
+
+ Light background lockup +
light use
+
+
+
+ diff --git a/brand/source-lockup-dark.png b/brand/source-lockup-dark.png new file mode 100644 index 0000000..44df9df Binary files /dev/null and b/brand/source-lockup-dark.png differ diff --git a/brand/source-mark.png b/brand/source-mark.png new file mode 100644 index 0000000..ddc74df Binary files /dev/null and b/brand/source-mark.png differ diff --git a/brand/wordmark-horizontal-dark.png b/brand/wordmark-horizontal-dark.png new file mode 100644 index 0000000..2ca61c5 Binary files /dev/null and b/brand/wordmark-horizontal-dark.png differ diff --git a/brand/wordmark-horizontal-light.png b/brand/wordmark-horizontal-light.png new file mode 100644 index 0000000..44c93bc Binary files /dev/null and b/brand/wordmark-horizontal-light.png differ diff --git a/brand/wordmark-mark.png b/brand/wordmark-mark.png new file mode 100644 index 0000000..223cd2e Binary files /dev/null and b/brand/wordmark-mark.png differ diff --git a/web/README.md b/web/README.md index 8650fe8..424e247 100644 --- a/web/README.md +++ b/web/README.md @@ -52,9 +52,8 @@ redirects served by the backend Worker — do NOT point the apex at Pages. ## Open threads -- `public/og-image.png` — placeholder / TODO. Generate a 1200×630 PNG - from `brand/appicon.svg` on a dark canvas (use `rsvg-convert`) before - shipping the public beta. +- Brand assets are generated from `brand/source-mark.png` via + `brand/export.sh`; `public/og-image.png` is now the v2 social card. - TestFlight URL — currently `#testflight`. Replace when the TestFlight build is live on App Store Connect. - `/privacy` and `/terms` — placeholder copy. Final versions land before diff --git a/web/public/brand/appicon-1024.png b/web/public/brand/appicon-1024.png index d6f8e64..14f4ef7 100644 Binary files a/web/public/brand/appicon-1024.png and b/web/public/brand/appicon-1024.png differ diff --git a/web/public/brand/appicon.svg b/web/public/brand/appicon.svg index 533cc94..a0fc720 100644 --- a/web/public/brand/appicon.svg +++ b/web/public/brand/appicon.svg @@ -1,34 +1,46 @@ - - FastShared — Plane + Arc + FastShared app icon - - - - - - + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/public/brand/logo-horizontal.png b/web/public/brand/logo-horizontal.png new file mode 100644 index 0000000..3613db8 Binary files /dev/null and b/web/public/brand/logo-horizontal.png differ diff --git a/web/public/brand/logo-horizontal.svg b/web/public/brand/logo-horizontal.svg index cab48f9..f5b3c7b 100644 --- a/web/public/brand/logo-horizontal.svg +++ b/web/public/brand/logo-horizontal.svg @@ -1,35 +1,41 @@ - - - FastShared + + fastshared. - - - - + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + - - fastshared. diff --git a/web/public/brand/logo-mark.png b/web/public/brand/logo-mark.png new file mode 100644 index 0000000..55f0c4b Binary files /dev/null and b/web/public/brand/logo-mark.png differ diff --git a/web/public/brand/logo-mark.svg b/web/public/brand/logo-mark.svg index 9c44593..5417bd8 100644 --- a/web/public/brand/logo-mark.svg +++ b/web/public/brand/logo-mark.svg @@ -1,25 +1,43 @@ - - - FastShared + + FastShared mark - - - - + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + diff --git a/web/public/brand/og-image.png b/web/public/brand/og-image.png index f4f50f3..a3b7776 100644 Binary files a/web/public/brand/og-image.png and b/web/public/brand/og-image.png differ diff --git a/web/public/brand/wordmark-horizontal-dark.png b/web/public/brand/wordmark-horizontal-dark.png index 15b661f..2ca61c5 100644 Binary files a/web/public/brand/wordmark-horizontal-dark.png and b/web/public/brand/wordmark-horizontal-dark.png differ diff --git a/web/public/brand/wordmark-horizontal-light.png b/web/public/brand/wordmark-horizontal-light.png index 6cc7b60..44c93bc 100644 Binary files a/web/public/brand/wordmark-horizontal-light.png and b/web/public/brand/wordmark-horizontal-light.png differ diff --git a/web/public/brand/wordmark-mark.png b/web/public/brand/wordmark-mark.png index 220df8b..223cd2e 100644 Binary files a/web/public/brand/wordmark-mark.png and b/web/public/brand/wordmark-mark.png differ diff --git a/web/public/favicon.svg b/web/public/favicon.svg index df0d74b..5417bd8 100644 --- a/web/public/favicon.svg +++ b/web/public/favicon.svg @@ -1,25 +1,43 @@ - - - FastShared + + FastShared mark - - - - + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + diff --git a/web/public/og-image.png b/web/public/og-image.png index f4f50f3..a3b7776 100644 Binary files a/web/public/og-image.png and b/web/public/og-image.png differ diff --git a/web/src/components/BrandLockup.astro b/web/src/components/BrandLockup.astro index c4454d2..16ff320 100644 --- a/web/src/components/BrandLockup.astro +++ b/web/src/components/BrandLockup.astro @@ -1,8 +1,7 @@ --- /** - * BrandLockup — app icon + product name. - * The landing rebrand uses the shipped app icon as the primary mark so the - * website, README, and TestFlight builds all point at the same identity. + * BrandLockup - app icon tile + visual wordmark. + * Textual metadata still uses "FastShared"; the lockup is the brand glyph. */ interface Props { @@ -39,7 +38,7 @@ const { class="font-display font-bold" style={`font-size:${textSize}px;letter-spacing:0;line-height:1;`} > - FastShared + fastshared. @@ -49,4 +48,8 @@ const { border-radius: 22%; box-shadow: 0 8px 18px -12px rgba(15, 12, 34, 0.45); } + + .brand-lockup__dot { + color: #9d7aff; + } diff --git a/web/src/components/Nav.astro b/web/src/components/Nav.astro index 67d5c85..e30cb99 100644 --- a/web/src/components/Nav.astro +++ b/web/src/components/Nav.astro @@ -1,4 +1,6 @@ --- +import BrandLockup from './BrandLockup.astro'; + // Fixed navigation with the shipped app icon as the public brand mark. ---