1→import { AlertCircle, RefreshCw, Home, Mail } from 'lucide-react';
     2→import { Button } from '@/components/ui/button';
     3→import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
     4→
     5→interface ErrorDisplayProps {
     6→  title?: string;
     7→  message?: string;
     8→  error?: Error;
     9→  onRetry?: () => void;
    10→  onGoHome?: () => void;
    11→  onContactSupport?: () => void;
    12→  showContactSupport?: boolean;
    13→  variant?: 'default' | 'authentication' | 'network' | 'not-found';
    14→}
    15→
    16→export function ErrorDisplay({
    17→  title = 'Something went wrong',
    18→  message = 'We encountered an unexpected error. Please try again.',
    19→  error,
    20→  onRetry,
    21→  onGoHome,
    22→  onContactSupport,
    23→  showContactSupport = true,
    24→  variant = 'default',
    25→}: ErrorDisplayProps) {
    26→  const getVariantConfig = () => {
    27→    switch (variant) {
    28→      case 'authentication':
    29→        return {
    30→          title: 'Session Expired',
    31→          message: 'Your session has expired. Please log in again to continue.',
    32→          primaryAction: {
    33→            label: 'Log in again',
    34→            icon: <Home className="mr-2 h-4 w-4" />,
    35→            onClick: () => window.location.href = '/auth/login',
    36→          },
    37→        };
    38→      case 'network':
    39→        return {
    40→          title: 'Connection Error',
    41→          message: 'Unable to connect to the server. Please check your internet connection.',
    42→          primaryAction: onRetry ? {
    43→            label: 'Retry',
    44→            icon: <RefreshCw className="mr-2 h-4 w-4" />,
    45→            onClick: onRetry,
    46→          } : undefined,
    47→        };
    48→      case 'not-found':
    49→        return {
    50→          title: 'Page Not Found',
    51→          message: 'The page you are looking for does not exist.',
    52→          primaryAction: {
    53→            label: 'Go to homepage',
    54→            icon: <Home className="mr-2 h-4 w-4" />,
    55→            onClick: () => window.location.href = '/',
    56→          },
    57→        };
    58→      default:
    59→        return {
    60→          title,
    61→          message,
    62→          primaryAction: onRetry ? {
    63→            label: 'Try again',
    64→            icon: <RefreshCw className="mr-2 h-4 w-4" />,
    65→            onClick: onRetry,
    66→          } : undefined,
    67→        };
    68→    }
    69→  };
    70→
    71→  const config = getVariantConfig();
    72→
    73→  return (
    74→

    75→      <Card className="border-destructive/20">
    76→        <CardHeader className="text-center">
    77→

    78→            <AlertCircle className="h-6 w-6 text-destructive" />
    79→

    80→          <CardTitle className="text-xl">{config.title}</CardTitle>
    81→          <CardDescription className="mt-2">{config.message}</CardDescription>
    82→        </CardHeader>
    83→
    84→        {error && process.env.NODE_ENV === 'development' && (
    85→          <CardContent>
    86→

    87→

    88→                {error.message || 'Unknown error occurred'}
    89→

    90→              {error.name && (
    91→

    92→                  Error: {error.name}
    93→

    94→              )}
    95→

    96→          </CardContent>
    97→        )}
    98→
    99→        <CardFooter className="flex flex-col gap-3">
   100→          {config.primaryAction && (
   101→            <Button
   102→              onClick={config.primaryAction.onClick}
   103→              className="w-full"
   104→              variant="default"
   105→            >
   106→              {config.primaryAction.icon}
   107→              {config.primaryAction.label}
   108→            </Button>
   109→          )}
   110→
   111→          {variant !== 'not-found' && (
   112→            <Button
   113→              onClick={onGoHome || (() => window.location.href = '/')}
   114→              className="w-full"
   115→              variant="outline"
   116→            >
   117→              <Home className="mr-2 h-4 w-4" />
   118→              Go to homepage
   119→            </Button>
   120→          )}
   121→
   122→          {showContactSupport && (
   123→            <Button
   124→              onClick={onContactSupport || (() => window.location.href = 'mailto:support@rateright.com.au')}
   125→              className="w-full"
   126→              variant="ghost"
   127→            >
   128→              <Mail className="mr-2 h-4 w-4" />
   129→              Contact support
   130→            </Button>
   131→          )}
   132→        </CardFooter>
   133→      </Card>
   134→
   135→
   136→

   137→          RateRight — Hire Construction Workers Fast
   138→

   139→
   140→

   141→  );
   142→}

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


