diff --git a/patches/react-native+0.74.1.patch b/patches/react-native+0.74.1.patch
index 5d2900a7..789ba84a 100644
--- a/patches/react-native+0.74.1.patch
+++ b/patches/react-native+0.74.1.patch
@@ -1,3 +1,29 @@
+diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm
+index b0d71dc..9974932 100644
+--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm
++++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm
+@@ -377,10 +377,6 @@ - (void)textInputDidBeginEditing
+     self.backedTextInputView.attributedText = [NSAttributedString new];
+   }
+
+-  if (_selectTextOnFocus) {
+-    [self.backedTextInputView selectAll:nil];
+-  }
+-
+   [_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus
+                                  reactTag:self.reactTag
+                                      text:[self.backedTextInputView.attributedText.string copy]
+@@ -611,6 +607,10 @@ - (UIView *)reactAccessibilityElement
+ - (void)reactFocus
+ {
+   [self.backedTextInputView reactFocus];
++
++  if (_selectTextOnFocus) {
++    [self.backedTextInputView selectAll:nil];
++  }
+ }
+
+ - (void)reactBlur
 diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h
 index e9b330f..1ecdf0a 100644
 --- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h
@@ -5,7 +31,7 @@ index e9b330f..1ecdf0a 100644
 @@ -16,4 +16,6 @@
  @property (nonatomic, copy) RCTDirectEventBlock onRefresh;
  @property (nonatomic, weak) UIScrollView *scrollView;
- 
+
 +- (void)forwarderBeginRefreshing;
 +
  @end
@@ -16,7 +42,7 @@ index b09e653..4c32b31 100644
 @@ -198,9 +198,53 @@ - (void)refreshControlValueChanged
    [self setCurrentRefreshingState:super.refreshing];
    _refreshingProgrammatically = NO;
- 
+
 +  if (@available(iOS 17.4, *)) {
 +    if (_currentRefreshingState) {
 +      UIImpactFeedbackGenerator *feedbackGenerator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
@@ -29,7 +55,7 @@ index b09e653..4c32b31 100644
      _onRefresh(nil);
    }
  }
- 
+
 +/*
 + This method is used by Bluesky's ExpoScrollForwarder. This allows other React Native
 + libraries to perform a refresh of a scrollview and access the refresh control's onRefresh
@@ -38,15 +64,15 @@ index b09e653..4c32b31 100644
 +- (void)forwarderBeginRefreshing
 +{
 +  _refreshingProgrammatically = NO;
-+  
++
 +  [self sizeToFit];
-+  
++
 +  if (!self.scrollView) {
 +    return;
 +  }
-+  
++
 +  UIScrollView *scrollView = (UIScrollView *)self.scrollView;
-+  
++
 +  [UIView animateWithDuration:0.3
 +    delay:0
 +    options:UIViewAnimationOptionBeginFromCurrentState
@@ -58,7 +84,7 @@ index b09e653..4c32b31 100644
 +    completion:^(__unused BOOL finished) {
 +      [super beginRefreshing];
 +      [self setCurrentRefreshingState:super.refreshing];
-+    
++
 +      if (self->_onRefresh) {
 +        self->_onRefresh(nil);
 +      }
@@ -73,7 +99,7 @@ index 5f5e1ab..aac00b6 100644
 +++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java
 @@ -99,8 +99,9 @@ public class JavaTimerManager {
        }
- 
+
        // If the JS thread is busy for multiple frames we cancel any other pending runnable.
 -      if (mCurrentIdleCallbackRunnable != null) {
 -        mCurrentIdleCallbackRunnable.cancel();
@@ -81,5 +107,5 @@ index 5f5e1ab..aac00b6 100644
 +      if (currentRunnable != null) {
 +        currentRunnable.cancel();
        }
- 
+
        mCurrentIdleCallbackRunnable = new IdleCallbackRunnable(frameTimeNanos);
diff --git a/patches/react-native+0.74.1.patch.md b/patches/react-native+0.74.1.patch.md
index 9c93aee5..84953df2 100644
--- a/patches/react-native+0.74.1.patch.md
+++ b/patches/react-native+0.74.1.patch.md
@@ -11,3 +11,10 @@ in the RN repo: https://github.com/facebook/react-native/issues/43388
 Patching `RCTRefreshControl.m` and `RCTRefreshControl.h` to add a new `forwarderBeginRefreshing` method to the class.
 This method is used by `ExpoScrollForwarder` to initiate a refresh of the underlying `UIScrollView` from inside that
 module.
+
+
+## TextInput Patch - `selectTextOnFocus` fix
+
+Patching `RCTBaseTextInputView.m` to fix an issue where `selectTextOnFocus` does not work as expected on iOS 17. This
+patch _only_ fixes the Paper version of `TextInput`. If we migrate to Fabric and the fix has not been made upstream,
+we can apply the same fix. See https://github.com/facebook/react-native/pull/44307.
diff --git a/src/App.native.tsx b/src/App.native.tsx
index 5b2071e1..322e944a 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -14,7 +14,11 @@ import * as SplashScreen from 'expo-splash-screen'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
-import {Provider as StatsigProvider} from '#/lib/statsig/statsig'
+import {
+  initialize,
+  Provider as StatsigProvider,
+  tryFetchGates,
+} from '#/lib/statsig/statsig'
 import {logger} from '#/logger'
 import {MessagesProvider} from '#/state/messages'
 import {init as initPersistedState} from '#/state/persisted'
@@ -69,6 +73,9 @@ function InnerApp() {
       try {
         if (account) {
           await resumeSession(account)
+        } else {
+          await initialize()
+          await tryFetchGates(undefined, 'prefer-fresh-gates')
         }
       } catch (e) {
         logger.error(`session: resume failed`, {message: e})
diff --git a/src/components/FeedCard.tsx b/src/components/FeedCard.tsx
index 2745ed7c..94d97cb6 100644
--- a/src/components/FeedCard.tsx
+++ b/src/components/FeedCard.tsx
@@ -11,6 +11,7 @@ import {
   useRemoveFeedMutation,
 } from '#/state/queries/preferences'
 import {sanitizeHandle} from 'lib/strings/handles'
+import {useSession} from 'state/session'
 import {UserAvatar} from '#/view/com/util/UserAvatar'
 import * as Toast from 'view/com/util/Toast'
 import {useTheme} from '#/alf'
@@ -116,6 +117,12 @@ export function Likes({count}: {count: number}) {
 }
 
 export function Action({uri, pin}: {uri: string; pin?: boolean}) {
+  const {hasSession} = useSession()
+  if (!hasSession) return null
+  return <ActionInner uri={uri} pin={pin} />
+}
+
+function ActionInner({uri, pin}: {uri: string; pin?: boolean}) {
   const {_} = useLingui()
   const {data: preferences} = usePreferencesQuery()
   const {isPending: isAddSavedFeedPending, mutateAsync: saveFeeds} =
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index 4481935f..6e460dc6 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -1,5 +1,6 @@
 export type Gate =
   // Keep this alphabetic please.
+  | 'native_pwi_disabled'
   | 'request_notifications_permission_after_onboarding_v2'
   | 'show_avi_follow_button'
   | 'show_follow_back_label_v2'
diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx
index f6aed999..b5a239c3 100644
--- a/src/lib/statsig/statsig.tsx
+++ b/src/lib/statsig/statsig.tsx
@@ -14,6 +14,8 @@ import {useNonReactiveCallback} from '../hooks/useNonReactiveCallback'
 import {LogEvents} from './events'
 import {Gate} from './gates'
 
+const SDK_KEY = 'client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV'
+
 type StatsigUser = {
   userID: string | undefined
   // TODO: Remove when enough users have custom.platform:
@@ -251,7 +253,7 @@ AppState.addEventListener('change', (state: AppStateStatus) => {
 })
 
 export async function tryFetchGates(
-  did: string,
+  did: string | undefined,
   strategy: 'prefer-low-latency' | 'prefer-fresh-gates',
 ) {
   try {
@@ -275,6 +277,10 @@ export async function tryFetchGates(
   }
 }
 
+export function initialize() {
+  return Statsig.initialize(SDK_KEY, null, createStatsigOptions([]))
+}
+
 export function Provider({children}: {children: React.ReactNode}) {
   const {currentAccount, accounts} = useSession()
   const did = currentAccount?.did
@@ -320,7 +326,7 @@ export function Provider({children}: {children: React.ReactNode}) {
     <GateCache.Provider value={gateCache}>
       <StatsigProvider
         key={did}
-        sdkKey="client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV"
+        sdkKey={SDK_KEY}
         mountKey={currentStatsigUser.userID}
         user={currentStatsigUser}
         // This isn't really blocking due to short initTimeoutMs above.
diff --git a/src/screens/Onboarding/Layout.tsx b/src/screens/Onboarding/Layout.tsx
index 75e2f99f..02b20730 100644
--- a/src/screens/Onboarding/Layout.tsx
+++ b/src/screens/Onboarding/Layout.tsx
@@ -152,7 +152,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) {
               {children}
             </View>
 
-            <View style={{height: 200}} />
+            <View style={{height: 400}} />
           </View>
         </View>
       </ScrollView>
diff --git a/src/screens/Onboarding/StepProfile/PlaceholderCanvas.tsx b/src/screens/Onboarding/StepProfile/PlaceholderCanvas.tsx
index 29ba39a0..d1d1af6d 100644
--- a/src/screens/Onboarding/StepProfile/PlaceholderCanvas.tsx
+++ b/src/screens/Onboarding/StepProfile/PlaceholderCanvas.tsx
@@ -5,7 +5,7 @@ import ViewShot from 'react-native-view-shot'
 import {useAvatar} from '#/screens/Onboarding/StepProfile/index'
 import {atoms as a} from '#/alf'
 
-const SIZE_MULTIPLIER = 1.5
+const SIZE_MULTIPLIER = 5
 
 export interface PlaceholderCanvasRef {
   capture: () => Promise<string>
diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts
index 2981b41b..83d6a763 100644
--- a/src/state/queries/feed.ts
+++ b/src/state/queries/feed.ts
@@ -234,26 +234,28 @@ export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
         data: InfiniteData<AppBskyUnspeccedGetPopularFeedGenerators.OutputSchema>,
       ) => {
         const {savedFeeds, hasSession: hasSessionInner} = selectArgs
-        data?.pages.map(page => {
-          page.feeds = page.feeds.filter(feed => {
-            if (
-              !hasSessionInner &&
-              KNOWN_AUTHED_ONLY_FEEDS.includes(feed.uri)
-            ) {
-              return false
-            }
-            const alreadySaved = Boolean(
-              savedFeeds?.find(f => {
-                return f.value === feed.uri
+        return {
+          ...data,
+          pages: data.pages.map(page => {
+            return {
+              ...page,
+              feeds: page.feeds.filter(feed => {
+                if (
+                  !hasSessionInner &&
+                  KNOWN_AUTHED_ONLY_FEEDS.includes(feed.uri)
+                ) {
+                  return false
+                }
+                const alreadySaved = Boolean(
+                  savedFeeds?.find(f => {
+                    return f.value === feed.uri
+                  }),
+                )
+                return !alreadySaved
               }),
-            )
-            return !alreadySaved
-          })
-
-          return page
-        })
-
-        return data
+            }
+          }),
+        }
       },
       [selectArgs /* Don't change. Everything needs to go into selectArgs. */],
     ),
