Index: app/(app)/add/actions.ts
===================================================================
--- app/(app)/add/actions.ts	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
+++ app/(app)/add/actions.ts	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
@@ -0,0 +1,290 @@
+'use server';
+
+import { auth } from '@/auth';
+import { redirect } from 'next/navigation';
+import { sql } from '@/app/lib/db';
+import { revalidatePath } from 'next/cache';
+
+// ─── Shared helpers ────────────────────────────────────────────
+
+export type ActionState = {
+    error?: string;
+    success?: string;
+} | undefined;
+
+async function requireUserId(): Promise<number> {
+    const session = await auth();
+    if (!session?.user?.id) {
+        redirect('/login');
+    }
+    const userId = Number(session.user.id);
+    if (!Number.isInteger(userId)) {
+        redirect('/login');
+    }
+    return userId;
+}
+
+// ─── Add Transaction Account ───────────────────────────────────
+
+export async function addTransactionAccount(
+    _prev: ActionState,
+    formData: FormData,
+): Promise<ActionState> {
+    const userId = await requireUserId();
+
+    const name = String(formData.get('name') ?? '').trim();
+    const balanceRaw = String(formData.get('balance') ?? '0').trim();
+
+    if (!name) {
+        return { error: 'Account name is required.' };
+    }
+
+    const balance = Number(balanceRaw);
+    if (Number.isNaN(balance)) {
+        return { error: 'Balance must be a number.' };
+    }
+
+    try {
+        await sql`
+            INSERT INTO transaction_account (account_name, balance, user_id)
+            VALUES (${name}, ${balance}, ${userId})
+        `;
+    } catch {
+        return { error: 'Failed to create account.' };
+    }
+
+    revalidatePath('/dashboard');
+    revalidatePath('/add');
+    return { success: `Account "${name}" created.` };
+}
+
+// ─── Add Tag ───────────────────────────────────────────────────
+
+export async function addTag(
+    _prev: ActionState,
+    formData: FormData,
+): Promise<ActionState> {
+    await requireUserId(); // auth guard only
+
+    const name = String(formData.get('name') ?? '').trim().toLowerCase();
+
+    if (!name) {
+        return { error: 'Tag name is required.' };
+    }
+    if (name.startsWith('__note:')) {
+        return { error: 'Reserved tag prefix.' };
+    }
+
+    // Check duplicate (case-insensitive)
+    const existing = await sql`
+        SELECT tag_id FROM tag WHERE LOWER(tag_name) = ${name}
+    `;
+    if (existing.length > 0) {
+        return { error: `Tag "${name}" already exists.` };
+    }
+
+    try {
+        await sql`INSERT INTO tag (tag_name) VALUES (${name})`;
+    } catch {
+        return { error: 'Failed to create tag.' };
+    }
+
+    revalidatePath('/add');
+    return { success: `Tag "${name}" created.` };
+}
+
+// ─── Add Transaction ───────────────────────────────────────────
+
+type BreakdownInput = {
+    type: 'from' | 'to';
+    accountId: number;
+    amount: number;
+};
+
+export async function addTransaction(
+    _prev: ActionState,
+    formData: FormData,
+): Promise<ActionState> {
+    const userId = await requireUserId();
+
+    const name = String(formData.get('name') ?? '').trim();
+    const date = String(formData.get('date') ?? '').trim();
+    const amountRaw = String(formData.get('amount') ?? '').trim();
+    const tagsJson = String(formData.get('tags') ?? '[]');
+    const note = String(formData.get('note') ?? '').trim();
+    const breakdownsJson = String(formData.get('breakdowns') ?? '[]');
+
+    // ── Validation ──
+    if (!name) {
+        return { error: 'Transaction name is required.' };
+    }
+    if (!date) {
+        return { error: 'Date is required.' };
+    }
+
+    const amount = Number(amountRaw);
+    if (!amountRaw || Number.isNaN(amount)) {
+        return { error: 'Amount must be a number.' };
+    }
+
+    let tags: string[];
+    try {
+        tags = JSON.parse(tagsJson);
+        if (!Array.isArray(tags)) {
+            throw new Error();
+        }
+    } catch {
+        return { error: 'Invalid tags data.' };
+    }
+
+    let breakdowns: BreakdownInput[];
+    try {
+        breakdowns = JSON.parse(breakdownsJson);
+        if (!Array.isArray(breakdowns)) {
+            throw new Error();
+        }
+    } catch {
+        return { error: 'Invalid breakdowns data.' };
+    }
+
+    // Validate each breakdown
+    for (const breakdown of breakdowns) {
+        if (!['from', 'to'].includes(breakdown.type)) {
+            return { error: 'Invalid breakdown type.' };
+        }
+        if (!Number.isFinite(breakdown.amount) || breakdown.amount <= 0) {
+            return { error: 'Breakdown amounts must be positive.' };
+        }
+        if (!Number.isInteger(breakdown.accountId)) {
+            return { error: 'Invalid account in breakdown.' };
+        }
+    }
+
+    // Verify all accounts belong to this user
+    if (breakdowns.length > 0) {
+        const accountIds = [...new Set(breakdowns.map((b) => b.accountId))];
+        const ownedAccounts = await sql`
+            SELECT transaction_account_id
+            FROM transaction_account
+            WHERE user_id = ${userId}
+              AND transaction_account_id = ANY(${accountIds}::int[])
+        `;
+        if (ownedAccounts.length !== accountIds.length) {
+            return { error: 'One or more accounts do not belong to you.' };
+        }
+    }
+
+    // Compute net_amount from breakdowns
+    let totalEarned = 0;
+    let totalSpent = 0;
+    for (const breakdown of breakdowns) {
+        if (breakdown.type === 'to') {
+            totalEarned += breakdown.amount;
+        }
+        else {
+            totalSpent += breakdown.amount;
+        }
+    }
+    const netAmount = totalEarned - totalSpent;
+
+    try {
+        // Use a SQL transaction for atomicity
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        await sql.begin(async (tx: any) => {
+            // 1. Insert transaction
+            const [txRow] = await tx`
+                INSERT INTO transaction (transaction_name, amount, net_amount, date)
+                VALUES (${name}, ${amount}, ${netAmount}, ${date})
+                RETURNING transaction_id
+            `;
+            const transactionId: number = txRow.transaction_id;
+
+            // 2. Insert breakdowns & update account balances
+            for (const breakdown of breakdowns) {
+                const spent = breakdown.type === 'from' ? breakdown.amount : 0;
+                const earned = breakdown.type === 'to' ? breakdown.amount : 0;
+
+                await tx`
+                    INSERT INTO transaction_breakdown
+                        (transaction_id, transaction_account_id, spent_amount, earned_amount)
+                    VALUES (${transactionId}, ${breakdown.accountId}, ${spent}, ${earned})
+                `;
+
+                // Update balance: earned increases, spent decreases
+                await tx`
+                    UPDATE transaction_account
+                    SET balance = balance + ${earned} - ${spent}
+                    WHERE transaction_account_id = ${breakdown.accountId}
+                `;
+            }
+
+            // 3. Handle tags
+            for (const tagName of tags) {
+                const trimmed = tagName.trim().toLowerCase();
+                if (!trimmed || trimmed.startsWith('__note:')) {
+                    continue;
+                }
+
+                // Find or insert tag
+                const existing = await tx`
+                    SELECT tag_id FROM tag WHERE LOWER(tag_name) = ${trimmed}
+                `;
+                let tagId: number;
+                if (existing.length > 0) {
+                    tagId = existing[0].tag_id;
+                } else {
+                    const [inserted] = await tx`
+                        INSERT INTO tag (tag_name) VALUES (${trimmed})
+                        RETURNING tag_id
+                    `;
+                    tagId = inserted.tag_id;
+                }
+
+                // Check if assignment already exists
+                const existingAssignment = await tx`
+                    SELECT tag_assigned_to_transaction_id
+                    FROM tag_assigned_to_transaction
+                    WHERE transaction_id = ${transactionId} AND tag_id = ${tagId}
+                `;
+                if (existingAssignment.length === 0) {
+                    await tx`
+                        INSERT INTO tag_assigned_to_transaction (transaction_id, tag_id)
+                        VALUES (${transactionId}, ${tagId})
+                    `;
+                }
+            }
+
+            // 4. Handle note as a special __note: tag
+            if (note) {
+                const noteTagName = `__note:${note}`;
+
+                const existingNote = await tx`
+                    SELECT tag_id FROM tag WHERE tag_name = ${noteTagName}
+                `;
+                let noteTagId: number;
+                if (existingNote.length > 0) {
+                    noteTagId = existingNote[0].tag_id;
+                } else {
+                    const [inserted] = await tx`
+                        INSERT INTO tag (tag_name) VALUES (${noteTagName})
+                        RETURNING tag_id
+                    `;
+                    noteTagId = inserted.tag_id;
+                }
+
+                await tx`
+                    INSERT INTO tag_assigned_to_transaction (transaction_id, tag_id)
+                    VALUES (${transactionId}, ${noteTagId})
+                `;
+            }
+        });
+    } catch (e) {
+        console.error('addTransaction error:', e);
+        return { error: 'Failed to create transaction.' };
+    }
+
+    revalidatePath('/dashboard');
+    revalidatePath('/history');
+    revalidatePath('/add');
+    return { success: `Transaction "${name}" created.` };
+}
Index: app/(app)/add/add-account-form.tsx
===================================================================
--- app/(app)/add/add-account-form.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
+++ app/(app)/add/add-account-form.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
@@ -0,0 +1,95 @@
+'use client';
+
+import { useActionState, useEffect, useRef } from 'react';
+import { addTransactionAccount, type ActionState } from './actions';
+import { poppins } from '@/app/ui/fonts';
+import {
+    CurrencyDollarIcon,
+    ExclamationCircleIcon,
+    CheckCircleIcon,
+} from '@heroicons/react/24/outline';
+import { Button } from '@/app/ui/button';
+
+export default function AddAccountForm() {
+    const [state, formAction, isPending] = useActionState<ActionState, FormData>(
+        addTransactionAccount,
+        undefined,
+    );
+    const formRef = useRef<HTMLFormElement>(null);
+
+    useEffect(() => {
+        if (state?.success) {
+            formRef.current?.reset();
+        }
+    }, [state]);
+
+    return (
+        <form ref={formRef} action={formAction} className="space-y-4">
+            {/* Account Name */}
+            <div>
+                <label className="block text-xs text-white/50 mb-1.5 pl-1">
+                    Account Name
+                </label>
+                <input
+                    name="name"
+                    type="text"
+                    required
+                    placeholder="e.g. Cash, Bank Card…"
+                    className={`${poppins.className}
+                        w-full h-12 rounded-xl
+                        bg-white/10 border border-white/15
+                        px-4 text-white text-sm
+                        placeholder:text-white/40
+                        focus:outline-none focus:ring-2 focus:ring-blue-500/50
+                    `}
+                />
+            </div>
+
+            {/* Initial Balance */}
+            <div>
+                <label className="block text-xs text-white/50 mb-1.5 pl-1">
+                    Initial Balance (optional)
+                </label>
+                <div className="relative">
+                    <input
+                        name="balance"
+                        type="number"
+                        step="any"
+                        defaultValue="0"
+                        placeholder="0"
+                        className={`${poppins.className}
+                            w-full h-12 rounded-xl
+                            bg-white/10 border border-white/15
+                            pl-10 pr-4 text-white text-sm
+                            placeholder:text-white/40
+                            focus:outline-none focus:ring-2 focus:ring-blue-500/50
+                        `}
+                    />
+                    <CurrencyDollarIcon className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-white/40" />
+                </div>
+            </div>
+
+            {/* Feedback */}
+            {state?.error && (
+                <div className="flex items-center gap-2 text-red-400 text-sm">
+                    <ExclamationCircleIcon className="h-4 w-4 shrink-0" />
+                    {state.error}
+                </div>
+            )}
+            {state?.success && (
+                <div className="flex items-center gap-2 text-green-400 text-sm">
+                    <CheckCircleIcon className="h-4 w-4 shrink-0" />
+                    {state.success}
+                </div>
+            )}
+
+            <Button
+                type="submit"
+                aria-disabled={isPending}
+                className="w-full justify-center h-12 rounded-xl text-sm font-semibold"
+            >
+                {isPending ? 'Creating…' : 'Create Account'}
+            </Button>
+        </form>
+    );
+}
Index: app/(app)/add/add-page-client.tsx
===================================================================
--- app/(app)/add/add-page-client.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
+++ app/(app)/add/add-page-client.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
@@ -0,0 +1,54 @@
+'use client';
+
+import { useState } from 'react';
+import FormSelector, { type FormType } from './form-selector';
+import AddAccountForm from './add-account-form';
+import AddTagForm from './add-tag-form';
+import AddTransactionForm from './add-transaction-form';
+import { poppins } from '@/app/ui/fonts';
+
+type AccountOption = { transaction_account_id: number; account_name: string | null };
+type TagOption = { tag_id: number; tag_name: string };
+
+export default function AddPageClient({
+    accounts,
+    allTags,
+}: {
+    accounts: AccountOption[];
+    allTags: TagOption[];
+}) {
+    const [formType, setFormType] = useState<FormType>('transaction');
+
+    const titles: Record<FormType, string> = {
+        transaction: 'Transaction',
+        account: 'Account',
+        tag: 'Tag',
+    };
+
+    return (
+        <div className="w-full px-6 pt-10 pb-10">
+            <h1
+                className={`${poppins.className}
+                    text-[32px] leading-tight tracking-tight
+                    font-semibold text-center text-white mb-6
+                `}
+            >
+                Add
+            </h1>
+
+            {/* Selector */}
+            <div className="mb-6">
+                <FormSelector value={formType} onChange={setFormType} />
+            </div>
+
+            {/* Form card */}
+            <div className="rounded-2xl bg-white/5 border border-white/10 p-5">
+                {formType === 'transaction' && (
+                    <AddTransactionForm accounts={accounts} allTags={allTags} />
+                )}
+                {formType === 'account' && <AddAccountForm />}
+                {formType === 'tag' && <AddTagForm />}
+            </div>
+        </div>
+    );
+}
Index: app/(app)/add/add-tag-form.tsx
===================================================================
--- app/(app)/add/add-tag-form.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
+++ app/(app)/add/add-tag-form.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
@@ -0,0 +1,77 @@
+'use client';
+
+import { useActionState, useEffect, useRef } from 'react';
+import { addTag, type ActionState } from './actions';
+import { poppins } from '@/app/ui/fonts';
+import {
+    TagIcon,
+    ExclamationCircleIcon,
+    CheckCircleIcon,
+} from '@heroicons/react/24/outline';
+import { Button } from '@/app/ui/button';
+
+export default function AddTagForm() {
+    const [state, formAction, isPending] = useActionState<ActionState, FormData>(
+        addTag,
+        undefined,
+    );
+    const formRef = useRef<HTMLFormElement>(null);
+
+    useEffect(() => {
+        if (state?.success) {
+            formRef.current?.reset();
+        }
+    }, [state]);
+
+    return (
+        <form ref={formRef} action={formAction} className="space-y-4">
+            {/* Tag Name */}
+            <div>
+                <label className="block text-xs text-white/50 mb-1.5 pl-1">
+                    Tag Name
+                </label>
+                <div className="relative">
+                    <input
+                        name="name"
+                        type="text"
+                        required
+                        placeholder="e.g. food, rent, salary…"
+                        className={`${poppins.className}
+                            w-full h-12 rounded-xl
+                            bg-white/10 border border-white/15
+                            pl-10 pr-4 text-white text-sm
+                            placeholder:text-white/40
+                            focus:outline-none focus:ring-2 focus:ring-blue-500/50
+                        `}
+                    />
+                    <TagIcon className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-white/40" />
+                </div>
+                <p className="text-xs text-white/30 mt-1.5 pl-1">
+                    Tags are shared across all users. Will be lowercased.
+                </p>
+            </div>
+
+            {/* Feedback */}
+            {state?.error && (
+                <div className="flex items-center gap-2 text-red-400 text-sm">
+                    <ExclamationCircleIcon className="h-4 w-4 shrink-0" />
+                    {state.error}
+                </div>
+            )}
+            {state?.success && (
+                <div className="flex items-center gap-2 text-green-400 text-sm">
+                    <CheckCircleIcon className="h-4 w-4 shrink-0" />
+                    {state.success}
+                </div>
+            )}
+
+            <Button
+                type="submit"
+                aria-disabled={isPending}
+                className="w-full justify-center h-12 rounded-xl text-sm font-semibold"
+            >
+                {isPending ? 'Creating…' : 'Create Tag'}
+            </Button>
+        </form>
+    );
+}
Index: app/(app)/add/add-transaction-form.tsx
===================================================================
--- app/(app)/add/add-transaction-form.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
+++ app/(app)/add/add-transaction-form.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
@@ -0,0 +1,443 @@
+'use client';
+
+import { useActionState, useEffect, useRef, useState } from 'react';
+import { addTransaction, type ActionState } from './actions';
+import { poppins } from '@/app/ui/fonts';
+import {
+    CalendarIcon,
+    PlusIcon,
+    XMarkIcon,
+    ExclamationCircleIcon,
+    CheckCircleIcon,
+    ArrowDownTrayIcon,
+    ArrowUpTrayIcon,
+} from '@heroicons/react/24/outline';
+import { Button } from '@/app/ui/button';
+
+type AccountOption = { transaction_account_id: number; account_name: string | null };
+type TagOption = { tag_id: number; tag_name: string };
+
+type Breakdown = {
+    id: number; // client-side key
+    type: 'from' | 'to';
+    accountId: number;
+    amount: string;
+};
+
+let nextId = 1;
+
+export default function AddTransactionForm({
+    accounts,
+    allTags,
+}: {
+    accounts: AccountOption[];
+    allTags: TagOption[];
+}) {
+    const [state, formAction, isPending] = useActionState<ActionState, FormData>(
+        addTransaction,
+        undefined,
+    );
+    const formRef = useRef<HTMLFormElement>(null);
+
+    // ── Breakdowns ──
+    const [breakdowns, setBreakdowns] = useState<Breakdown[]>([]);
+    const [showFieldMenu, setShowFieldMenu] = useState(false);
+    const fieldMenuRef = useRef<HTMLDivElement>(null);
+
+    function addBreakdown(type: 'from' | 'to') {
+        const defaultAccount = accounts[0]?.transaction_account_id ?? 0;
+        setBreakdowns((prev) => [
+            ...prev,
+            { id: nextId++, type, accountId: defaultAccount, amount: '' },
+        ]);
+        setShowFieldMenu(false);
+    }
+
+    function removeBreakdown(id: number) {
+        setBreakdowns((prev) => prev.filter((b) => b.id !== id));
+    }
+
+    function updateBreakdown(id: number, field: Partial<Breakdown>) {
+        setBreakdowns((prev) =>
+            prev.map((b) => (b.id === id ? { ...b, ...field } : b)),
+        );
+    }
+
+    // ── Tags ──
+    const [selectedTags, setSelectedTags] = useState<string[]>([]);
+    const [tagInput, setTagInput] = useState('');
+
+    function toggleTag(name: string) {
+        setSelectedTags((prev) =>
+            prev.includes(name)
+                ? prev.filter((t) => t !== name)
+                : [...prev, name],
+        );
+    }
+
+    function addCustomTag() {
+        const trimmed = tagInput.trim().toLowerCase();
+        if (trimmed && !selectedTags.includes(trimmed)) {
+            setSelectedTags((prev) => [...prev, trimmed]);
+        }
+        setTagInput('');
+    }
+
+    // ── Reset on success ──
+    useEffect(() => {
+        if (state?.success) {
+            formRef.current?.reset();
+            setBreakdowns([]);
+            setSelectedTags([]);
+            setTagInput('');
+        }
+    }, [state]);
+
+    // ── Close field menu on outside click ──
+    useEffect(() => {
+        function handler(e: MouseEvent) {
+            if (
+                fieldMenuRef.current &&
+                !fieldMenuRef.current.contains(e.target as Node)
+            ) {
+                setShowFieldMenu(false);
+            }
+        }
+        document.addEventListener('mousedown', handler);
+        return () => document.removeEventListener('mousedown', handler);
+    }, []);
+
+    // ── Build serialised data for hidden fields ──
+    const breakdownsPayload = breakdowns.map((b) => ({
+        type: b.type,
+        accountId: b.accountId,
+        amount: Number(b.amount) || 0,
+    }));
+
+    const inputClasses = `${poppins.className}
+        w-full h-12 rounded-xl
+        bg-white/10 border border-white/15
+        px-4 text-white text-sm
+        placeholder:text-white/40
+        focus:outline-none focus:ring-2 focus:ring-blue-500/50
+    `;
+
+    return (
+        <form ref={formRef} action={formAction} className="space-y-5">
+            {/* Hidden serialised fields */}
+            <input type="hidden" name="tags" value={JSON.stringify(selectedTags)} />
+            <input
+                type="hidden"
+                name="breakdowns"
+                value={JSON.stringify(breakdownsPayload)}
+            />
+
+            {/* Transaction Name */}
+            <div>
+                <label className="block text-xs text-white/50 mb-1.5 pl-1">
+                    Name
+                </label>
+                <input
+                    name="name"
+                    type="text"
+                    required
+                    placeholder="e.g. Grocery shopping"
+                    className={inputClasses}
+                />
+            </div>
+
+            {/* Date */}
+            <div>
+                <label className="block text-xs text-white/50 mb-1.5 pl-1">
+                    Date
+                </label>
+                <div className="relative">
+                    <input
+                        name="date"
+                        type="date"
+                        required
+                        defaultValue={new Date().toISOString().slice(0, 10)}
+                        className={`${inputClasses} pl-10 [color-scheme:dark]`}
+                    />
+                    <CalendarIcon className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-white/40" />
+                </div>
+            </div>
+
+            {/* Amount */}
+            <div>
+                <label className="block text-xs text-white/50 mb-1.5 pl-1">
+                    Amount
+                </label>
+                <input
+                    name="amount"
+                    type="number"
+                    step="any"
+                    required
+                    placeholder="0"
+                    className={inputClasses}
+                />
+            </div>
+
+            {/* ── Breakdown fields ── */}
+            {breakdowns.length > 0 && (
+                <div className="space-y-3">
+                    <span className="block text-xs text-white/50 pl-1">
+                        Account Breakdowns
+                    </span>
+                    {breakdowns.map((bd) => (
+                        <div
+                            key={bd.id}
+                            className="
+                                flex items-center gap-2
+                                rounded-xl bg-white/5 border border-white/10
+                                p-3
+                            "
+                        >
+                            {/* Type badge */}
+                            <span
+                                className={`
+                                    shrink-0 flex items-center gap-1
+                                    text-xs font-medium px-2 py-1 rounded-lg
+                                    ${bd.type === 'from'
+                                        ? 'bg-red-500/20 text-red-400'
+                                        : 'bg-green-500/20 text-green-400'
+                                    }
+                                `}
+                            >
+                                {bd.type === 'from' ? (
+                                    <ArrowUpTrayIcon className="h-3.5 w-3.5" />
+                                ) : (
+                                    <ArrowDownTrayIcon className="h-3.5 w-3.5" />
+                                )}
+                                {bd.type === 'from' ? 'From' : 'To'}
+                            </span>
+
+                            {/* Account Select */}
+                            <select
+                                value={bd.accountId}
+                                onChange={(e) =>
+                                    updateBreakdown(bd.id, {
+                                        accountId: Number(e.target.value),
+                                    })
+                                }
+                                className={`${poppins.className}
+                                    flex-1 min-w-0 h-9 rounded-lg
+                                    bg-white/10 border border-white/15
+                                    px-2 text-white text-xs
+                                    focus:outline-none focus:ring-1 focus:ring-blue-500/50
+                                `}
+                            >
+                                {accounts.map((a) => (
+                                    <option
+                                        key={a.transaction_account_id}
+                                        value={a.transaction_account_id}
+                                        className="bg-[#1a1a2e] text-white"
+                                    >
+                                        {a.account_name ?? `Account #${a.transaction_account_id}`}
+                                    </option>
+                                ))}
+                            </select>
+
+                            {/* Amount */}
+                            <input
+                                type="number"
+                                step="any"
+                                placeholder="0"
+                                value={bd.amount}
+                                onChange={(e) =>
+                                    updateBreakdown(bd.id, { amount: e.target.value })
+                                }
+                                className={`${poppins.className}
+                                    w-20 h-9 rounded-lg
+                                    bg-white/10 border border-white/15
+                                    px-2 text-white text-xs text-right
+                                    placeholder:text-white/40
+                                    focus:outline-none focus:ring-1 focus:ring-blue-500/50
+                                `}
+                            />
+
+                            {/* Remove */}
+                            <button
+                                type="button"
+                                onClick={() => removeBreakdown(bd.id)}
+                                className="shrink-0 p-1 rounded-lg hover:bg-white/10 transition-colors"
+                            >
+                                <XMarkIcon className="h-4 w-4 text-white/40" />
+                            </button>
+                        </div>
+                    ))}
+                </div>
+            )}
+
+            {/* Add a Field button */}
+            <div ref={fieldMenuRef} className="relative">
+                <button
+                    type="button"
+                    onClick={() => setShowFieldMenu(!showFieldMenu)}
+                    className="
+                        flex items-center gap-1.5
+                        text-sm text-blue-400 hover:text-blue-300
+                        transition-colors
+                    "
+                >
+                    <PlusIcon className="h-4 w-4" />
+                    Add a field
+                </button>
+
+                {showFieldMenu && (
+                    <div
+                        className="
+                            absolute z-40 mt-1 left-0
+                            w-48 rounded-xl
+                            bg-[#1a1a2e] border border-white/15
+                            shadow-xl overflow-hidden
+                        "
+                    >
+                        <button
+                            type="button"
+                            onClick={() => addBreakdown('from')}
+                            className="
+                                w-full flex items-center gap-2 px-4 py-3
+                                text-sm text-white/80 hover:bg-white/10
+                                transition-colors
+                            "
+                        >
+                            <ArrowUpTrayIcon className="h-4 w-4 text-red-400" />
+                            From Account
+                        </button>
+                        <button
+                            type="button"
+                            onClick={() => addBreakdown('to')}
+                            className="
+                                w-full flex items-center gap-2 px-4 py-3
+                                text-sm text-white/80 hover:bg-white/10
+                                transition-colors
+                            "
+                        >
+                            <ArrowDownTrayIcon className="h-4 w-4 text-green-400" />
+                            To Account
+                        </button>
+                    </div>
+                )}
+            </div>
+
+            {/* ── Tags ── */}
+            <div>
+                <label className="block text-xs text-white/50 mb-1.5 pl-1">
+                    Tags
+                </label>
+
+                {/* Existing tags as toggleable pills */}
+                <div className="flex flex-wrap gap-1.5 mb-2">
+                    {allTags.map((t) => {
+                        const active = selectedTags.includes(t.tag_name);
+                        return (
+                            <button
+                                key={t.tag_id}
+                                type="button"
+                                onClick={() => toggleTag(t.tag_name)}
+                                className={`
+                                    px-2.5 py-1 rounded-full text-xs font-medium
+                                    transition-colors border
+                                    ${active
+                                        ? 'bg-blue-500/30 border-blue-400/50 text-blue-300'
+                                        : 'bg-white/5 border-white/10 text-white/50 hover:bg-white/10'
+                                    }
+                                `}
+                            >
+                                {t.tag_name}
+                            </button>
+                        );
+                    })}
+                </div>
+
+                {/* Custom tag pills (not from DB yet) */}
+                {selectedTags
+                    .filter((s) => !allTags.some((t) => t.tag_name === s))
+                    .map((custom) => (
+                        <span
+                            key={custom}
+                            className="
+                                inline-flex items-center gap-1
+                                mr-1.5 mb-1.5
+                                px-2.5 py-1 rounded-full text-xs font-medium
+                                bg-blue-500/30 border border-blue-400/50 text-blue-300
+                            "
+                        >
+                            {custom}
+                            <button
+                                type="button"
+                                onClick={() => toggleTag(custom)}
+                                className="hover:text-white"
+                            >
+                                <XMarkIcon className="h-3 w-3" />
+                            </button>
+                        </span>
+                    ))}
+
+                {/* Input to add new tag */}
+                <input
+                    type="text"
+                    value={tagInput}
+                    onChange={(e) => setTagInput(e.target.value)}
+                    onKeyDown={(e) => {
+                        if (e.key === 'Enter') {
+                            e.preventDefault();
+                            addCustomTag();
+                        }
+                    }}
+                    placeholder="Type a new tag + Enter"
+                    className={`${poppins.className}
+                        w-full h-10 rounded-xl
+                        bg-white/5 border border-white/10
+                        px-3 text-white text-xs
+                        placeholder:text-white/30
+                        focus:outline-none focus:ring-1 focus:ring-blue-500/50
+                    `}
+                />
+            </div>
+
+            {/* ── Note ── */}
+            <div>
+                <label className="block text-xs text-white/50 mb-1.5 pl-1">
+                    Note (optional)
+                </label>
+                <textarea
+                    name="note"
+                    rows={2}
+                    placeholder="Any extra details…"
+                    className={`${poppins.className}
+                        w-full rounded-xl
+                        bg-white/10 border border-white/15
+                        px-4 py-3 text-white text-sm
+                        placeholder:text-white/40
+                        focus:outline-none focus:ring-2 focus:ring-blue-500/50
+                        resize-none
+                    `}
+                />
+            </div>
+
+            {/* ── Feedback ── */}
+            {state?.error && (
+                <div className="flex items-center gap-2 text-red-400 text-sm">
+                    <ExclamationCircleIcon className="h-4 w-4 shrink-0" />
+                    {state.error}
+                </div>
+            )}
+            {state?.success && (
+                <div className="flex items-center gap-2 text-green-400 text-sm">
+                    <CheckCircleIcon className="h-4 w-4 shrink-0" />
+                    {state.success}
+                </div>
+            )}
+
+            <Button
+                type="submit"
+                aria-disabled={isPending}
+                className="w-full justify-center h-12 rounded-xl text-sm font-semibold"
+            >
+                {isPending ? 'Creating…' : 'Create Transaction'}
+            </Button>
+        </form>
+    );
+}
Index: app/(app)/add/form-selector.tsx
===================================================================
--- app/(app)/add/form-selector.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
+++ app/(app)/add/form-selector.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
@@ -0,0 +1,88 @@
+'use client';
+
+import { useState, useRef, useEffect } from 'react';
+import { ChevronDownIcon } from '@heroicons/react/24/outline';
+import { poppins } from '@/app/ui/fonts';
+
+const FORM_OPTIONS = [
+    { value: 'transaction', label: 'Transaction' },
+    { value: 'account', label: 'Transaction Account' },
+    { value: 'tag', label: 'Tag' },
+] as const;
+
+export type FormType = (typeof FORM_OPTIONS)[number]['value'];
+
+export default function FormSelector({
+    value,
+    onChange,
+}: {
+    value: FormType;
+    onChange: (v: FormType) => void;
+}) {
+    const [open, setOpen] = useState(false);
+    const ref = useRef<HTMLDivElement>(null);
+
+    useEffect(() => {
+        function handleClick(e: MouseEvent) {
+            if (ref.current && !ref.current.contains(e.target as Node)) {
+                setOpen(false);
+            }
+        }
+        document.addEventListener('mousedown', handleClick);
+        return () => document.removeEventListener('mousedown', handleClick);
+    }, []);
+
+    const currentLabel = FORM_OPTIONS.find((o) => o.value === value)?.label;
+
+    return (
+        <div ref={ref} className="relative w-full">
+            <button
+                type="button"
+                onClick={() => setOpen(!open)}
+                className={`${poppins.className}
+                    w-full flex items-center justify-center
+                    h-12 rounded-xl
+                    bg-white/10 border border-white/15
+                    px-4 text-white text-2xl
+                    transition-colors hover:bg-white/15
+                `}
+            >
+                <span className='pr-2'>{currentLabel}</span>
+                <ChevronDownIcon
+                    className={`h-4 w-4 text-white/60 transition-transform ${open ? 'rotate-180' : ''
+                        }`}
+                />
+            </button>
+
+            {open && (
+                <div
+                    className="
+                        absolute z-50 mt-1 w-full
+                        rounded-xl bg-[#1a1a2e] border border-white/15
+                        shadow-xl overflow-hidden
+                    "
+                >
+                    {FORM_OPTIONS.map((opt) => (
+                        <button
+                            key={opt.value}
+                            type="button"
+                            onClick={() => {
+                                onChange(opt.value);
+                                setOpen(false);
+                            }}
+                            className={`
+                                w-full text-left px-4 py-3 text-sm transition-colors
+                                ${opt.value === value
+                                    ? 'bg-blue-500/20 text-blue-400'
+                                    : 'text-white/80 hover:bg-white/10'
+                                }
+                            `}
+                        >
+                            {opt.label}
+                        </button>
+                    ))}
+                </div>
+            )}
+        </div>
+    );
+}
Index: app/(app)/add/page.tsx
===================================================================
--- app/(app)/add/page.tsx	(revision f5f1d898d8749982f099b71786a2b8aebb909be1)
+++ app/(app)/add/page.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
@@ -1,24 +1,23 @@
-import { poppins } from '@/app/ui/fonts';
+import { auth } from '@/auth';
+import { redirect } from 'next/navigation';
+import { getUserTransactionAccounts, getAllTags } from '@/app/lib/queries';
+import AddPageClient from './add-page-client';
 
-export default function Page() {
-    return (
-        <div className="w-full px-6 pt-10 pb-10">
-            <h1
-                className={`${poppins.className}
-          text-[40px]
-          leading-tight
-          tracking-tight
-          font-semibold
-          text-center
-          text-white
-        `}
-            >
-                Add Transaction
-            </h1>
+export default async function Page() {
+    const session = await auth();
+    if (!session?.user?.id) {
+        redirect('/login?callbackUrl=/add');
+    }
 
-            <div className="mt-10 rounded-3xl bg-white/5 border border-white/10 p-6 text-white/80">
-                Add form placeholder
-            </div>
-        </div>
-    );
+    const userId = Number(session.user.id);
+    if (!Number.isInteger(userId)) {
+        redirect('/login?callbackUrl=/add');
+    }
+
+    const [accounts, allTags] = await Promise.all([
+        getUserTransactionAccounts(userId),
+        getAllTags(),
+    ]);
+
+    return <AddPageClient accounts={accounts} allTags={allTags} />;
 }
Index: app/(app)/history/transaction-list.tsx
===================================================================
--- app/(app)/history/transaction-list.tsx	(revision f5f1d898d8749982f099b71786a2b8aebb909be1)
+++ app/(app)/history/transaction-list.tsx	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
@@ -24,5 +24,5 @@
                 // array literal string like "{food,transport}". Normalise to
                 // a plain JS string[] so the pills always render.
-                const tags: string[] = Array.isArray(tx.tags)
+                const rawTags: string[] = Array.isArray(tx.tags)
                     ? tx.tags.filter(Boolean)
                     : typeof tx.tags === 'string' && (tx.tags as string).length > 2
@@ -33,4 +33,10 @@
                             .filter(Boolean)
                       : [];
+
+                // Separate note tags (__note:…) from regular tags
+                const tags = rawTags.filter((t) => !t.startsWith('__note:'));
+                const notes = rawTags
+                    .filter((t) => t.startsWith('__note:'))
+                    .map((t) => t.slice('__note:'.length));
 
                 return (
@@ -55,4 +61,9 @@
                                 </div>
                             )}
+                            {notes.length > 0 && (
+                                <div className="mt-1 text-xs text-white/40 italic truncate">
+                                    {notes[0]}
+                                </div>
+                            )}
                             <div className="mt-1 text-xs text-white/40">
                                 {formatDateToLocal(tx.date)}
Index: app/lib/queries.ts
===================================================================
--- app/lib/queries.ts	(revision f5f1d898d8749982f099b71786a2b8aebb909be1)
+++ app/lib/queries.ts	(revision 8563782f7b44a1553af5f70be2d12ab25a84d6bd)
@@ -186,7 +186,18 @@
         JOIN transaction_account ta ON ta.transaction_account_id = tb.transaction_account_id
         WHERE ta.user_id = ${userId}
+          AND tg.tag_name NOT LIKE '__note:%'
         ORDER BY tg.tag_name ASC
     `;
     return rows.map((r) => r.tag_name);
+}
+
+export async function getAllTags() {
+    const rows = await sql<{ tag_id: number; tag_name: string }[]>`
+        SELECT tag_id, tag_name
+        FROM tag
+        WHERE tag_name NOT LIKE '__note:%'
+        ORDER BY tag_name ASC
+    `;
+    return rows;
 }
 