1→import { Loader2, RefreshCw } from "lucide-react";
     2→import { Button } from "@/components/ui/button";
     3→import { Card, CardContent } from "@/components/ui/card";
     4→
     5→interface LoadingStateProps {
     6→  message?: string;
     7→  showRetry?: boolean;
     8→  onRetry?: () => void;
     9→  retryDelay?: number;
    10→  className?: string;
    11→}
    12→
    13→export function LoadingState({
    14→  message = "Loading...",
    15→  showRetry = false,
    16→  onRetry,
    17→  retryDelay = 5000,
    18→  className = "",
    19→}: LoadingStateProps) {
    20→  return (
    21→    <div className={`flex items-center justify-center p-8 ${className}`}>
    22→      <Card className="w-full max-w-md">
    23→        <CardContent className="flex flex-col items-center gap-4 p-6">
    24→          <Loader2 className="h-8 w-8 animate-spin text-primary" />
    25→
{message}

    26→
    27→          {showRetry && onRetry && (
    28→            <Button
    29→              onClick={onRetry}
    30→              variant="outline"
    31→              size="sm"
    32→              className="mt-2 gap-2"
    33→            >
    34→              <RefreshCw className="h-4 w-4" />
    35→              Retry
    36→            </Button>
    37→          )}
    38→        </CardContent>
    39→      </Card>
    40→

    41→  );
    42→}
    43→
    44→interface LoadingSpinnerProps {
    45→  size?: "sm" | "md" | "lg";
    46→  className?: string;
    47→}
    48→
    49→export function LoadingSpinner({ size = "md", className = "" }: LoadingSpinnerProps) {
    50→  const sizeClasses = {
    51→    sm: "h-4 w-4",
    52→    md: "h-6 w-6",
    53→    lg: "h-8 w-8",
    54→  };
    55→
    56→  return (
    57→    <div className={`flex items-center justify-center ${className}`}>
    58→      <Loader2 className={`animate-spin text-primary ${sizeClasses[size]}`} />
    59→

    60→  );
    61→}

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→'use client';
     2→
     3→import { AlertCircle, RefreshCw } from 'lucide-react';
     4→import { Button } from '@/components/ui/button';
     5→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
     6→
     7→interface LoadingErrorProps {
     8→  title?: string;
     9→  message?: string;
    10→  onRetry?: () => void;
    11→}
    12→
    13→export function LoadingError({
    14→  title = 'Failed to load',
    15→  message = 'Unable to load the requested data. Please try again.',
    16→  onRetry
    17→}: LoadingErrorProps) {
    18→  return (
    19→    <Card className="w-full border-destructive/20">
    20→      <CardHeader>
    21→        <div className="flex items-center gap-2">
    22→          <AlertCircle className="h-5 w-5 text-destructive" />
    23→          <CardTitle className="text-lg">{title}</CardTitle>
    24→        </div>
    25→        <CardDescription>{message}</CardDescription>
    26→      </CardHeader>
    27→      {onRetry && (
    28→        <CardContent>
    29→          <Button onClick={onRetry} className="w-full gap-2">
    30→            <RefreshCw className="h-4 w-4" />
    31→            Try again
    32→          </Button>
    33→        </CardContent>
    34→      )}
    35→    </Card>
    36→  );
    37→}

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


Now let me check if the auth/signup page exists, and also look for which components are actually imported across the codebase.


src/app/global-error.tsx:4:import { ErrorDisplay } from '@/components/error-display';
src/app/error.tsx:4:import { ErrorDisplay } from '@/components/error-display';
src/app/(authenticated)/error.tsx:4:import { ErrorDisplay } from '@/components/error-display';


No matches found


src/app/(authenticated)/dashboard/page.tsx:9:import { BottomNavWrapper } from "@/components/bottom-nav-wrapper";


src/components/bottom-nav-wrapper.tsx:3:import { BottomNav } from "@/components/bottom-nav";
src/app/(authenticated)/contractor/matches/page.tsx:9:import { BottomNav } from "@/components/bottom-nav";
src/app/(authenticated)/worker/jobs/page.tsx:9:import { BottomNav } from "@/components/bottom-nav";