diff --git a/src/view/screens/Search/Explore.tsx b/src/view/screens/Search/Explore.tsx
index f6e99883..c7f5f939 100644
--- a/src/view/screens/Search/Explore.tsx
+++ b/src/view/screens/Search/Explore.tsx
@@ -16,18 +16,17 @@ import {useModerationOpts} from '#/state/preferences/moderation-opts'
 import {useGetPopularFeedsQuery} from '#/state/queries/feed'
 import {usePreferencesQuery} from '#/state/queries/preferences'
 import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows'
-import {useSession} from '#/state/session'
 import {cleanError} from 'lib/strings/errors'
 import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
 import {List} from '#/view/com/util/List'
 import {UserAvatar} from '#/view/com/util/UserAvatar'
-import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard'
 import {
   FeedFeedLoadingPlaceholder,
   ProfileCardFeedLoadingPlaceholder,
 } from 'view/com/util/LoadingPlaceholder'
 import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
 import {Button} from '#/components/Button'
+import * as FeedCard from '#/components/FeedCard'
 import {ArrowBottom_Stroke2_Corner0_Rounded as ArrowBottom} from '#/components/icons/Arrow'
 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
 import {Props as SVGIconProps} from '#/components/icons/common'
@@ -271,7 +270,6 @@ type ExploreScreenItems =
 export function Explore() {
   const {_} = useLingui()
   const t = useTheme()
-  const {hasSession} = useSession()
   const {data: preferences, error: preferencesError} = usePreferencesQuery()
   const moderationOpts = useModerationOpts()
   const {
@@ -480,15 +478,14 @@ export function Explore() {
         }
         case 'feed': {
           return (
-            <View style={[a.border_b, t.atoms.border_contrast_low]}>
-              <FeedSourceCard
-                feedUri={item.feed.uri}
-                showSaveBtn={hasSession}
-                showDescription
-                showLikes
-                pinOnSave
-                hideTopBorder
-              />
+            <View
+              style={[
+                a.border_b,
+                t.atoms.border_contrast_low,
+                a.px_lg,
+                a.py_lg,
+              ]}>
+              <FeedCard.Default feed={item.feed} />
             </View>
           )
         }
@@ -538,7 +535,7 @@ export function Explore() {
         }
       }
     },
