diff --git a/client-app/app/(app)/feed/index.tsx b/client-app/app/(app)/feed/index.tsx
index ccfd673..4815ab1 100644
--- a/client-app/app/(app)/feed/index.tsx
+++ b/client-app/app/(app)/feed/index.tsx
@@ -21,7 +21,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { router } from 'expo-router';
import { TabHeader } from '@/components/TabHeader';
-import { PostCard } from '@/components/feed/PostCard';
+import { PostCard, PostSeparator } from '@/components/feed/PostCard';
import { useStore } from '@/lib/store';
import {
fetchTimeline, fetchForYou, fetchTrending, fetchStats, bumpView,
@@ -218,6 +218,7 @@ export default function FeedScreen() {
onDeleted={onDeleted}
/>
)}
+ ItemSeparatorComponent={PostSeparator}
refreshControl={
- {/* Floating compose button — pinned to the bottom-right corner
- with 14px side inset. Vertical offset clears the 5-icon NavBar
- (which lives below this view in the same layer) by sitting
- ~14px above its top edge. */}
- router.push('/(app)/compose' as never)}
- style={({ pressed }) => ({
+ {/* Floating compose button.
+ *
+ * Wrapped in a StyleSheet.absoluteFill container with pointerEvents
+ * "box-none" so only the FAB captures touches — taps anywhere else
+ * pass through to the FlatList below.
+ *
+ * Inside the wrapper, alignSelf: 'flex-end' pins to the right;
+ * bottom inset leaves ~14px clearance above the NavBar (≈56px tall
+ * + safe-area-bottom). Explicit `right: 14` is belt-and-braces
+ * for RTL / platform quirks where alignSelf alone might not pin. */}
+
-
-
+ router.push('/(app)/compose' as never)}
+ style={({ pressed }) => ({
+ position: 'absolute',
+ right: 14,
+ bottom: Math.max(insets.bottom, 8) + 70,
+ width: 56, height: 56,
+ borderRadius: 28,
+ backgroundColor: pressed ? '#1a8cd8' : '#1d9bf0',
+ alignItems: 'center', justifyContent: 'center',
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 0.5,
+ shadowRadius: 6,
+ elevation: 8,
+ })}
+ >
+
+
+
);
}
diff --git a/client-app/app/(app)/feed/tag/[tag].tsx b/client-app/app/(app)/feed/tag/[tag].tsx
index a262582..a5bafb4 100644
--- a/client-app/app/(app)/feed/tag/[tag].tsx
+++ b/client-app/app/(app)/feed/tag/[tag].tsx
@@ -14,7 +14,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Header } from '@/components/Header';
import { IconButton } from '@/components/IconButton';
-import { PostCard } from '@/components/feed/PostCard';
+import { PostCard, PostSeparator } from '@/components/feed/PostCard';
import { useStore } from '@/lib/store';
import { fetchHashtag, fetchStats, type FeedPostItem } from '@/lib/feed';
@@ -94,6 +94,7 @@ export default function HashtagScreen() {
onStatsChanged={onStatsChanged}
/>
)}
+ ItemSeparatorComponent={PostSeparator}
refreshControl={
({
flexDirection: 'row',
paddingHorizontal: 16,
- paddingTop: compact ? 12 : 16,
- paddingBottom: compact ? 14 : 18,
+ paddingTop: compact ? 14 : 18,
+ paddingBottom: compact ? 16 : 20,
backgroundColor: pressed ? '#080808' : 'transparent',
- borderBottomWidth: 1,
- borderBottomColor: '#222222',
})}
>
{/* Avatar column */}
@@ -321,6 +319,20 @@ const _imgKeep = Image;
export const PostCard = React.memo(PostCardInner);
+/**
+ * PostSeparator — visible divider line between post cards. Exported so
+ * every feed surface (timeline, author, hashtag, post detail) can pass
+ * it as ItemSeparatorComponent and get identical spacing / colour.
+ *
+ * The line colour (#2a2a2a) is the minimum grey that reads on OLED
+ * black under mobile-bright-mode gamma — go darker and the seam vanishes.
+ * Height 1 is one logical px (hairline on retina). No horizontal inset:
+ * Twitter runs the seam edge-to-edge and it looks cleaner than a gap.
+ */
+export function PostSeparator() {
+ return ;
+}
+
// ── Inline helpers ──────────────────────────────────────────────────────
/** ActionButton — small icon + optional label. */