1→"use client";
     2→import { useState } from "react";
     3→import { useRouter } from "next/navigation";
     4→import Link from "next/link";
     5→import { createClient } from "@/lib/supabase/client";
     6→import { Button } from "@/components/ui/button";
     7→import { Input } from "@/components/ui/input";
     8→import { Label } from "@/components/ui/label";
     9→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
    10→import { HardHat, Building2 } from "lucide-react";
    11→import type { UserType } from "@/lib/types";
    12→
    13→export default function SignupPage() {
    14→  const [email, setEmail] = useState("");
    15→  const [password, setPassword] = useState("");
    16→  const [userType, setUserType] = useState<UserType | null>(null);
    17→  const [error, setError] = useState<string | null>(null);
    18→  const [loading, setLoading] = useState(false);
    19→  const router = useRouter();
    20→  const supabase = createClient();
    21→
    22→  async function handleSignup(e: React.FormEvent) {
    23→    e.preventDefault();
    24→    if (!userType) {
    25→      setError("Please select whether you're hiring or looking for work");
    26→      return;
    27→    }
    28→
    29→    setLoading(true);
    30→    setError(null);
    31→
    32→    const { error } = await supabase.auth.signUp({
    33→      email,
    34→      password,
    35→      options: {
    36→        data: { type: userType },
    37→        emailRedirectTo: `${window.location.origin}/auth/callback`,
    38→      },
    39→    });
    40→
    41→    if (error) {
    42→      setError(error.message);
    43→      setLoading(false);
    44→      return;
    45→    }
    46→
    47→    // Redirect to email verification page instead of dashboard
    48→    router.push("/auth/verify-email");
    49→    router.refresh();
    50→  }
    51→
    52→  return (
    53→

    54→      <Card className="w-full max-w-md">
    55→        <CardHeader>
    56→          <CardTitle className="text-2xl font-bold text-center">Create your account</CardTitle>
    57→          <CardDescription className="text-center">
    58→            Sign up to find work or hire workers
    59→          </CardDescription>
    60→        </CardHeader>
    61→        <CardContent>
    62→          <form onSubmit={handleSignup} className="space-y-6">
    63→

    64→

    65→                Email address
    66→                <Input
    67→                  id="email"
    68→                  type="email"
    69→                  value={email}
    70→                  onChange={(e) => setEmail(e.target.value)}
    71→                  required
    72→                  placeholder="you@example.com"
    73→                />
    74→

    75→

    76→                Password
    77→                <Input
    78→                  id="password"
    79→                  type="password"
    80→                  value={password}
    81→                  onChange={(e) => setPassword(e.target.value)}
    82→                  required
    83→                  placeholder="••••••••"
    84→                  minLength={6}
    85→                />
    86→

    87→

    88→
    89→

    90→              I am a...
    91→

    92→                <Button
    93→                  type="button"
    94→                  variant={userType === "contractor" ? "default" : "outline"}
    95→                  onClick={() => setUserType("contractor")}
    96→                  className="h-24 flex-col gap-2"
    97→                >
    98→                  <Building2 className="h-6 w-6" />
    99→                  Contractor
   100→                  Hiring workers
   101→                </Button>
   102→                <Button
   103→                  type="button"
   104→                  variant={userType === "worker" ? "default" : "outline"}
   105→                  onClick={() => setUserType("worker")}
   106→                  className="h-24 flex-col gap-2"
   107→                >
   108→                  <HardHat className="h-6 w-6" />
   109→                  Worker
   110→                  Looking for work
   111→                </Button>
   112→

   113→

   114→
   115→            {error && (
   116→

   117→
{error}

   118→

   119→            )}
   120→
   121→            <Button type="submit" className="w-full" disabled={loading}>
   122→              {loading ? "Creating account..." : "Create account"}
   123→            </Button>
   124→
   125→

   126→              Already have an account?{" "}
   127→              <Link href="/auth/login" className="font-medium text-blue-600 hover:text-blue-500">
   128→                Sign in
   129→              </Link>
   130→

   131→          </form>
   132→        </CardContent>
   133→      </Card>
   134→

   135→  );
   136→}
   137→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


src/lib/supabase/middleware.ts:73:      url.pathname = "/(authenticated)/dashboard";
src/app/auth/verify-email/page.tsx:27:      router.push('/(authenticated)/dashboard');
src/app/auth/login/page.tsx:43:    router.push("/(authenticated)/dashboard");
src/app/auth/callback/route.ts:8:  const next = searchParams.get("next") ?? "/(authenticated)/dashboard";


TodoWrite

Read all layout and page files

Read all component files

Analyze for duplicate headers/navs

Analyze for missing 'use client' directives

Analyzing for missing 'use client' directives

Analyze for sticky positioning issues

Analyze for duplicate component files

Analyze for loading state issues

Analyze for mobile responsive issues

Analyze for touch target issues

Compile final audit report


Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable


Now let me check a few more things to be thorough.


src/app/(authenticated)/layout.tsx:36:      <nav className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-4 py-2 safe-area-pb">