-    [t, hasSession, moderationOpts],
+    [t, moderationOpts],
   )
 
   return (
diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx
index ed132d24..0b1fe37a 100644
--- a/src/view/screens/Search/Search.tsx
+++ b/src/view/screens/Search/Search.tsx
@@ -30,7 +30,7 @@ import {makeProfileLink} from '#/lib/routes/links'
 import {NavigationProp} from '#/lib/routes/types'
 import {augmentSearchQuery} from '#/lib/strings/helpers'
 import {logger} from '#/logger'
-import {isIOS, isNative, isWeb} from '#/platform/detection'
+import {isNative, isWeb} from '#/platform/detection'
 import {listenSoftReset} from '#/state/events'
 import {useModerationOpts} from '#/state/preferences/moderation-opts'
 import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
@@ -57,8 +57,8 @@ import {Text} from '#/view/com/util/text/Text'
 import {CenteredView, ScrollView} from '#/view/com/util/Views'
 import {Explore} from '#/view/screens/Search/Explore'
 import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search'
-import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard'
-import {atoms as a} from '#/alf'
+import {atoms as a, useTheme as useThemeNew} from '#/alf'
+import * as FeedCard from '#/components/FeedCard'
 import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
 
 function Loader() {
@@ -285,8 +285,8 @@ let SearchScreenFeedsResults = ({
   query: string
   active: boolean
 }): React.ReactNode => {
+  const t = useThemeNew()
   const {_} = useLingui()
-  const {hasSession} = useSession()
 
   const {data: results, isFetched} = usePopularFeedsSearch({
     query,
@@ -299,13 +299,15 @@ let SearchScreenFeedsResults = ({
         <List
           data={results}
           renderItem={({item}) => (
-            <FeedSourceCard
-              feedUri={item.uri}
-              showSaveBtn={hasSession}
-              showDescription
-              showLikes
-              pinOnSave
-            />
+            <View
+              style={[
+                a.border_b,
+                t.atoms.border_contrast_low,
+                a.px_lg,
+                a.py_lg,
+              ]}>
+              <FeedCard.Default feed={item} />
+            </View>
           )}
           keyExtractor={item => item.uri}
           // @ts-ignore web only -prf
@@ -802,12 +804,6 @@ let SearchInputBox = ({
             })
           } else {
             setShowAutocomplete(true)
-            if (isIOS) {
-              // We rely on selectTextOnFocus, but it's broken on iOS:
-              // https://github.com/facebook/react-native/issues/41988
-              textInput.current?.setSelection(0, searchText.length)
-              // We still rely on selectTextOnFocus for it to be instant on Android.
-            }
           }
         }}
         onChangeText={onChangeText}
diff --git a/src/view/shell/createNativeStackNavigatorWithAuth.tsx b/src/view/shell/createNativeStackNavigatorWithAuth.tsx
index 3c611351..82dd6d22 100644
--- a/src/view/shell/createNativeStackNavigatorWithAuth.tsx
+++ b/src/view/shell/createNativeStackNavigatorWithAuth.tsx
@@ -29,7 +29,8 @@ import {
   useLoggedOutView,
   useLoggedOutViewControls,
 } from '#/state/shell/logged-out'
-import {isWeb} from 'platform/detection'
+import {useGate} from 'lib/statsig/statsig'
+import {isNative, isWeb} from 'platform/detection'
 import {Deactivated} from '#/screens/Deactivated'
 import {Onboarding} from '#/screens/Onboarding'
 import {SignupQueued} from '#/screens/SignupQueued'
@@ -50,6 +51,7 @@ function NativeStackNavigator({
   screenOptions,
   ...rest
 }: NativeStackNavigatorProps) {
+  const gate = useGate()
   // --- this is copy and pasted from the original native stack navigator ---
   const {state, descriptors, navigation, NavigationContent} =
     useNavigationBuilder<
@@ -100,7 +102,11 @@ function NativeStackNavigator({
   const {showLoggedOut} = useLoggedOutView()
   const {setShowLoggedOut} = useLoggedOutViewControls()
   const {isMobile, isTabletOrMobile} = useWebMediaQueries()
-  if ((!PWI_ENABLED || activeRouteRequiresAuth) && !hasSession) {
+  const isNativePWIDisabled = isNative && gate('native_pwi_disabled')
+  if (
+    (!PWI_ENABLED || isNativePWIDisabled || activeRouteRequiresAuth) &&
+    !hasSession
+  ) {
     return <LoggedOut />
   }
   if (hasSession && currentAccount?.signupQueued) {