diff --git a/.agents/skills/building-native-ui/SKILL.md b/.agents/skills/building-native-ui/SKILL.md
new file mode 100644
index 0000000..9a9df4c
--- /dev/null
+++ b/.agents/skills/building-native-ui/SKILL.md
@@ -0,0 +1,321 @@
+---
+name: building-native-ui
+description: Complete guide for building beautiful apps with Expo Router. Covers fundamentals, styling, components, navigation, animations, patterns, and native tabs.
+version: 1.0.1
+license: MIT
+---
+
+# Expo UI Guidelines
+
+## References
+
+Consult these resources as needed:
+
+```
+references/
+ animations.md Reanimated: entering, exiting, layout, scroll-driven, gestures
+ controls.md Native iOS: Switch, Slider, SegmentedControl, DateTimePicker, Picker
+ form-sheet.md Form sheets in expo-router: configuration, footers and background interaction.
+ gradients.md CSS gradients via experimental_backgroundImage (New Arch only)
+ icons.md SF Symbols via expo-image (sf: source), names, animations, weights
+ media.md Camera, audio, video, and file saving
+ route-structure.md Route conventions, dynamic routes, groups, folder organization
+ search.md Search bar with headers, useSearch hook, filtering patterns
+ storage.md SQLite, AsyncStorage, SecureStore
+ tabs.md NativeTabs, migration from JS tabs, iOS 26 features
+ toolbar-and-headers.md Stack headers and toolbar buttons, menus, search (iOS only)
+ visual-effects.md Blur (expo-blur) and liquid glass (expo-glass-effect)
+ webgpu-three.md 3D graphics, games, GPU visualizations with WebGPU and Three.js
+ zoom-transitions.md Apple Zoom: fluid zoom transitions with Link.AppleZoom (iOS 18+)
+```
+
+## Running the App
+
+**CRITICAL: Always try Expo Go first before creating custom builds.**
+
+Most Expo apps work in Expo Go without any custom native code. Before running `npx expo run:ios` or `npx expo run:android`:
+
+1. **Start with Expo Go**: Run `npx expo start` and scan the QR code with Expo Go
+2. **Check if features work**: Test your app thoroughly in Expo Go
+3. **Only create custom builds when required** - see below
+
+### When Custom Builds Are Required
+
+You need `npx expo run:ios/android` or `eas build` ONLY when using:
+
+- **Local Expo modules** (custom native code in `modules/`)
+- **Apple targets** (widgets, app clips, extensions via `@bacons/apple-targets`)
+- **Third-party native modules** not included in Expo Go
+- **Custom native configuration** that can't be expressed in `app.json`
+
+### When Expo Go Works
+
+Expo Go supports a huge range of features out of the box:
+
+- All `expo-*` packages (camera, location, notifications, etc.)
+- Expo Router navigation
+- Most UI libraries (reanimated, gesture handler, etc.)
+- Push notifications, deep links, and more
+
+**If you're unsure, try Expo Go first.** Creating custom builds adds complexity, slower iteration, and requires Xcode/Android Studio setup.
+
+## Code Style
+
+- Be cautious of unterminated strings. Ensure nested backticks are escaped; never forget to escape quotes correctly.
+- Always use import statements at the top of the file.
+- Always use kebab-case for file names, e.g. `comment-card.tsx`
+- Always remove old route files when moving or restructuring navigation
+- Never use special characters in file names
+- Configure tsconfig.json with path aliases, and prefer aliases over relative imports for refactors.
+
+## Routes
+
+See `./references/route-structure.md` for detailed route conventions.
+
+- Routes belong in the `app` directory.
+- Never co-locate components, types, or utilities in the app directory. This is an anti-pattern.
+- Ensure the app always has a route that matches "/", it may be inside a group route.
+
+## Library Preferences
+
+- Never use modules removed from React Native such as Picker, WebView, SafeAreaView, or AsyncStorage
+- Never use legacy expo-permissions
+- `expo-audio` not `expo-av`
+- `expo-video` not `expo-av`
+- `expo-image` with `source="sf:name"` for SF Symbols, not `expo-symbols` or `@expo/vector-icons`
+- `react-native-safe-area-context` not react-native SafeAreaView
+- `process.env.EXPO_OS` not `Platform.OS`
+- `React.use` not `React.useContext`
+- `expo-image` Image component instead of intrinsic element `img`
+- `expo-glass-effect` for liquid glass backdrops
+
+## Responsiveness
+
+- Always wrap root component in a scroll view for responsiveness
+- Use `` instead of `` for smarter safe area insets
+- `contentInsetAdjustmentBehavior="automatic"` should be applied to FlatList and SectionList as well
+- Use flexbox instead of Dimensions API
+- ALWAYS prefer `useWindowDimensions` over `Dimensions.get()` to measure screen size
+
+## Behavior
+
+- Use expo-haptics conditionally on iOS to make more delightful experiences
+- Use views with built-in haptics like `` from React Native and `@react-native-community/datetimepicker`
+- When a route belongs to a Stack, its first child should almost always be a ScrollView with `contentInsetAdjustmentBehavior="automatic"` set
+- When adding a `ScrollView` to the page it should almost always be the first component inside the route component
+- Prefer `headerSearchBarOptions` in Stack.Screen options to add a search bar
+- Use the `` prop on text containing data that could be copied
+- Consider formatting large numbers like 1.4M or 38k
+- Never use intrinsic elements like 'img' or 'div' unless in a webview or Expo DOM component
+
+# Styling
+
+Follow Apple Human Interface Guidelines.
+
+## General Styling Rules
+
+- Prefer flex gap over margin and padding styles
+- Prefer padding over margin where possible
+- Always account for safe area, either with stack headers, tabs, or ScrollView/FlatList `contentInsetAdjustmentBehavior="automatic"`
+- Ensure both top and bottom safe area insets are accounted for
+- Inline styles not StyleSheet.create unless reusing styles is faster
+- Add entering and exiting animations for state changes
+- Use `{ borderCurve: 'continuous' }` for rounded corners unless creating a capsule shape
+- ALWAYS use a navigation stack title instead of a custom text element on the page
+- When padding a ScrollView, use `contentContainerStyle` padding and gap instead of padding on the ScrollView itself (reduces clipping)
+- CSS and Tailwind are not supported - use inline styles
+
+## Text Styling
+
+- Add the `selectable` prop to every `` element displaying important data or error messages
+- Counters should use `{ fontVariant: 'tabular-nums' }` for alignment
+
+## Shadows
+
+Use CSS `boxShadow` style prop. NEVER use legacy React Native shadow or elevation styles.
+
+```tsx
+
+```
+
+'inset' shadows are supported.
+
+# Navigation
+
+## Link
+
+Use `` from 'expo-router' for navigation between routes.
+
+```tsx
+import { Link } from 'expo-router';
+
+// Basic link
+
+
+// Wrapping custom components
+
+ ...
+
+```
+
+Whenever possible, include a `` to follow iOS conventions. Add context menus and previews frequently to enhance navigation.
+
+## Stack
+
+- ALWAYS use `_layout.tsx` files to define stacks
+- Use Stack from 'expo-router/stack' for native navigation stacks
+
+### Page Title
+
+Set the page title in Stack.Screen options:
+
+```tsx
+
+```
+
+## Context Menus
+
+Add long press context menus to Link components:
+
+```tsx
+import { Link } from "expo-router";
+
+
+
+
+
+
+
+
+
+
+
+ {}} />
+ {}}
+ />
+
+
+;
+```
+
+## Link Previews
+
+Use link previews frequently to enhance navigation:
+
+```tsx
+
+
+
+
+
+
+
+
+```
+
+Link preview can be used with context menus.
+
+## Modal
+
+Present a screen as a modal:
+
+```tsx
+
+```
+
+Prefer this to building a custom modal component.
+
+## Sheet
+
+Present a screen as a dynamic form sheet:
+
+```tsx
+
+```
+
+- Using `contentStyle: { backgroundColor: "transparent" }` makes the background liquid glass on iOS 26+.
+
+## Common route structure
+
+A standard app layout with tabs and stacks inside each tab:
+
+```
+app/
+ _layout.tsx —
+ (index,search)/
+ _layout.tsx —
+ index.tsx — Main list
+ search.tsx — Search view
+```
+
+```tsx
+// app/_layout.tsx
+import { NativeTabs, Icon, Label } from "expo-router/unstable-native-tabs";
+import { Theme } from "../components/theme";
+
+export default function Layout() {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+Create a shared group route so both tabs can push common screens:
+
+```tsx
+// app/(index,search)/_layout.tsx
+import { Stack } from "expo-router/stack";
+import { PlatformColor } from "react-native";
+
+export default function Layout({ segment }) {
+ const screen = segment.match(/\((.*)\)/)?.[1]!;
+ const titles: Record = { index: "Items", search: "Search" };
+
+ return (
+
+
+
+
+ );
+}
+```
diff --git a/.agents/skills/building-native-ui/references/animations.md b/.agents/skills/building-native-ui/references/animations.md
new file mode 100644
index 0000000..657cad8
--- /dev/null
+++ b/.agents/skills/building-native-ui/references/animations.md
@@ -0,0 +1,220 @@
+# Animations
+
+Use Reanimated v4. Avoid React Native's built-in Animated API.
+
+## Entering and Exiting Animations
+
+Use Animated.View with entering and exiting animations. Layout animations can animate state changes.
+
+```tsx
+import Animated, {
+ FadeIn,
+ FadeOut,
+ LinearTransition,
+} from "react-native-reanimated";
+
+function App() {
+ return (
+
+ );
+}
+```
+
+## On-Scroll Animations
+
+Create high-performance scroll animations using Reanimated's hooks:
+
+```tsx
+import Animated, {
+ useAnimatedRef,
+ useScrollViewOffset,
+ useAnimatedStyle,
+ interpolate,
+} from "react-native-reanimated";
+
+function Page() {
+ const ref = useAnimatedRef();
+ const scroll = useScrollViewOffset(ref);
+
+ const style = useAnimatedStyle(() => ({
+ opacity: interpolate(scroll.value, [0, 30], [0, 1], "clamp"),
+ }));
+
+ return (
+
+
+
+ );
+}
+```
+
+## Common Animation Presets
+
+### Entering Animations
+
+- `FadeIn`, `FadeInUp`, `FadeInDown`, `FadeInLeft`, `FadeInRight`
+- `SlideInUp`, `SlideInDown`, `SlideInLeft`, `SlideInRight`
+- `ZoomIn`, `ZoomInUp`, `ZoomInDown`
+- `BounceIn`, `BounceInUp`, `BounceInDown`
+
+### Exiting Animations
+
+- `FadeOut`, `FadeOutUp`, `FadeOutDown`, `FadeOutLeft`, `FadeOutRight`
+- `SlideOutUp`, `SlideOutDown`, `SlideOutLeft`, `SlideOutRight`
+- `ZoomOut`, `ZoomOutUp`, `ZoomOutDown`
+- `BounceOut`, `BounceOutUp`, `BounceOutDown`
+
+### Layout Animations
+
+- `LinearTransition` — Smooth linear interpolation
+- `SequencedTransition` — Sequenced property changes
+- `FadingTransition` — Fade between states
+
+## Customizing Animations
+
+```tsx
+
+```
+
+### Modifiers
+
+```tsx
+// Duration in milliseconds
+FadeIn.duration(300);
+
+// Delay before starting
+FadeIn.delay(100);
+
+// Spring physics
+FadeIn.springify();
+FadeIn.springify().damping(15).stiffness(100);
+
+// Easing curves
+FadeIn.easing(Easing.bezier(0.25, 0.1, 0.25, 1));
+
+// Chaining
+FadeInDown.duration(400).delay(200).springify();
+```
+
+## Shared Value Animations
+
+For imperative control over animations:
+
+```tsx
+import {
+ useSharedValue,
+ withSpring,
+ withTiming,
+} from "react-native-reanimated";
+
+const offset = useSharedValue(0);
+
+// Spring animation
+offset.value = withSpring(100);
+
+// Timing animation
+offset.value = withTiming(100, { duration: 300 });
+
+// Use in styles
+const style = useAnimatedStyle(() => ({
+ transform: [{ translateX: offset.value }],
+}));
+```
+
+## Gesture Animations
+
+Combine with React Native Gesture Handler:
+
+```tsx
+import { Gesture, GestureDetector } from "react-native-gesture-handler";
+import Animated, {
+ useSharedValue,
+ useAnimatedStyle,
+ withSpring,
+} from "react-native-reanimated";
+
+function DraggableBox() {
+ const translateX = useSharedValue(0);
+ const translateY = useSharedValue(0);
+
+ const gesture = Gesture.Pan()
+ .onUpdate((e) => {
+ translateX.value = e.translationX;
+ translateY.value = e.translationY;
+ })
+ .onEnd(() => {
+ translateX.value = withSpring(0);
+ translateY.value = withSpring(0);
+ });
+
+ const style = useAnimatedStyle(() => ({
+ transform: [
+ { translateX: translateX.value },
+ { translateY: translateY.value },
+ ],
+ }));
+
+ return (
+
+
+
+ );
+}
+```
+
+## Keyboard Animations
+
+Animate with keyboard height changes:
+
+```tsx
+import Animated, {
+ useAnimatedKeyboard,
+ useAnimatedStyle,
+} from "react-native-reanimated";
+
+function KeyboardAwareView() {
+ const keyboard = useAnimatedKeyboard();
+
+ const style = useAnimatedStyle(() => ({
+ paddingBottom: keyboard.height.value,
+ }));
+
+ return {/* content */};
+}
+```
+
+## Staggered List Animations
+
+Animate list items with delays:
+
+```tsx
+{
+ items.map((item, index) => (
+
+
+
+ ));
+}
+```
+
+## Best Practices
+
+- Add entering and exiting animations for state changes
+- Use layout animations when items are added/removed from lists
+- Use `useAnimatedStyle` for scroll-driven animations
+- Prefer `interpolate` with "clamp" for bounded values
+- You can't pass PlatformColors to reanimated views or styles; use static colors instead
+- Keep animations under 300ms for responsive feel
+- Use spring animations for natural movement
+- Avoid animating layout properties (width, height) when possible — prefer transforms
diff --git a/.agents/skills/building-native-ui/references/controls.md b/.agents/skills/building-native-ui/references/controls.md
new file mode 100644
index 0000000..762fe20
--- /dev/null
+++ b/.agents/skills/building-native-ui/references/controls.md
@@ -0,0 +1,270 @@
+# Native Controls
+
+Native iOS controls provide built-in haptics, accessibility, and platform-appropriate styling.
+
+## Switch
+
+Use for binary on/off settings. Has built-in haptics.
+
+```tsx
+import { Switch } from "react-native";
+import { useState } from "react";
+
+const [enabled, setEnabled] = useState(false);
+
+;
+```
+
+### Customization
+
+```tsx
+
+```
+
+## Segmented Control
+
+Use for non-navigational tabs or mode selection. Avoid changing default colors.
+
+```tsx
+import SegmentedControl from "@react-native-segmented-control/segmented-control";
+import { useState } from "react";
+
+const [index, setIndex] = useState(0);
+
+ setIndex(nativeEvent.selectedSegmentIndex)}
+/>;
+```
+
+### Rules
+
+- Maximum 4 options — use a picker for more
+- Keep labels short (1-2 words)
+- Avoid custom colors — native styling adapts to dark mode
+
+### With Icons (iOS 14+)
+
+```tsx
+ setIndex(nativeEvent.selectedSegmentIndex)}
+/>
+```
+
+## Slider
+
+Continuous value selection.
+
+```tsx
+import Slider from "@react-native-community/slider";
+import { useState } from "react";
+
+const [value, setValue] = useState(0.5);
+
+;
+```
+
+### Customization
+
+```tsx
+
+```
+
+### Discrete Steps
+
+```tsx
+
+```
+
+## Date/Time Picker
+
+Compact pickers with popovers. Has built-in haptics.
+
+```tsx
+import DateTimePicker from "@react-native-community/datetimepicker";
+import { useState } from "react";
+
+const [date, setDate] = useState(new Date());
+
+ {
+ if (selectedDate) setDate(selectedDate);
+ }}
+ mode="datetime"
+/>;
+```
+
+### Modes
+
+- `date` — Date only
+- `time` — Time only
+- `datetime` — Date and time
+
+### Display Styles
+
+```tsx
+// Compact inline (default)
+
+
+// Spinner wheel
+
+
+// Full calendar
+
+```
+
+### Time Intervals
+
+```tsx
+
+```
+
+### Min/Max Dates
+
+```tsx
+
+```
+
+## Stepper
+
+Increment/decrement numeric values.
+
+```tsx
+import { Stepper } from "react-native";
+import { useState } from "react";
+
+const [count, setCount] = useState(0);
+
+;
+```
+
+## TextInput
+
+Native text input with various keyboard types.
+
+```tsx
+import { TextInput } from "react-native";
+
+
+```
+
+### Keyboard Types
+
+```tsx
+// Email
+
+
+// Phone
+
+
+// Number
+
+
+// Password
+
+
+// Search
+
+```
+
+### Multiline
+
+```tsx
+
+```
+
+## Picker (Wheel)
+
+For selection from many options (5+ items).
+
+```tsx
+import { Picker } from "@react-native-picker/picker";
+import { useState } from "react";
+
+const [selected, setSelected] = useState("js");
+
+
+
+
+
+
+;
+```
+
+## Best Practices
+
+- **Haptics**: Switch and DateTimePicker have built-in haptics — don't add extra
+- **Accessibility**: Native controls have proper accessibility labels by default
+- **Dark Mode**: Avoid custom colors — native styling adapts automatically
+- **Spacing**: Use consistent padding around controls (12-16pt)
+- **Labels**: Place labels above or to the left of controls
+- **Grouping**: Group related controls in sections with headers
diff --git a/.agents/skills/building-native-ui/references/form-sheet.md b/.agents/skills/building-native-ui/references/form-sheet.md
new file mode 100644
index 0000000..1ed80fb
--- /dev/null
+++ b/.agents/skills/building-native-ui/references/form-sheet.md
@@ -0,0 +1,253 @@
+# Form Sheets in Expo Router
+
+This skill covers implementing form sheets with footers using Expo Router's Stack navigator and react-native-screens.
+
+## Overview
+
+Form sheets are modal presentations that appear as a card sliding up from the bottom of the screen. They're ideal for:
+
+- Quick actions and confirmations
+- Settings panels
+- Login/signup flows
+- Action sheets with custom content
+
+**Requirements:**
+
+- Expo Router Stack navigator
+
+## Basic Usage
+
+### Form Sheet with Footer
+
+Configure the Stack.Screen with transparent backgrounds and sheet presentation:
+
+```tsx
+// app/_layout.tsx
+import { Stack } from "expo-router";
+
+export default function Layout() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+### Form Sheet Screen Content
+
+> Requires Expo SDK 55 or later.
+
+Use `flex: 1` to allow the content to fill available space, enabling footer positioning:
+
+```tsx
+// app/about.tsx
+import { View, Text, StyleSheet } from "react-native";
+
+export default function AboutSheet() {
+ return (
+
+ {/* Main content */}
+
+ Sheet Content
+
+
+ {/* Footer - stays at bottom */}
+
+ Footer Content
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ content: {
+ flex: 1,
+ padding: 16,
+ },
+ footer: {
+ padding: 16,
+ },
+});
+```
+
+### Formsheet with interactive content below
+
+Use `sheetLargestUndimmedDetentIndex` (zero-indexed) to keep content behind the form sheet interactive — e.g. letting users pan a map beneath it. Setting it to `1` allows interaction at the first two detents but dims on the third.
+
+```tsx
+// app/_layout.tsx
+import { Stack } from 'expo-router';
+
+export default function Layout() {
+ return (
+
+
+
+
+ )
+}
+```
+
+## Key Options
+
+| Option | Type | Description |
+| --------------------- | ---------- | ----------------------------------------------------------- |
+| `presentation` | `string` | Set to `'formSheet'` for sheet presentation |
+| `sheetGrabberVisible` | `boolean` | Shows the drag handle at the top of the sheet |
+| `sheetAllowedDetents` | `number[]` | Array of detent heights (0-1 range, e.g., `[0.25]` for 25%) |
+| `headerTransparent` | `boolean` | Makes header background transparent |
+| `contentStyle` | `object` | Style object for the screen content container |
+| `title` | `string` | Screen title (set to `''` for no title) |
+
+## Common Detent Values
+
+- `[0.25]` - Quarter sheet (compact actions)
+- `[0.5]` - Half sheet (medium content)
+- `[0.75]` - Three-quarter sheet (detailed forms)
+- `[0.25, 0.5, 1]` - Multiple stops (expandable sheet)
+
+## Complete Example
+
+```tsx
+// _layout.tsx
+import { Stack } from "expo-router";
+
+export default function Layout() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+```tsx
+// app/confirm.tsx
+import { View, Text, Pressable, StyleSheet } from "react-native";
+import { router } from "expo-router";
+
+export default function ConfirmSheet() {
+ return (
+
+
+ Confirm Action
+
+ Are you sure you want to proceed?
+
+
+
+
+ router.back()}>
+ Cancel
+
+ router.back()}>
+ Confirm
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ content: {
+ flex: 1,
+ padding: 20,
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ title: {
+ fontSize: 18,
+ fontWeight: "600",
+ marginBottom: 8,
+ },
+ description: {
+ fontSize: 14,
+ color: "#666",
+ textAlign: "center",
+ },
+ footer: {
+ flexDirection: "row",
+ padding: 16,
+ gap: 12,
+ },
+ cancelButton: {
+ flex: 1,
+ padding: 14,
+ borderRadius: 10,
+ backgroundColor: "#f0f0f0",
+ alignItems: "center",
+ },
+ cancelText: {
+ fontSize: 16,
+ fontWeight: "500",
+ },
+ confirmButton: {
+ flex: 1,
+ padding: 14,
+ borderRadius: 10,
+ backgroundColor: "#007AFF",
+ alignItems: "center",
+ },
+ confirmText: {
+ fontSize: 16,
+ fontWeight: "500",
+ color: "white",
+ },
+});
+```
+
+## Troubleshooting
+
+### Content not filling sheet
+
+Make sure the root View uses `flex: 1`:
+
+```tsx
+{/* content */}
+```
+
+### Sheet background showing through
+
+Set `contentStyle: { backgroundColor: 'transparent' }` in options and style your content container with the desired background color instead.
diff --git a/.agents/skills/building-native-ui/references/gradients.md b/.agents/skills/building-native-ui/references/gradients.md
new file mode 100644
index 0000000..329600d
--- /dev/null
+++ b/.agents/skills/building-native-ui/references/gradients.md
@@ -0,0 +1,106 @@
+# CSS Gradients
+
+> **New Architecture Only**: CSS gradients require React Native's New Architecture (Fabric). They are not available in the old architecture or Expo Go.
+
+Use CSS gradients with the `experimental_backgroundImage` style property.
+
+## Linear Gradients
+
+```tsx
+// Top to bottom
+
+
+// Left to right
+
+
+// Diagonal
+
+
+// Using degrees
+
+```
+
+## Radial Gradients
+
+```tsx
+// Circle at center
+
+
+// Ellipse
+
+
+// Positioned
+
+```
+
+## Multiple Gradients
+
+Stack multiple gradients by comma-separating them:
+
+```tsx
+
+```
+
+## Common Patterns
+
+### Overlay on Image
+
+```tsx
+
+
+
+
+```
+
+### Frosted Glass Effect
+
+```tsx
+
+```
+
+### Button Gradient
+
+```tsx
+
+ Submit
+
+```
+
+## Important Notes
+
+- Do NOT use `expo-linear-gradient` — use CSS gradients instead
+- Gradients are strings, not objects
+- Use `rgba()` for transparency, or `transparent` keyword
+- Color stops use percentages (0%, 50%, 100%)
+- Direction keywords: `to top`, `to bottom`, `to left`, `to right`, `to top left`, etc.
+- Degree values: `45deg`, `90deg`, `135deg`, etc.
diff --git a/.agents/skills/building-native-ui/references/icons.md b/.agents/skills/building-native-ui/references/icons.md
new file mode 100644
index 0000000..eebf674
--- /dev/null
+++ b/.agents/skills/building-native-ui/references/icons.md
@@ -0,0 +1,213 @@
+# Icons (SF Symbols)
+
+Use SF Symbols for native feel. Never use FontAwesome or Ionicons.
+
+## Basic Usage
+
+```tsx
+import { SymbolView } from "expo-symbols";
+import { PlatformColor } from "react-native";
+
+;
+```
+
+## Props
+
+```tsx
+
+```
+
+## Common Icons
+
+### Navigation & Actions
+- `house.fill` - home
+- `gear` - settings
+- `magnifyingglass` - search
+- `plus` - add
+- `xmark` - close
+- `chevron.left` - back
+- `chevron.right` - forward
+- `arrow.left` - back arrow
+- `arrow.right` - forward arrow
+
+### Media
+- `play.fill` - play
+- `pause.fill` - pause
+- `stop.fill` - stop
+- `backward.fill` - rewind
+- `forward.fill` - fast forward
+- `speaker.wave.2.fill` - volume
+- `speaker.slash.fill` - mute
+
+### Camera
+- `camera` - camera
+- `camera.fill` - camera filled
+- `arrow.triangle.2.circlepath` - flip camera
+- `photo` - gallery/photos
+- `bolt` - flash
+- `bolt.slash` - flash off
+
+### Communication
+- `message` - message
+- `message.fill` - message filled
+- `envelope` - email
+- `envelope.fill` - email filled
+- `phone` - phone
+- `phone.fill` - phone filled
+- `video` - video call
+- `video.fill` - video call filled
+
+### Social
+- `heart` - like
+- `heart.fill` - liked
+- `star` - favorite
+- `star.fill` - favorited
+- `hand.thumbsup` - thumbs up
+- `hand.thumbsdown` - thumbs down
+- `person` - profile
+- `person.fill` - profile filled
+- `person.2` - people
+- `person.2.fill` - people filled
+
+### Content Actions
+- `square.and.arrow.up` - share
+- `square.and.arrow.down` - download
+- `doc.on.doc` - copy
+- `trash` - delete
+- `pencil` - edit
+- `folder` - folder
+- `folder.fill` - folder filled
+- `bookmark` - bookmark
+- `bookmark.fill` - bookmarked
+
+### Status & Feedback
+- `checkmark` - success/done
+- `checkmark.circle.fill` - completed
+- `xmark.circle.fill` - error/failed
+- `exclamationmark.triangle` - warning
+- `info.circle` - info
+- `questionmark.circle` - help
+- `bell` - notification
+- `bell.fill` - notification filled
+
+### Misc
+- `ellipsis` - more options
+- `ellipsis.circle` - more in circle
+- `line.3.horizontal` - menu/hamburger
+- `slider.horizontal.3` - filters
+- `arrow.clockwise` - refresh
+- `location` - location
+- `location.fill` - location filled
+- `map` - map
+- `mappin` - pin
+- `clock` - time
+- `calendar` - calendar
+- `link` - link
+- `nosign` - block/prohibited
+
+## Animated Symbols
+
+```tsx
+
+```
+
+### Animation Effects
+
+- `bounce` - Bouncy animation
+- `pulse` - Pulsing effect
+- `variableColor` - Color cycling
+- `scale` - Scale animation
+
+```tsx
+// Bounce with direction
+animationSpec={{
+ effect: { type: "bounce", direction: "up" } // up | down
+}}
+
+// Pulse
+animationSpec={{
+ effect: { type: "pulse" }
+}}
+
+// Variable color (multicolor symbols)
+animationSpec={{
+ effect: {
+ type: "variableColor",
+ cumulative: true,
+ reversing: true
+ }
+}}
+```
+
+## Symbol Weights
+
+```tsx
+// Lighter weights
+
+
+
+
+// Default
+
+
+// Heavier weights
+
+
+
+
+
+```
+
+## Symbol Scales
+
+```tsx
+
+ // default
+
+```
+
+## Multicolor Symbols
+
+Some symbols support multiple colors:
+
+```tsx
+
+```
+
+## Finding Symbol Names
+
+1. Use the SF Symbols app on macOS (free from Apple)
+2. Search at https://developer.apple.com/sf-symbols/
+3. Symbol names use dot notation: `square.and.arrow.up`
+
+## Best Practices
+
+- Always use SF Symbols over vector icon libraries
+- Match symbol weight to nearby text weight
+- Use `.fill` variants for selected/active states
+- Use PlatformColor for tint to support dark mode
+- Keep icons at consistent sizes (16, 20, 24, 32)
diff --git a/.agents/skills/building-native-ui/references/media.md b/.agents/skills/building-native-ui/references/media.md
new file mode 100644
index 0000000..50c0ffb
--- /dev/null
+++ b/.agents/skills/building-native-ui/references/media.md
@@ -0,0 +1,198 @@
+# Media
+
+## Camera
+
+- Hide navigation headers when there's a full screen camera
+- Ensure to flip the camera with `mirror` to emulate social apps
+- Use liquid glass buttons on cameras
+- Icons: `arrow.triangle.2.circlepath` (flip), `photo` (gallery), `bolt` (flash)
+- Eagerly request camera permission
+- Lazily request media library permission
+
+```tsx
+import React, { useRef, useState } from "react";
+import { View, TouchableOpacity, Text, Alert } from "react-native";
+import { CameraView, CameraType, useCameraPermissions } from "expo-camera";
+import * as MediaLibrary from "expo-media-library";
+import * as ImagePicker from "expo-image-picker";
+import * as Haptics from "expo-haptics";
+import { SymbolView } from "expo-symbols";
+import { PlatformColor } from "react-native";
+import { GlassView } from "expo-glass-effect";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+
+function Camera({ onPicture }: { onPicture: (uri: string) => Promise }) {
+ const [permission, requestPermission] = useCameraPermissions();
+ const cameraRef = useRef(null);
+ const [type, setType] = useState("back");
+ const { bottom } = useSafeAreaInsets();
+
+ if (!permission?.granted) {
+ return (
+
+ Camera access is required
+
+
+ Grant Permission
+
+
+
+ );
+ }
+
+ const takePhoto = async () => {
+ await Haptics.selectionAsync();
+ if (!cameraRef.current) return;
+ const photo = await cameraRef.current.takePictureAsync({ quality: 0.8 });
+ await onPicture(photo.uri);
+ };
+
+ const selectPhoto = async () => {
+ await Haptics.selectionAsync();
+ const result = await ImagePicker.launchImageLibraryAsync({
+ mediaTypes: "images",
+ allowsEditing: false,
+ quality: 0.8,
+ });
+ if (!result.canceled && result.assets?.[0]) {
+ await onPicture(result.assets[0].uri);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ setType(t => t === "back" ? "front" : "back")} icon="arrow.triangle.2.circlepath" />
+
+
+
+ );
+}
+```
+
+## Audio Playback
+
+Use `expo-audio` not `expo-av`:
+
+```tsx
+import { useAudioPlayer } from 'expo-audio';
+
+const player = useAudioPlayer({ uri: 'https://stream.nightride.fm/rektory.mp3' });
+
+