270:            © {new Date().getFullYear()} RateRight Pty Ltd. All rights


     1→@import "tailwindcss";
     2→@import "tw-animate-css";
     3→
     4→@custom-variant dark (&:is(.dark *));
     5→
     6→@theme inline {
     7→  --color-background: var(--background);
     8→  --color-foreground: var(--foreground);
     9→  --font-sans: var(--font-geist-sans);
    10→  --font-mono: var(--font-geist-mono);
    11→  --color-sidebar-ring: var(--sidebar-ring);
    12→  --color-sidebar-border: var(--sidebar-border);
    13→  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
    14→  --color-sidebar-accent: var(--sidebar-accent);
    15→  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
    16→  --color-sidebar-primary: var(--sidebar-primary);
    17→  --color-sidebar-foreground: var(--sidebar-foreground);
    18→  --color-sidebar: var(--sidebar);
    19→  --color-chart-5: var(--chart-5);
    20→  --color-chart-4: var(--chart-4);
    21→  --color-chart-3: var(--chart-3);
    22→  --color-chart-2: var(--chart-2);
    23→  --color-chart-1: var(--chart-1);
    24→  --color-ring: var(--ring);
    25→  --color-input: var(--input);
    26→  --color-border: var(--border);
    27→  --color-destructive: var(--destructive);
    28→  --color-accent-foreground: var(--accent-foreground);
    29→  --color-accent: var(--accent);
    30→  --color-muted-foreground: var(--muted-foreground);
    31→  --color-muted: var(--muted);
    32→  --color-secondary-foreground: var(--secondary-foreground);
    33→  --color-secondary: var(--secondary);
    34→  --color-primary-foreground: var(--primary-foreground);
    35→  --color-primary: var(--primary);
    36→  --color-popover-foreground: var(--popover-foreground);
    37→  --color-popover: var(--popover);
    38→  --color-card-foreground: var(--card-foreground);
    39→  --color-card: var(--card);
    40→  --radius-sm: calc(var(--radius) - 4px);
    41→  --radius-md: calc(var(--radius) - 2px);
    42→  --radius-lg: var(--radius);
    43→  --radius-xl: calc(var(--radius) + 4px);
    44→  --radius-2xl: calc(var(--radius) + 8px);
    45→  --radius-3xl: calc(var(--radius) + 12px);
    46→  --radius-4xl: calc(var(--radius) + 16px);
    47→}
    48→
    49→:root {
    50→  --radius: 0.625rem;
    51→  --background: oklch(1 0 0);
    52→  --foreground: oklch(0.145 0 0);
    53→  --card: oklch(1 0 0);
    54→  --card-foreground: oklch(0.145 0 0);
    55→  --popover: oklch(1 0 0);
    56→  --popover-foreground: oklch(0.145 0 0);
    57→  --primary: oklch(0.646 0.222 41.116);
    58→  --primary-foreground: oklch(1 0 0);
    59→  --secondary: oklch(0.97 0 0);
    60→  --secondary-foreground: oklch(0.205 0 0);
    61→  --muted: oklch(0.97 0 0);
    62→  --muted-foreground: oklch(0.556 0 0);
    63→  --accent: oklch(0.97 0 0);
    64→  --accent-foreground: oklch(0.205 0 0);
    65→  --destructive: oklch(0.577 0.245 27.325);
    66→  --border: oklch(0.922 0 0);
    67→  --input: oklch(0.922 0 0);
    68→  --ring: oklch(0.708 0 0);
    69→  --chart-1: oklch(0.646 0.222 41.116);
    70→  --chart-2: oklch(0.6 0.118 184.704);
    71→  --chart-3: oklch(0.398 0.07 227.392);
    72→  --chart-4: oklch(0.828 0.189 84.429);
    73→  --chart-5: oklch(0.769 0.188 70.08);
    74→  --sidebar: oklch(0.985 0 0);
    75→  --sidebar-foreground: oklch(0.145 0 0);
    76→  --sidebar-primary: oklch(0.205 0 0);
    77→  --sidebar-primary-foreground: oklch(0.985 0 0);
    78→  --sidebar-accent: oklch(0.97 0 0);
    79→  --sidebar-accent-foreground: oklch(0.205 0 0);
    80→  --sidebar-border: oklch(0.922 0 0);
    81→  --sidebar-ring: oklch(0.708 0 0);
    82→}
    83→
    84→.dark {
    85→  --background: oklch(0.145 0 0);
    86→  --foreground: oklch(0.985 0 0);
    87→  --card: oklch(0.205 0 0);
    88→  --card-foreground: oklch(0.985 0 0);
    89→  --popover: oklch(0.205 0 0);
    90→  --popover-foreground: oklch(0.985 0 0);
    91→  --primary: oklch(0.705 0.191 47.604);
    92→  --primary-foreground: oklch(0.145 0 0);
    93→  --secondary: oklch(0.269 0 0);
    94→  --secondary-foreground: oklch(0.985 0 0);
    95→  --muted: oklch(0.269 0 0);
    96→  --muted-foreground: oklch(0.708 0 0);
    97→  --accent: oklch(0.269 0 0);
    98→  --accent-foreground: oklch(0.985 0 0);
    99→  --destructive: oklch(0.704 0.191 22.216);
   100→  --border: oklch(1 0 0 / 10%);
   101→  --input: oklch(1 0 0 / 15%);
   102→  --ring: oklch(0.556 0 0);
   103→  --chart-1: oklch(0.488 0.243 264.376);
   104→  --chart-2: oklch(0.696 0.17 162.48);
   105→  --chart-3: oklch(0.769 0.188 70.08);
   106→  --chart-4: oklch(0.627 0.265 303.9);
   107→  --chart-5: oklch(0.645 0.246 16.439);
   108→  --sidebar: oklch(0.205 0 0);
   109→  --sidebar-foreground: oklch(0.985 0 0);
   110→  --sidebar-primary: oklch(0.488 0.243 264.376);
   111→  --sidebar-primary-foreground: oklch(0.985 0 0);
   112→  --sidebar-accent: oklch(0.269 0 0);
   113→  --sidebar-accent-foreground: oklch(0.985 0 0);
   114→  --sidebar-border: oklch(1 0 0 / 10%);
   115→  --sidebar-ring: oklch(0.556 0 0);
   116→}
   117→
   118→@layer base {
   119→  * {
   120→    @apply border-border outline-ring/50;
   121→  }
   122→  body {
   123→    @apply bg-background text-foreground;
   124→  }
   125→}
   126→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


