diff --git a/src/App.tsx b/src/App.tsx
index 6acdf8f..9054f95 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,7 +7,7 @@ import { StatsBar } from './components/StatsBar'
import { FilterBar } from './components/FilterBar'
import { StoreCard } from './components/StoreCard'
import { StoreTable } from './components/StoreTable'
-import { MobileRolodex } from './components/MobileRolodex'
+import { MobileStoreList } from './components/MobileStoreList'
import { StoreModal } from './components/StoreModal'
import { DeleteConfirm } from './components/DeleteConfirm'
import { Pagination } from './components/Pagination'
@@ -186,9 +186,9 @@ export default function App() {
) : (
<>
- {/* Mobile: rolodex */}
+ {/* Mobile: animated expandable list */}
-
+
{/* Desktop: toggle between table and grid */}
diff --git a/src/components/MobileStoreList.tsx b/src/components/MobileStoreList.tsx
new file mode 100644
index 0000000..63f844f
--- /dev/null
+++ b/src/components/MobileStoreList.tsx
@@ -0,0 +1,169 @@
+import { useState, useRef, useEffect } from 'react'
+import { Pencil, Trash2, ChevronDown, Tag, Calendar, Layers } from 'lucide-react'
+import type { Store } from '../types'
+import { RouteBadge } from './RouteBadge'
+import { TagChip } from './TagChip'
+import { parseTags, formatDate } from '../utils'
+
+interface Props {
+ stores: Store[]
+ onEdit: (store: Store) => void
+ onDelete: (store: Store) => void
+}
+
+const ROUTE_GLOW: Record = {
+ auto: 'rgba(34, 197, 94, 0.12)',
+ ask: 'rgba(245, 158, 11, 0.12)',
+ verify: 'rgba(59, 130, 246, 0.12)',
+ ignore: 'rgba(107, 114, 128, 0.08)',
+}
+
+const ROUTE_BORDER: Record = {
+ auto: '#22c55e',
+ ask: '#f59e0b',
+ verify: '#3b82f6',
+ ignore: '#4b5563',
+}
+
+function StoreRow({
+ store,
+ index,
+ expanded,
+ onToggle,
+ onEdit,
+ onDelete,
+}: {
+ store: Store
+ index: number
+ expanded: boolean
+ onToggle: () => void
+ onEdit: (s: Store) => void
+ onDelete: (s: Store) => void
+}) {
+ const bodyRef = useRef(null)
+ const [bodyHeight, setBodyHeight] = useState(0)
+
+ useEffect(() => {
+ if (bodyRef.current) setBodyHeight(bodyRef.current.scrollHeight)
+ }, [store])
+
+ const tags = parseTags(store.tags)
+ const glow = ROUTE_GLOW[store.route] ?? ROUTE_GLOW.ignore
+ const border = ROUTE_BORDER[store.route] ?? ROUTE_BORDER.ignore
+
+ return (
+
+ {/* Row header — always visible */}
+
+
+ {/* Expandable body */}
+
+
+ {/* Divider */}
+
+
+ {/* Meta row */}
+
+
+
+ {store.category}
+
+
+
+ {formatDate(store.created_at)}
+
+
+
+ {/* Tags */}
+ {tags.length > 0 && (
+
+
+
+ {tags.map(t => )}
+
+
+ )}
+
+ {/* Actions */}
+
+
+
+
+
+
+
+ )
+}
+
+export function MobileStoreList({ stores, onEdit, onDelete }: Props) {
+ const [expandedId, setExpandedId] = useState(null)
+
+ // Reset expanded when store list changes (filter/search)
+ useEffect(() => { setExpandedId(null) }, [stores])
+
+ function toggle(id: number) {
+ setExpandedId(prev => prev === id ? null : id)
+ }
+
+ return (
+
+ {stores.map((store, i) => (
+ toggle(store.id)}
+ onEdit={onEdit}
+ onDelete={onDelete}
+ />
+ ))}
+
+ )
+}
diff --git a/src/index.css b/src/index.css
index 026c658..f2a42c4 100644
--- a/src/index.css
+++ b/src/index.css
@@ -51,3 +51,11 @@ body { margin: 0; }
to { opacity: 1; transform: translateX(0); }
}
.animate-slide-in { animation: slide-in 0.2s ease-out; }
+
+@keyframes row-in {
+ from { opacity: 0; transform: translateY(8px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+.animate-row-in {
+ animation: row-in 0.25s ease-out both;
+}