Read all layout and page files

Read all component files

Analyze for duplicate headers/navs

Analyze for missing 'use client' directives

Analyzing for missing 'use client' directives

Analyze for sticky positioning issues

Analyze for duplicate component files

Analyze for loading state issues

Analyze for mobile responsive issues

Analyze for touch target issues

Compile final audit report


Read all layout and page files

Read all component files

Analyze for duplicate headers/navs

Analyze for missing 'use client' directives

Analyzing for missing 'use client' directives

Analyze for sticky positioning issues

Analyze for duplicate component files

Analyze for loading state issues

Analyze for mobile responsive issues

Analyze for touch target issues

Compile final audit report


---

# UI/UX Audit Report

## CRITICAL ISSUE 1: Triple-stacked headers and double bottom nav on authenticated pages

**Severity: Critical**

The authenticated layout at `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/layout.tsx` renders **both** a header (lines 21-28) and a bottom nav (lines 36-55). Multiple child pages then render their **own** headers and/or their own `<BottomNav>` / `<BottomNavWrapper>`, creating duplicates.

### 1a. Dashboard page: duplicate bottom nav
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/dashboard/page.tsx`, line 187
- **Problem:** The page renders `<BottomNavWrapper userType={...} />` while the authenticated layout already renders its own `<nav>` bottom bar. Users see **two** bottom navigation bars stacked.
- **Fix:** Either remove the layout's bottom nav or remove the page-level `<BottomNavWrapper>`. Since other pages also render their own `<BottomNav>`, the cleanest fix is to remove the nav from the authenticated layout entirely and let each page handle it. Alternatively, centralize the nav in the layout and remove all page-level instances.

### 1b. Contractor matches page: duplicate bottom nav
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/contractor/matches/page.tsx`, line 564
- **Problem:** Renders `<BottomNav userType="contractor" />` in addition to the layout's nav. Two bottom bars appear.
- **Fix:** Same as 1a.

### 1c. Worker jobs page: duplicate bottom nav
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/jobs/page.tsx`, line 447
- **Problem:** Renders `<BottomNav userType="worker" />` in addition to the layout's nav.
- **Fix:** Same as 1a.

### 1d. Worker signup page: duplicate header
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/signup/page.tsx`, lines 278-287
- **Problem:** Renders its own `<header>` with the RateRight logo and sticky positioning. The authenticated layout already provides a header. Users see **two** headers stacked at the top.
- **Fix:** Remove the page-level header since the layout already provides one. If this page is meant to be accessible outside the authenticated layout, move it out of the `(authenticated)` route group.

### 1e. Conversation page: duplicate header
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/[conversationId]/page.tsx`, lines 200-236
- **Problem:** Renders its own `<header>` with the back button, avatar, and contact info. The layout already provides a header above it, so the conversation header appears below the layout header.
- **Fix:** Remove the layout header for this route, or rework the conversation page to not use a full `<header>` element and instead place its content inside `<main>`.

### 1f. Messages page: sticky search bar conflict
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/page.tsx`, lines 101-112
- **Problem:** Has a `sticky top-0` search bar, but it will be hidden behind the layout's header which also occupies `top-0`. The search bar will never visually stick at the correct position.
- **Fix:** Adjust `top` offset to account for the layout header height (e.g., `top-[53px]` if header is ~53px), or remove the layout header.

### 1g. Layout nav does not account for user type
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/layout.tsx`, lines 38-53
- **Problem:** The layout nav always shows the same links regardless of user type (contractor vs worker). The "Jobs" link always goes to `/contractor/post-job` and "Profile" always goes to `/worker/profile/build`. A contractor sees a worker profile link, and a worker sees a contractor job-posting link.
- **Fix:** Either query the user's type in the layout and conditionally render links, or remove the layout nav and rely on the page-level `<BottomNav>` which already handles user type.

---

## CRITICAL ISSUE 2: Broken router.push paths with route group prefix

### 2a. Login page uses route group in path
- **File:** `/home/ccuser/the-50-dollar-app/src/app/auth/login/page.tsx`, line 43
- **Problem:** `router.push("/(authenticated)/dashboard")` -- The `(authenticated)` segment is a route group and should NOT appear in the URL. The correct path is `/dashboard`. This push will navigate to a non-existent URL and likely show a 404.
- **Fix:** Change to `router.push("/dashboard")`.

### 2b. Verify-email page uses route group in path
- **File:** `/home/ccuser/the-50-dollar-app/src/app/auth/verify-email/page.tsx`, line 27
- **Problem:** `router.push('/(authenticated)/dashboard')` -- Same bug. Should be `/dashboard`.
- **Fix:** Change to `router.push('/dashboard')`.

---

## ISSUE 3: Duplicate component files

### 3a. ErrorDisplay.tsx vs error-display.tsx
- **Files:**
  - `/home/ccuser/the-50-dollar-app/src/components/ErrorDisplay.tsx` (PascalCase)
  - `/home/ccuser/the-50-dollar-app/src/components/error-display.tsx` (kebab-case)
- **Problem:** Two different `ErrorDisplay` components exist with different interfaces. The actual error boundary pages (`error.tsx`, `global-error.tsx`, `(authenticated)/error.tsx`) all import from `@/components/error-display` (kebab-case). The PascalCase `ErrorDisplay.tsx` is never imported by anything, making it dead code.
- **Fix:** Delete `/home/ccuser/the-50-dollar-app/src/components/ErrorDisplay.tsx` to avoid confusion.

### 3b. LoadingState.tsx is never used
- **File:** `/home/ccuser/the-50-dollar-app/src/components/LoadingState.tsx`
- **Problem:** No file in the codebase imports `LoadingState` or `LoadingSpinner` from this file. It is dead code.
- **Fix:** Delete or integrate it into pages that use inline loading spinners.

### 3c. loading-error.tsx is never used
- **File:** `/home/ccuser/the-50-dollar-app/src/components/loading-error.tsx`
- **Problem:** No file in the codebase imports `LoadingError` from this file. It is dead code.
- **Fix:** Delete or integrate it into pages that need loading error states.

---

## ISSUE 4: Missing "use client" directive

### 4a. ConversationPreview component
- **File:** `/home/ccuser/the-50-dollar-app/src/components/ConversationPreview.tsx`
- **Problem:** This component does not have `"use client"` at the top. While it does not directly use hooks, it uses the `onClick` prop and is rendered by a client component (messages page), so it works at runtime. However, it imports from `date-fns` and performs interactive behavior (click handling). It would be safer to add `"use client"` since it is inherently interactive.
- **Fix:** Add `"use client";` at line 1, or verify it always only renders from client component parents.

### 4b. MessageBubble component
- **File:** `/home/ccuser/the-50-dollar-app/src/components/MessageBubble.tsx`
- **Problem:** Same situation -- no `"use client"` directive. It is only used in client components so it works, but the absence could cause confusion if ever used in a server context.
- **Fix:** Add `"use client";` at line 1 for clarity and safety.

---

## ISSUE 5: Sticky positioning conflicts

### 5a. Post-job page sticky CTA overlaps layout bottom nav
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/contractor/post-job/page.tsx`, lines 473-499
- **Problem:** The sticky bottom CTA is `fixed bottom-0` with `z-50`. The layout's bottom nav is also `fixed bottom-0`. Both compete for the same screen space. The page CTA renders on top (same z-index), but the layout nav peeks out from behind or is inaccessible beneath.
- **Fix:** Hide the layout's bottom nav on this page, or add `pb-[env(safe-area-inset-bottom)]` to the CTA and position it above the nav.

### 5b. Profile build page sticky CTA overlaps layout bottom nav
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/profile/build/page.tsx`, lines 707-729
- **Problem:** Same issue. The "Save My Profile" sticky bottom CTA at `fixed bottom-0` competes with the layout's fixed bottom nav.
- **Fix:** Same as 5a.

### 5c. Worker jobs page detail sheet CTA overlaps bottom nav
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/jobs/page.tsx`, lines 412-442
- **Problem:** The job detail bottom sheet has a `fixed bottom-0` CTA at `z-50`. When the sheet is open, this overlaps the layout bottom nav. The sheet overlay should cover the nav, but edge cases (safe area, z-index conflicts) may cause visual glitches.
- **Fix:** The sheet overlay uses `z-[60]` (line 328), which should override the layout nav at default z-index, but the inner CTA at `z-50` (line 412) is actually lower than the overlay's `z-[60]`, creating a potential stacking issue. Raise the inner CTA to `z-[61]` or higher.

### 5d. Conversation page message input overlaps bottom nav
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/[conversationId]/page.tsx`, lines 263-284
- **Problem:** The message input `<footer>` is `sticky bottom-0`. But the layout already has a fixed bottom nav. The input bar will overlap with the layout nav, and the `pb-20` on the page (line 199) does not account for the layout's bottom nav.
- **Fix:** Add the conversation page to a route that does not include the layout's bottom nav, or offset the footer bottom position.

### 5e. Landing page header `sticky top-0` conflict with layout
- **File:** `/home/ccuser/the-50-dollar-app/src/app/page.tsx`, line 29
- **Problem:** Minor -- this page is NOT inside the authenticated layout, so no conflict. This is fine.

---

## ISSUE 6: Layout uses hardcoded colors instead of theme tokens

### 6a. Authenticated layout uses `bg-white`, `border-gray-200`, `text-blue-600`, `text-gray-500`
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/layout.tsx`, lines 21, 36, 23, 26, 38
- **Problem:** The layout uses hardcoded Tailwind colors (`bg-white`, `text-blue-600`, `border-gray-200`, `text-gray-600`) instead of the design system tokens (`bg-background`, `text-primary`, `border-border`, `text-muted-foreground`). This will break in dark mode and is visually inconsistent with the rest of the app.
- **Fix:** Replace `bg-white` with `bg-background`, `border-gray-200` with `border-border`, `text-blue-600` with `text-primary`, `text-gray-500` with `text-muted-foreground`, `text-gray-600` with `text-muted-foreground`.

### 6b. Verify-email page uses hardcoded colors
- **File:** `/home/ccuser/the-50-dollar-app/src/app/auth/verify-email/page.tsx`
- **Problem:** Uses `bg-gray-50` (line 62), `text-gray-900` (line 68), `text-gray-600` (line 71), `bg-blue-50` (line 76), `bg-red-50` (line 89), `bg-green-50` (line 102) etc. instead of theme tokens. Dark mode will look broken.
- **Fix:** Use `bg-background`, `text-foreground`, `bg-primary/5`, `text-primary`, `bg-destructive/10`, `text-destructive`, etc.

### 6c. Signup page uses hardcoded colors
- **File:** `/home/ccuser/the-50-dollar-app/src/app/auth/signup/page.tsx`
- **Problem:** Uses `bg-gray-50` (line 53), `bg-red-50` (line 116), `text-red-700` (line 117), `text-blue-600` (line 127). Inconsistent with theme.
- **Fix:** Use theme tokens.

---

## ISSUE 7: Undefined CSS class `safe-area-pb`

- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/layout.tsx`, line 36
- **Problem:** The class `safe-area-pb` is used on the nav element, but it is not defined in `globals.css`, Tailwind config, or any other CSS file. It does nothing.
- **Fix:** Replace with `pb-[env(safe-area-inset-bottom)]` which is what the other `<BottomNav>` component correctly uses.

---

## ISSUE 8: `force-static` on landing page with dynamic `new Date().getFullYear()`

- **File:** `/home/ccuser/the-50-dollar-app/src/app/page.tsx`, lines 20, 270
- **Problem:** The page declares `export const dynamic = 'force-static'` but uses `new Date().getFullYear()` in the footer. Since the page is statically generated at build time, the year will be frozen to the build year and never update. This is a minor issue since years change infrequently, but it is technically incorrect.
- **Fix:** Either remove `force-static` or hardcode the year, or accept this known behavior.

---

## ISSUE 9: Double `pb-20` padding on authenticated pages

### 9a. Dashboard page
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/dashboard/page.tsx`, line 59
- **Problem:** The page adds `pb-20` to its root `<div>`. The authenticated layout's `<main>` also has `pb-20` (layout line 31). This creates 160px (2x `pb-20`) of bottom padding, with the layout's bottom nav on top of that. Content is pushed excessively upward.
- **Fix:** Remove `pb-20` from either the page or the layout.

### 9b. Contractor matches page
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/contractor/matches/page.tsx`, line 458
- **Problem:** Same double `pb-20`. Page has `pb-20` and layout has `pb-20`.
- **Fix:** Same.

### 9c. Worker jobs page
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/jobs/page.tsx`, line 272
- **Problem:** Same.
- **Fix:** Same.

### 9d. Messages page
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/page.tsx`, line 99
- **Problem:** Same.
- **Fix:** Same.

### 9e. Conversation page
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/[conversationId]/page.tsx`, line 199
- **Problem:** Same.
- **Fix:** Same.

---

## ISSUE 10: Touch target sizes

### 10a. Worker signup trade selection buttons
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/signup/page.tsx`, lines 443-453
- **Problem:** Trade selection buttons have `py-3` (12px top/bottom) with no explicit min-height. On mobile, the resulting touch target can be as small as ~38px, below the recommended 44px minimum.
- **Fix:** Add `min-h-[48px]` to ensure adequate touch targets.

### 10b. Worker signup certification buttons
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/signup/page.tsx`, lines 619-642
- **Problem:** Certification toggle buttons have `py-3.5` but no min-height. They are close to 44px but may fall short with smaller text.
- **Fix:** Add `min-h-[48px]`.

### 10c. Worker signup back buttons (text-only)
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/signup/page.tsx`, lines 505-511, 599-605, 761-767
- **Problem:** The "Back" links are plain text links (`text-sm text-muted-foreground`) with no padding, making them very small touch targets (~20px height).
- **Fix:** Wrap in a button or add `py-3 px-4` padding to make touch targets at least 44px.

### 10d. Profile build page "Change" links
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/profile/build/page.tsx`, lines 607, 621, 635
- **Problem:** "Change" links are `text-xs text-primary` with no padding. Very small touch targets on mobile.
- **Fix:** Increase size to at least `text-sm` and add `py-2 px-3` padding.

### 10e. Post-job page certification remove buttons
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/contractor/post-job/page.tsx`, lines 424-429
- **Problem:** The `x` remove button inside badges is tiny (`ml-2`) with no explicit size. Very hard to tap on mobile.
- **Fix:** Make the remove button at least 24x24px with adequate padding, or use a proper icon button.

### 10f. Payment form cancel/pay buttons
- **File:** `/home/ccuser/the-50-dollar-app/src/components/payment/PaymentForm.tsx`, lines 94-117
- **Problem:** Buttons lack explicit height. Default button height may be adequate (~40px), but for a payment action on mobile, `h-12` or `h-14` would be better.
- **Fix:** Add `h-12` to both buttons.

### 10g. Signup page role selection buttons
- **File:** `/home/ccuser/the-50-dollar-app/src/app/auth/signup/page.tsx`, lines 92-111
- **Problem:** Role selector buttons are `h-24` which is fine for height, but the submit button (line 121) has no explicit height. Default button height (~40px) is below the mobile-friendly 44px.
- **Fix:** Add `h-14` to the submit button.

### 10h. Verify-email page buttons
- **File:** `/home/ccuser/the-50-dollar-app/src/app/auth/verify-email/page.tsx`, lines 116-131
- **Problem:** Both the "Resend" and "Back to login" buttons have no explicit height, defaulting to ~40px. Below the recommended 44px.
- **Fix:** Add `h-12` or `h-14` to both buttons.

---

## ISSUE 11: Duplicate meta tags in root layout

- **File:** `/home/ccuser/the-50-dollar-app/src/app/layout.tsx`, lines 28-41, 57-68
- **Problem:** The metadata export (lines 28-41) already sets `manifest`, `appleWebApp`, and various `other` meta tags. The `<head>` block (lines 57-68) then duplicates all of these as manual `<meta>` and `<link>` tags. This results in every meta tag being rendered twice in the HTML.
- **Fix:** Remove the duplicate manual tags from the `<head>` block (lines 57-68). Next.js will render them from the metadata export.

---

## ISSUE 12: Missing loading indicators

### 12a. Messages page fetch has no visual loading feedback beyond text
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/page.tsx`, line 92
- **Problem:** Loading state shows `<div className="animate-pulse text-muted-foreground">Loading conversations...</div>` -- plain text with no spinner. Inconsistent with other pages.
- **Fix:** Use a `<Loader2>` spinner consistent with the rest of the app.

### 12b. Conversation page fetch has no spinner
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/[conversationId]/page.tsx`, line 192
- **Problem:** Loading state shows `<div className="animate-pulse text-muted-foreground">Loading messages...</div>` -- same issue.
- **Fix:** Use a `<Loader2>` spinner.

---

## ISSUE 13: `useEffect` missing dependency warnings

### 13a. Messages page
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/page.tsx`, line 50
- **Problem:** `useEffect` dependency array is `[]` but uses `fetchConversations` and `supabase`, which are defined outside the effect. React will show an exhaustive-deps warning.
- **Fix:** Include `fetchConversations` in the dependency array (wrapped in `useCallback`), or move the function inside the effect.

### 13b. Conversation page
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/[conversationId]/page.tsx`, line 85
- **Problem:** Same issue. `useEffect` depends on `conversationId` but also references `supabase` and `fetchConversation` without listing them.
- **Fix:** Same as above.

---

## ISSUE 14: Supabase client recreated on every render

### 14a. Contractor matches page
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/contractor/matches/page.tsx`, line 287
- **Problem:** `const supabase = createClient()` is called at the component body level, creating a new client on every render. The client is then used in `useEffect` dependency arrays (line 340, 395), which could cause infinite re-render loops since the `supabase` reference changes every render.
- **Fix:** Wrap `createClient()` in `useMemo` or move it outside the component, or remove `supabase` from dependency arrays.

### 14b. Worker jobs page
- **File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/jobs/page.tsx`, line 184
- **Problem:** Same issue.
- **Fix:** Same.

---

## ISSUE 15: StripeProvider caches the first publishable key forever

- **File:** `/home/ccuser/the-50-dollar-app/src/components/payment/StripeProvider.tsx`, lines 8-15
- **Problem:** `stripePromise` is a module-level singleton. If `getStripe()` is called with a publishable key, the `stripePromise` is cached. If later called with a different key (e.g., switching between test/live), the old key is used. The `if (!stripePromise && publishableKey)` check means once set, it never updates.
- **Fix:** Key the cache on `publishableKey` value, or accept this as a known limitation.

---

## Summary of Issues by Severity

| Severity | Count | Issues |
|----------|-------|--------|
| Critical | 2 | Double headers/navs (#1), Broken router.push paths (#2) |
| High | 4 | Sticky position conflicts (#5), Double pb-20 (#9), Hardcoded colors (#6), Undefined CSS class (#7) |
| Medium | 5 | Duplicate components (#3), Touch targets (#10), Duplicate meta tags (#11), Missing loading spinners (#12), useEffect deps (#13) |
| Low | 3 | Missing "use client" (#4), Static year (#8), Stripe caching (#15), Supabase re-creation (#14) |

The most impactful fix would be **reworking the authenticated layout** (`/home/ccuser/the-50-dollar-app/src/app/(authenticated)/layout.tsx`): either strip its header and nav entirely (letting pages provide their own), or keep it as the single source of truth and remove all page-level headers and navs. The current hybrid approach causes double headers, double navs, double padding, sticky conflicts, and hardcoded color inconsistencies on every authenticated page.