Read the FULL contents of ALL of the following files. I need every line of every file. Be very thorough - do not skip any files.

1. /tmp/the-50-dollar-app/src/lib/supabase/middleware.ts
2. /tmp/the-50-dollar-app/src/app/auth/callback/route.ts
3. /tmp/the-50-dollar-app/src/app/auth/login/page.tsx
4. /tmp/the-50-dollar-app/src/app/auth/verify-email/page.tsx
5. /tmp/the-50-dollar-app/src/lib/csrf.ts
6. /tmp/the-50-dollar-app/src/lib/csrf-client.ts
7. /tmp/the-50-dollar-app/src/app/api/auth/csrf/route.ts
8. /tmp/the-50-dollar-app/src/app/api/payments/create/route.ts
9. /tmp/the-50-dollar-app/src/app/api/webhooks/stripe/route.ts
10. /tmp/the-50-dollar-app/src/app/api/ai/generate-profile/route.ts
11. /tmp/the-50-dollar-app/src/app/api/ai/parse-profile-voice/route.ts
12. /tmp/the-50-dollar-app/src/app/api/ai/transcribe-voice/route.ts
13. /tmp/the-50-dollar-app/src/app/api/auth/verify-email/route.ts
14. /tmp/the-50-dollar-app/src/app/api/messages/route.ts
15. /tmp/the-50-dollar-app/src/app/api/messages/start/route.ts
16. /tmp/the-50-dollar-app/src/app/api/messages/[id]/send/route.ts
17. /tmp/the-50-dollar-app/src/app/layout.tsx
18. /tmp/the-50-dollar-app/src/app/(authenticated)/layout.tsx
19. /tmp/the-50-dollar-app/src/app/(authenticated)/dashboard/page.tsx
20. /tmp/the-50-dollar-app/src/app/(authenticated)/contractor/matches/page.tsx
21. /tmp/the-50-dollar-app/src/app/(authenticated)/contractor/post-job/page.tsx
22. /tmp/the-50-dollar-app/src/app/(authenticated)/worker/signup/page.tsx
23. /tmp/the-50-dollar-app/src/app/(authenticated)/worker/profile/build/page.tsx
24. /tmp/the-50-dollar-app/src/app/(authenticated)/worker/jobs/page.tsx
25. /tmp/the-50-dollar-app/src/app/(authenticated)/messages/page.tsx
26. /tmp/the-50-dollar-app/src/app/(authenticated)/messages/[conversationId]/page.tsx
27. /tmp/the-50-dollar-app/src/app/page.tsx
28. /tmp/the-50-dollar-app/src/lib/supabase/client.ts
29. /tmp/the-50-dollar-app/src/lib/types.ts
30. /tmp/the-50-dollar-app/src/middleware.ts
31. /tmp/the-50-dollar-app/src/lib/stripe/server.ts
32. /tmp/the-50-dollar-app/src/lib/messaging.ts
33. /tmp/the-50-dollar-app/src/lib/sanitize.ts
34. /tmp/the-50-dollar-app/src/lib/error-handling.tsx
35. /tmp/the-50-dollar-app/src/lib/api-errors.ts
36. /tmp/the-50-dollar-app/src/lib/location-utils.ts
37. /tmp/the-50-dollar-app/src/components/payment/StripeProvider.tsx
38. /tmp/the-50-dollar-app/src/components/payment/PaymentForm.tsx
39. /tmp/the-50-dollar-app/src/components/payment/HirePaymentModal.tsx

Return the COMPLETE contents of each file with file path headers.


I'll read all these files for you. Let me start by reading them in parallel batches.


     1→import { createServerClient } from "@supabase/ssr";
     2→import { NextResponse, type NextRequest } from "next/server";
     3→
     4→export async function updateSession(request: NextRequest) {
     5→  let supabaseResponse = NextResponse.next({
     6→    request,
     7→  });
     8→
     9→  const supabase = createServerClient(
    10→    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    11→    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    12→    {
    13→      cookies: {
    14→        getAll() {
    15→          return request.cookies.getAll();
    16→        },
    17→        setAll(cookiesToSet) {
    18→          cookiesToSet.forEach(({ name, value }) =>
    19→            request.cookies.set(name, value)
    20→          );
    21→          supabaseResponse = NextResponse.next({
    22→            request,
    23→          });
    24→          cookiesToSet.forEach(({ name, value, options }) =>
    25→            supabaseResponse.cookies.set(name, value, options)
    26→          );
    27→        },
    28→      },
    29→    }
    30→  );
    31→
    32→  // Use getUser() — the only safe server-side auth check (validates JWT with Supabase).
    33→  // Do NOT use getSession() here — it reads unverified cookie data.
    34→  const {
    35→    data: { user },
    36→  } = await supabase.auth.getUser();
    37→
    38→  const pathname = request.nextUrl.pathname;
    39→
    40→  // Route classification
    41→  const protectedPaths = ["/dashboard", "/contractor", "/worker", "/messages", "/profile"];
    42→  const isProtected = protectedPaths.some((p) => pathname.startsWith(p));
    43→  const isSignup = pathname.includes("/signup");
    44→  const isVerifyPage = pathname === "/auth/verify-email";
    45→  const isAuthPage = pathname.startsWith("/auth/login") || pathname.startsWith("/auth/signup");
    46→
    47→  // ── Not logged in ──
    48→  if (!user) {
    49→    // Redirect unauthenticated users away from protected routes (but allow signup pages)
    50→    if (isProtected && !isSignup) {
    51→      const url = request.nextUrl.clone();
    52→      url.pathname = "/auth/login";
    53→      return NextResponse.redirect(url);
    54→    }
    55→    return supabaseResponse;
    56→  }
    57→
    58→  // ── Logged in ──
    59→  const emailVerified = !!user.email_confirmed_at;
    60→
    61→  if (!emailVerified) {
    62→    // Unverified user trying to access protected routes → send to verify page
    63→    // (but let them stay on signup flows to finish onboarding after verification)
    64→    if (isProtected && !isSignup && !isVerifyPage) {
    65→      const url = request.nextUrl.clone();
    66→      url.pathname = "/auth/verify-email";
    67→      return NextResponse.redirect(url);
    68→    }
    69→  } else {
    70→    // Verified user on auth pages → send to dashboard
    71→    if (isAuthPage || isVerifyPage) {
    72→      const url = request.nextUrl.clone();
    73→      url.pathname = "/(authenticated)/dashboard";
    74→      return NextResponse.redirect(url);
    75→    }
    76→  }
    77→
    78→  return supabaseResponse;
    79→}
    80→

<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 { NextResponse } from "next/server";
     2→import { createServerClient } from "@supabase/ssr";
     3→import { cookies } from "next/headers";
     4→
     5→export async function GET(request: Request) {
     6→  const { searchParams, origin } = new URL(request.url);
     7→  const code = searchParams.get("code");
     8→  const next = searchParams.get("next") ?? "/(authenticated)/dashboard";
     9→
    10→  if (code) {
    11→    const cookieStore = await cookies();
    12→
    13→    const supabase = createServerClient(
    14→      process.env.NEXT_PUBLIC_SUPABASE_URL!,
    15→      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    16→      {
    17→        cookies: {
    18→          getAll() {
    19→            return cookieStore.getAll();
    20→          },
    21→          setAll(cookiesToSet) {
    22→            try {
    23→              cookiesToSet.forEach(({ name, value, options }) =>
    24→                cookieStore.set(name, value, options)
    25→              );
    26→            } catch {
    27→              // Cookies can only be set in Server Actions or Route Handlers
    28→            }
    29→          },
    30→        },
    31→      }
    32→    );
    33→
    34→    const { error } = await supabase.auth.exchangeCodeForSession(code);
    35→
    36→    if (!error) {
    37→      return NextResponse.redirect(`${origin}${next}`);
    38→    }
    39→  }
    40→
    41→  return NextResponse.redirect(
    42→    `${origin}/auth/login?error=Could+not+authenticate`
    43→  );
    44→}
    45→

<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 { useState } from "react";
     4→import { useRouter } from "next/navigation";
     5→import Link from "next/link";
     6→import { createClient } from "@/lib/supabase/client";
     7→import { Button } from "@/components/ui/button";
     8→import { Input } from "@/components/ui/input";
     9→import { Label } from "@/components/ui/label";
    10→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
    11→
    12→export default function LoginPage() {
    13→  const [email, setEmail] = useState("");
    14→  const [password, setPassword] = useState("");
    15→  const [error, setError] = useState<string | null>(null);
    16→  const [loading, setLoading] = useState(false);
    17→  const router = useRouter();
    18→  const supabase = createClient();
    19→
    20→  async function handleLogin(e: React.FormEvent) {
    21→    e.preventDefault();
    22→    setLoading(true);
    23→    setError(null);
    24→
    25→    const { data, error } = await supabase.auth.signInWithPassword({
    26→      email,
    27→      password,
    28→    });
    29→
    30→    if (error) {
    31→      setError(error.message);
    32→      setLoading(false);
    33→      return;
    34→    }
    35→
    36→    // Check if email is verified before allowing dashboard access
    37→    if (data.user && !data.user.email_confirmed_at) {
    38→      router.push("/auth/verify-email");
    39→      router.refresh();
    40→      return;
    41→    }
    42→
    43→    router.push("/(authenticated)/dashboard");
    44→    router.refresh();
    45→  }
    46→
    47→  return (
    48→

    49→      <Card className="w-full max-w-sm">
    50→        <CardHeader className="text-center">
    51→          <CardTitle className="text-2xl font-bold">Welcome back</CardTitle>
    52→          <CardDescription>Sign in to your RateRight account</CardDescription>
    53→        </CardHeader>
    54→        <CardContent>
    55→          <form onSubmit={handleLogin} className="space-y-5">
    56→

    57→              Email
    58→              <Input
    59→                id="email"
    60→                type="email"
    61→                placeholder="you@example.com"
    62→                value={email}
    63→                onChange={(e) => setEmail(e.target.value)}
    64→                required
    65→                autoComplete="email"
    66→                className="h-14 text-base"
    67→              />
    68→

    69→

    70→              Password
    71→              <Input
    72→                id="password"
    73→                type="password"
    74→                placeholder="Your password"
    75→                value={password}
    76→                onChange={(e) => setPassword(e.target.value)}
    77→                required
    78→                autoComplete="current-password"
    79→                className="h-14 text-base"
    80→              />
    81→

    82→            {error && (
    83→
{error}

    84→            )}
    85→            <Button type="submit" className="w-full h-16 text-lg font-semibold" disabled={loading}>
    86→              {loading ? "Signing in..." : "Sign in"}
    87→            </Button>
    88→          </form>
    89→

    90→            Don't have an account?{" "}
    91→            <Link href="/auth/signup" className="text-primary underline hover:no-underline">
    92→              Sign up
    93→            </Link>
    94→

    95→        </CardContent>
    96→      </Card>
    97→

    98→  );
    99→}
   100→

<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 { useState, useEffect } from "react";
     4→import { useRouter } from "next/navigation";
     5→import { createClient } from "@/lib/supabase/client";
     6→import { Button } from "@/components/ui/button";
     7→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
     8→import { Mail, CheckCircle2, Clock } from "lucide-react";
     9→
    10→export default function VerifyEmailPage() {
    11→  const [emailSent, setEmailSent] = useState(false);
    12→  const [loading, setLoading] = useState(false);
    13→  const [error, setError] = useState<string | null>(null);
    14→  const router = useRouter();
    15→  const supabase = createClient();
    16→
    17→  useEffect(() => {
    18→    // Check if user is already logged in and email is verified
    19→    checkEmailStatus();
    20→  }, []);
    21→
    22→  async function checkEmailStatus() {
    23→    const { data: { user } } = await supabase.auth.getUser();
    24→
    25→    if (user?.email_confirmed_at) {
    26→      // Email is already verified, redirect to dashboard
    27→      router.push('/(authenticated)/dashboard');
    28→    }
    29→  }
    30→
    31→  async function resendVerificationEmail() {
    32→    setLoading(true);
    33→    setError(null);
    34→
    35→    try {
    36→      const { data: { user } } = await supabase.auth.getUser();
    37→
    38→      if (!user) {
    39→        setError("No user found. Please log in again.");
    40→        setLoading(false);
    41→        return;
    42→      }
    43→
    44→      const { error } = await supabase.auth.resend({
    45→        type: 'signup',
    46→        email: user.email as string,
    47→      });
    48→
    49→      if (error) {
    50→        setError(error.message);
    51→      } else {
    52→        setEmailSent(true);
    53→      }
    54→    } catch (error) {
    55→      setError("Failed to resend verification email.");
    56→    } finally {
    57→      setLoading(false);
    58→    }
    59→  }
    60→
    61→  return (
    62→

    63→      <Card className="w-full max-w-md">
    64→        <CardHeader className="text-center">
    65→

    66→            <Mail className="h-6 w-6 text-blue-600" />
    67→

    68→          <CardTitle className="mt-6 text-3xl font-extrabold text-gray-900">
    69→            Check your email
    70→          </CardTitle>
    71→          <CardDescription className="mt-2 text-sm text-gray-600">
    72→            We've sent a verification email to your address. Please check your inbox and click the link to verify your email.
    73→          </CardDescription>
    74→        </CardHeader>
    75→        <CardContent className="space-y-6">
    76→

    77→

    78→              <Clock className="h-5 w-5 text-blue-400 mt-0.5" />
    79→

    80→
Didn't receive the email?

    81→

    82→
Check your spam folder or click the button below to resend the verification email.

    83→

    84→

    85→

    86→

    87→
    88→          {error && (
    89→

    90→

    91→

    92→
Error

    93→

    94→
{error}

    95→

    96→

    97→

    98→

    99→          )}
   100→
   101→          {emailSent && (
   102→

   103→

   104→                <CheckCircle2 className="h-5 w-5 text-green-400" />
   105→

   106→
Email sent!

   107→

   108→
We've resent the verification email. Please check your inbox.

   109→

   110→

   111→

   112→

   113→          )}
   114→
   115→

   116→            <Button
   117→              onClick={resendVerificationEmail}
   118→              disabled={loading || emailSent}
   119→              className="w-full"
   120→              variant="outline"
   121→            >
   122→              {loading ? "Sending..." : emailSent ? "Email sent" : "Resend verification email"}
   123→            </Button>
   124→
   125→            <Button
   126→              onClick={() => router.push('/auth/login')}
   127→              variant="ghost"
   128→              className="w-full"
   129→            >
   130→              Back to login
   131→            </Button>
   132→

   133→        </CardContent>
   134→      </Card>
   135→

   136→  );
   137→}
   138→

<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 { NextRequest, NextResponse } from 'next/server';
     2→import { randomBytes } from 'crypto';
     3→
     4→// CSRF token configuration
     5→const CSRF_TOKEN_LENGTH = 32;
     6→const CSRF_COOKIE_NAME = 'csrf-token';
     7→const CSRF_HEADER_NAME = 'x-csrf-token';
     8→const CSRF_FORM_FIELD_NAME = 'csrf_token';
     9→
    10→/**
    11→ * Generate a cryptographically secure CSRF token
    12→ */
    13→export function generateCSRFToken(): string {
    14→  return randomBytes(CSRF_TOKEN_LENGTH).toString('hex');
    15→}
    16→
    17→/**
    18→ * Create a CSRF token and set it in a cookie
    19→ */
    20→export function createCSRFToken(): { token: string; cookie: string } {
    21→  const token = generateCSRFToken();
    22→  let cookie = `${CSRF_COOKIE_NAME}=${token}; Path=/; HttpOnly; SameSite=Strict; Max-Age=86400`; // 24 hours
    23→
    24→  // Add secure flag in production
    25→  if (process.env.NODE_ENV === 'production' && process.env.NEXT_PUBLIC_APP_URL?.startsWith('https://')) {
    26→    cookie += '; Secure';
    27→  }
    28→
    29→  return { token, cookie };
    30→}
    31→
    32→/**
    33→ * Get CSRF token from cookie
    34→ */
    35→export function getCSRFCookieToken(request: Request | NextRequest): string | null {
    36→  // Check if it's a NextRequest with cookies property
    37→  if ('cookies' in request && typeof request.cookies.get === 'function') {
    38→    const cookie = request.cookies.get(CSRF_COOKIE_NAME);
    39→    return cookie?.value || null;
    40→  }
    41→
    42→  // Fall back to parsing Cookie header for standard Request
    43→  const cookieHeader = request.headers.get('cookie');
    44→  if (!cookieHeader) return null;
    45→
    46→  const cookies = cookieHeader.split(';');
    47→  for (const cookie of cookies) {
    48→    const [name, value] = cookie.trim().split('=');
    49→    if (name === CSRF_COOKIE_NAME) {
    50→      return value;
    51→    }
    52→  }
    53→  return null;
    54→}
    55→
    56→/**
    57→ * Get CSRF token from request (header or form data)
    58→ */
    59→export function getCSRFRequestToken(request: Request | NextRequest): string | null {
    60→  // Check header first
    61→  const headerToken = request.headers.get(CSRF_HEADER_NAME);
    62→  if (headerToken) return headerToken;
    63→
    64→  // For form data, check in the request body (will need to parse appropriately)
    65→  // This is handled in the validation function for JSON requests
    66→  return null;
    67→}
    68→
    69→/**
    70→ * Validate CSRF token from request
    71→ * For JSON requests: checks x-csrf-token header
    72→ * For form requests: checks form field
    73→ */
    74→export async function validateCSRFToken(request: Request | NextRequest): Promise<boolean> {
    75→  const cookieToken = getCSRFCookieToken(request);
    76→
    77→  if (!cookieToken) {
    78→    console.error('CSRF validation failed: No token in cookie');
    79→    return false;
    80→  }
    81→
    82→  let requestToken: string | null = null;
    83→
    84→  // For JSON requests
    85→  if (request.headers.get('content-type')?.includes('application/json')) {
    86→    requestToken = request.headers.get(CSRF_HEADER_NAME);
    87→
    88→    // If no header, check in JSON body
    89→    if (!requestToken) {
    90→      try {
    91→        const body = await request.clone().json();
    92→        requestToken = body[CSRF_FORM_FIELD_NAME] || null;
    93→      } catch {
    94→        // Ignore JSON parsing errors
    95→      }
    96→    }
    97→  }
    98→
    99→  // For form data requests
   100→  else if (request.headers.get('content-type')?.includes('multipart/form-data')) {
   101→    try {
   102→      const formData = await request.clone().formData();
   103→      requestToken = formData.get(CSRF_FORM_FIELD_NAME) as string;
   104→    } catch {
   105→      // Ignore form parsing errors
   106→    }
   107→  }
   108→
   109→  if (!requestToken) {
   110→    console.error('CSRF validation failed: No token in request');
   111→    return false;
   112→  }
   113→
   114→  // Constant-time comparison to prevent timing attacks
   115→  const tokensMatch = timingSafeEqual(cookieToken, requestToken);
   116→
   117→  if (!tokensMatch) {
   118→    console.error('CSRF validation failed: Token mismatch');
   119→    return false;
   120→  }
   121→
   122→  return true;
   123→}
   124→
   125→/**
   126→ * Timing-safe string comparison
   127→ */
   128→function timingSafeEqual(a: string, b: string): boolean {
   129→  if (a.length !== b.length) return false;
   130→
   131→  let result = 0;
   132→  for (let i = 0; i < a.length; i++) {
   133→    result |= a.charCodeAt(i) ^ b.charCodeAt(i);
   134→  }
   135→
   136→  return result === 0;
   137→}
   138→
   139→/**
   140→ * Check if request is same-origin
   141→ */
   142→export function isSameOrigin(request: Request | NextRequest): boolean {
   143→  const origin = request.headers.get('origin');
   144→  const referer = request.headers.get('referer');
   145→
   146→  if (!origin && !referer) {
   147→    // No origin/referer headers - reject the request to prevent CSRF bypass
   148→    return false;
   149→  }
   150→
   151→  const checkUrl = origin || referer;
   152→  if (!checkUrl) return false;
   153→
   154→  try {
   155→    const parsedUrl = new URL(checkUrl);
   156→    const appUrl = new URL(process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000');
   157→
   158→    return parsedUrl.origin === appUrl.origin;
   159→  } catch {
   160→    return false;
   161→  }
   162→}
   163→
   164→/**
   165→ * CSRF protection middleware
   166→ * Use this for routes that need CSRF protection
   167→ */
   168→export function withCSRFProtection<T extends NextRequest>(
   169→  handler: (request: T) => Promise<NextResponse | Response>,
   170→  options?: {
   171→    requireSameOrigin?: boolean;
   172→    skipValidation?: boolean;
   173→  }
   174→): (request: T) => Promise<NextResponse | Response> {
   175→  return async (request: T) => {
   176→    // Skip CSRF for GET, HEAD, OPTIONS requests
   177→    if (['GET', 'HEAD', 'OPTIONS'].includes(request.method)) {
   178→      return handler(request);
   179→    }
   180→
   181→    // Check if request is same-origin (optional, but recommended)
   182→    if (options?.requireSameOrigin !== false && !isSameOrigin(request)) {
   183→      return NextResponse.json(
   184→        { error: 'Cross-origin request not allowed' },
   185→        { status: 403 }
   186→      );
   187→    }
   188→
   189→    // Validate CSRF token (unless explicitly skipped)
   190→    if (!options?.skipValidation) {
   191→      const isValid = await validateCSRFToken(request);
   192→      if (!isValid) {
   193→        return NextResponse.json(
   194→          { error: 'Invalid or missing CSRF token' },
   195→          { status: 403 }
   196→        );
   197→      }
   198→    }
   199→
   200→    return handler(request);
   201→  };
   202→}
   203→
   204→/**
   205→ * Create a CSRF token endpoint
   206→ * This should be called when a user session starts
   207→ */
   208→export async function createCSRTEndpoint(request: NextRequest): Promise<NextResponse> {
   209→  if (request.method !== 'GET') {
   210→    return NextResponse.json({ error: 'Method not allowed' }, { status: 405 });
   211→  }
   212→
   213→  const { token, cookie } = createCSRFToken();
   214→
   215→  const response = NextResponse.json({ token });
   216→  response.headers.append('Set-Cookie', cookie);
   217→
   218→  return response;
   219→}

<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>


Let me continue with the next batch of files:


     1→/**
     2→ * Client-side CSRF protection utilities
     3→ */
     4→
     5→const CSRF_HEADER_NAME = 'x-csrf-token';
     6→const CSRF_COOKIE_NAME = 'csrf-token';
     7→
     8→/**
     9→ * Get CSRF token from cookie
    10→ */
    11→export function getCSRFCookie(): string | null {
    12→  if (typeof document === 'undefined') return null;
    13→
    14→  const cookies = document.cookie.split(';');
    15→  for (const cookie of cookies) {
    16→    const [name, value] = cookie.trim().split('=');
    17→    if (name === CSRF_COOKIE_NAME) {
    18→      return value;
    19→    }
    20→  }
    21→  return null;
    22→}
    23→
    24→/**
    25→ * Fetch a new CSRF token from the server
    26→ * This should be called after authentication
    27→ */
    28→export async function fetchCSRFToken(): Promise<string | null> {
    29→  try {
    30→    const response = await fetch('/api/auth/csrf', {
    31→      method: 'GET',
    32→      credentials: 'include', // Include cookies
    33→    });
    34→
    35→    if (!response.ok) {
    36→      console.error('Failed to fetch CSRF token:', response.status);
    37→      return null;
    38→    }
    39→
    40→    const data = await response.json();
    41→    return data.token || null;
    42→  } catch (error) {
    43→    console.error('Error fetching CSRF token:', error);
    44→    return null;
    45→  }
    46→}
    47→
    48→/**
    49→ * Add CSRF token to fetch options
    50→ */
    51→export function withCSRFToken(options: RequestInit = {}): RequestInit {
    52→  const token = getCSRFCookie();
    53→
    54→  if (!token) {
    55→    console.warn('No CSRF token found in cookies');
    56→    return options;
    57→  }
    58→
    59→  return {
    60→    ...options,
    61→    headers: {
    62→      ...options.headers,
    63→      [CSRF_HEADER_NAME]: token,
    64→    },
    65→  };
    66→}
    67→
    68→/**
    69→ * Enhanced fetch with CSRF protection
    70→ */
    71→export async function csrfFetch(input: RequestInfo | URL, options?: RequestInit): Promise<Response> {
    72→  const enhancedOptions = withCSRFToken(options);
    73→  return fetch(input, enhancedOptions);
    74→}
    75→
    76→/**
    77→ * Add CSRF token to form data
    78→ */
    79→export function addCSRFTokenToFormData(formData: FormData): FormData {
    80→  const token = getCSRFCookie();
    81→
    82→  if (token) {
    83→    formData.append('csrf_token', token);
    84→  } else {
    85→    console.warn('No CSRF token found in cookies');
    86→  }
    87→
    88→  return formData;
    89→}
    90→
    91→/**
    92→ * Initialize CSRF token after authentication
    93→ * Call this after successful login/signup
    94→ */
    95→export async function initializeCSRF(): Promise<boolean> {
    96→  const token = await fetchCSRFToken();
    97→  return token !== null;
    98→}

<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 { NextRequest, NextResponse } from 'next/server';
     2→import { createCSRTEndpoint } from '@/lib/csrf';
     3→import { createClient } from '@/lib/supabase/server';
     4→
     5→/**
     6→ * CSRF Token Endpoint
     7→ * Generates and returns a CSRF token for authenticated users
     8→ * This should be called after successful authentication
     9→ */
    10→export async function GET(request: NextRequest) {
    11→  try {
    12→    // Check if user is authenticated
    13→    const supabase = await createClient();
    14→    const { data: { user } } = await supabase.auth.getUser();
    15→
    16→    if (!user) {
    17→      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    18→    }
    19→
    20→    // Create and return CSRF token
    21→    return createCSRTEndpoint(request);
    22→  } catch (error) {
    23→    console.error('CSRF token generation error:', error);
    24→    return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
    25→  }
    26→}

<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 { getSupabaseAdmin, createClient } from '@/lib/supabase/server';
     2→import { NextRequest, NextResponse } from 'next/server';
     3→import { checkRateLimit, getClientIP, createRateLimitResponse } from '@/lib/rate-limit';
     4→import { createPaymentIntent } from '@/lib/stripe/server';
     5→import { withCSRFProtection } from '@/lib/csrf';
     6→
     7→// Rate limit: 10 requests per 15 minutes per user (sensitive financial operation)
     8→const PAYMENT_RATE_LIMIT = 10;
     9→const PAYMENT_WINDOW_MS = 15 * 60 * 1000;
    10→
    11→const handler = async (request: NextRequest) => {
    12→  try {
    13→    // --- Rate limiting (by IP as initial check) ---
    14→    const clientIP = getClientIP(request);
    15→    const ipRateLimit = checkRateLimit(`payment-ip:${clientIP}`, PAYMENT_RATE_LIMIT, PAYMENT_WINDOW_MS);
    16→
    17→    if (!ipRateLimit.allowed) {
    18→      const retryAfter = Math.ceil((ipRateLimit.resetTime - Date.now()) / 1000);
    19→      return createRateLimitResponse(clientIP, retryAfter);
    20→    }
    21→
    22→    const supabase = await createClient();
    23→    const {
    24→      data: { user },
    25→    } = await supabase.auth.getUser();
    26→
    27→    if (!user) {
    28→      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    29→    }
    30→
    31→    // --- Stricter rate limiting by user ID ---
    32→    const userRateLimit = checkRateLimit(`payment-user:${user.id}`, PAYMENT_RATE_LIMIT, PAYMENT_WINDOW_MS);
    33→
    34→    if (!userRateLimit.allowed) {
    35→      const retryAfter = Math.ceil((userRateLimit.resetTime - Date.now()) / 1000);
    36→      return createRateLimitResponse(user.id, retryAfter);
    37→    }
    38→
    39→    const body = await request.json();
    40→    const { match_id, company_id } = body;
    41→    const amount = 50.0; // Hardcoded amount to prevent client override
    42→
    43→    if (!match_id || !company_id) {
    44→      return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
    45→    }
    46→
    47→    // Verify user owns the company
    48→    const { data: company } = await supabase
    49→      .from('companies')
    50→      .select('id, name')
    51→      .eq('id', company_id)
    52→      .eq('profile_id', user.id)
    53→      .single();
    54→
    55→    if (!company) {
    56→      return NextResponse.json({ error: 'Company not found or unauthorized' }, { status: 403 });
    57→    }
    58→
    59→    // Verify match exists and belongs to company's job (can be in any status except rejected)
    60→    const { data: match } = await supabase
    61→      .from('matches')
    62→      .select('status, jobs!inner(company_id, title)')
    63→      .eq('id', match_id)
    64→      .neq('status', 'rejected')
    65→      .eq('jobs.company_id', company_id)
    66→      .single();
    67→
    68→    if (!match) {
    69→      return NextResponse.json({ error: 'Match not found, already rejected, or unauthorized' }, { status: 403 });
    70→    }
    71→
    72→    // Check if payment already exists for this match
    73→    const { data: existingPayment } = await supabase
    74→      .from('payments')
    75→      .select('id, status, stripe_payment_id')
    76→      .eq('match_id', match_id)
    77→      .single();
    78→
    79→    if (existingPayment?.status === 'charged') {
    80→      return NextResponse.json({ error: 'Payment already completed for this hire' }, { status: 400 });
    81→    }
    82→
    83→    // Create Stripe PaymentIntent
    84→    const jobTitle = (match.jobs as { title?: string }).title || 'Job';
    85→    const { clientSecret, paymentIntentId } = await createPaymentIntent({
    86→      amount,
    87→      matchId: match_id,
    88→      companyId: company_id,
    89→      companyName: company.name,
    90→      description: `Hire for ${jobTitle}`,
    91→    });
    92→
    93→    // Create or update payment record using service role
    94→    const supabaseService = getSupabaseAdmin();
    95→
    96→    let payment;
    97→    if (existingPayment) {
    98→      // Update existing payment record with new PaymentIntent
    99→      const { data: updatedPayment, error } = await supabaseService
   100→        .from('payments')
   101→        .update({
   102→          stripe_payment_id: paymentIntentId,
   103→          amount,
   104→          status: 'pending',
   105→        })
   106→        .eq('id', existingPayment.id)
   107→        .select()
   108→        .single();
   109→
   110→      if (error) {
   111→        return NextResponse.json({ error: error.message }, { status: 500 });
   112→      }
   113→      payment = updatedPayment;
   114→    } else {
   115→      // Create new payment record
   116→      const { data: newPayment, error } = await supabaseService
   117→        .from('payments')
   118→        .insert({
   119→          match_id,
   120→          company_id,
   121→          amount,
   122→          status: 'pending',
   123→          stripe_payment_id: paymentIntentId,
   124→        })
   125→        .select()
   126→        .single();
   127→
   128→      if (error) {
   129→        return NextResponse.json({ error: error.message }, { status: 500 });
   130→      }
   131→      payment = newPayment;
   132→    }
   133→
   134→    return NextResponse.json({
   135→      payment,
   136→      clientSecret,
   137→      publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
   138→    });
   139→  } catch (error) {
   140→    console.error('Payment creation error:', error);
   141→    if (error instanceof Error && error.message.includes('Stripe')) {
   142→      return NextResponse.json({ error: 'Payment service unavailable' }, { status: 503 });
   143→    }
   144→    return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
   145→  }
   146→}
   147→
   148→// Apply CSRF protection to the handler
   149→export const POST = withCSRFProtection(handler);
   150→

<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 { NextRequest, NextResponse } from 'next/server';
     2→import { constructWebhookEvent, stripe } from '@/lib/stripe/server';
     3→import { getSupabaseAdmin } from '@/lib/supabase/server';
     4→import type Stripe from 'stripe';
     5→
     6→// Disable body parsing for webhook verification
     7→export const runtime = 'nodejs';
     8→export const dynamic = 'force-dynamic';
     9→
    10→export async function POST(request: NextRequest) {
    11→  try {
    12→    // Get the raw body as text for signature verification
    13→    const payload = await request.text();
    14→    const signature = request.headers.get('stripe-signature');
    15→
    16→    if (!signature) {
    17→      return NextResponse.json({ error: 'Missing stripe-signature header' }, { status: 400 });
    18→    }
    19→
    20→    // Verify webhook signature and construct event
    21→    let event: Stripe.Event;
    22→    try {
    23→      event = constructWebhookEvent(payload, signature);
    24→    } catch (error) {
    25→      console.error('Webhook signature verification failed:', error);
    26→      return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
    27→    }
    28→
    29→    const supabaseService = getSupabaseAdmin();
    30→
    31→    // Handle specific event types
    32→    switch (event.type) {
    33→      case 'payment_intent.succeeded': {
    34→        const paymentIntent = event.data.object as Stripe.PaymentIntent;
    35→        await handlePaymentSuccess(supabaseService, paymentIntent);
    36→        break;
    37→      }
    38→
    39→      case 'payment_intent.payment_failed': {
    40→        const paymentIntent = event.data.object as Stripe.PaymentIntent;
    41→        await handlePaymentFailure(supabaseService, paymentIntent);
    42→        break;
    43→      }
    44→
    45→      case 'payment_intent.canceled': {
    46→        const paymentIntent = event.data.object as Stripe.PaymentIntent;
    47→        await handlePaymentCancellation(supabaseService, paymentIntent);
    48→        break;
    49→      }
    50→
    51→      default:
    52→        console.log(`Unhandled event type: ${event.type}`);
    53→    }
    54→
    55→    return NextResponse.json({ received: true });
    56→  } catch (error) {
    57→    console.error('Webhook error:', error);
    58→    return NextResponse.json({ error: 'Webhook processing failed' }, { status: 500 });
    59→  }
    60→}
    61→
    62→async function handlePaymentSuccess(
    63→  supabase: ReturnType<typeof getSupabaseAdmin>,
    64→  paymentIntent: Stripe.PaymentIntent
    65→) {
    66→  const matchId = paymentIntent.metadata?.match_id;
    67→
    68→  if (!matchId) {
    69→    console.error('No match_id in payment intent metadata');
    70→    return;
    71→  }
    72→
    73→  try {
    74→    // Update payment status to charged
    75→    const { data: payment, error: paymentError } = await supabase
    76→      .from('payments')
    77→      .update({
    78→        status: 'charged',
    79→      })
    80→      .eq('stripe_payment_id', paymentIntent.id)
    81→      .select('id, match_id, company_id')
    82→      .single();
    83→
    84→    if (paymentError) {
    85→      console.error('Error updating payment status:', paymentError);
    86→      return;
    87→    }
    88→
    89→    if (!payment) {
    90→      console.error('Payment not found for intent:', paymentIntent.id);
    91→      return;
    92→    }
    93→
    94→    // Update match status to hired
    95→    const { error: matchError } = await supabase
    96→      .from('matches')
    97→      .update({
    98→        status: 'hired',
    99→        contractor_action: 'accepted',
   100→        hired_at: new Date().toISOString(),
   101→        fee_charged: true,
   102→      })
   103→      .eq('id', matchId);
   104→
   105→    if (matchError) {
   106→      console.error('Error updating match status:', matchError);
   107→      return;
   108→    }
   109→
   110→    // Create notification for worker
   111→    const { data: match } = await supabase
   112→      .from('matches')
   113→      .select('worker_id, jobs(title)')
   114→      .eq('id', matchId)
   115→      .single();
   116→
   117→    if (match) {
   118→      const jobTitle = (match.jobs as { title?: string }).title || 'a job';
   119→      await supabase.from('notifications').insert({
   120→        profile_id: match.worker_id,
   121→        type: 'hire_confirmed',
   122→        title: 'You\'ve been hired!',
   123→        body: `Congratulations! You've been hired for ${jobTitle}.`,
   124→        data: { match_id: matchId, payment_id: payment.id },
   125→      });
   126→    }
   127→
   128→    console.log('Payment success processed for match:', matchId);
   129→  } catch (error) {
   130→    console.error('Error processing payment success:', error);
   131→  }
   132→}
   133→
   134→async function handlePaymentFailure(
   135→  supabase: ReturnType<typeof getSupabaseAdmin>,
   136→  paymentIntent: Stripe.PaymentIntent
   137→) {
   138→  try {
   139→    const { error } = await supabase
   140→      .from('payments')
   141→      .update({
   142→        status: 'pending', // Keep as pending to allow retry
   143→      })
   144→      .eq('stripe_payment_id', paymentIntent.id);
   145→
   146→    if (error) {
   147→      console.error('Error updating payment failure:', error);
   148→    }
   149→
   150→    console.log('Payment failed for intent:', paymentIntent.id);
   151→  } catch (error) {
   152→    console.error('Error handling payment failure:', error);
   153→  }
   154→}
   155→
   156→async function handlePaymentCancellation(
   157→  supabase: ReturnType<typeof getSupabaseAdmin>,
   158→  paymentIntent: Stripe.PaymentIntent
   159→) {
   160→  try {
   161→    const { error } = await supabase
   162→      .from('payments')
   163→      .delete()
   164→      .eq('stripe_payment_id', paymentIntent.id)
   165→      .eq('status', 'pending');
   166→
   167→    if (error) {
   168→      console.error('Error deleting cancelled payment:', error);
   169→    }
   170→
   171→    console.log('Payment cancelled for intent:', paymentIntent.id);
   172→  } catch (error) {
   173→    console.error('Error handling payment cancellation:', error);
   174→  }
   175→}
   176→

<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 { NextRequest, NextResponse } from "next/server";
     2→import OpenAI from "openai";
     3→import { checkRateLimit, getClientIP, createRateLimitResponse } from "@/lib/rate-limit";
     4→import { sanitizeInput } from "@/lib/sanitize";
     5→import { withCSRFProtection } from "@/lib/csrf";
     6→
     7→const openai = new OpenAI({
     8→  apiKey: process.env.OPENAI_API_KEY || "",
     9→});
    10→
    11→// Rate limit: 5 requests per 15 minutes per IP (expensive AI call)
    12→const AI_RATE_LIMIT = 5;
    13→const AI_WINDOW_MS = 15 * 60 * 1000;
    14→
    15→const handler = async (request: NextRequest) => {
    16→  try {
    17→    // --- Rate limiting ---
    18→    const clientIP = getClientIP(request);
    19→    const rateLimit = checkRateLimit(`ai-profile:${clientIP}`, AI_RATE_LIMIT, AI_WINDOW_MS);
    20→
    21→    if (!rateLimit.allowed) {
    22→      const retryAfter = Math.ceil((rateLimit.resetTime - Date.now()) / 1000);
    23→      return createRateLimitResponse(clientIP, retryAfter);
    24→    }
    25→
    26→    const { trade, experience, location, availability } = await request.json();
    27→
    28→    if (!trade || experience === undefined || !location || !availability) {
    29→      return NextResponse.json(
    30→        { error: "Missing required fields" },
    31→        { status: 400 }
    32→      );
    33→    }
    34→
    35→    // Sanitize inputs to prevent prompt injection
    36→    const sanitizedTrade = sanitizeInput(trade, { maxLength: 100 });
    37→    const sanitizedLocation = sanitizeInput(location, { maxLength: 200 });
    38→    const sanitizedAvailability = sanitizeInput(availability, { maxLength: 50 });
    39→
    40→    // Validate experience is a number
    41→    const experienceYears = parseInt(experience, 10);
    42→    if (isNaN(experienceYears) || experienceYears < 0 || experienceYears > 100) {
    43→      return NextResponse.json(
    44→        { error: "Invalid experience value" },
    45→        { status: 400 }
    46→      );
    47→    }
    48→
    49→    const prompt = `You are an expert construction industry recruiter. Based on the following information about a construction worker, generate a professional profile.
    50→
    51→Worker Information:
    52→- Trade: ${sanitizedTrade}
    53→- Years of Experience: ${experienceYears} years
    54→- Location: ${sanitizedLocation}
    55→- Availability: ${sanitizedAvailability}
    56→
    57→Generate a JSON response with:
    58→1. bio: A professional 2-3 sentence bio (50-80 words) highlighting their expertise, experience, and work style
    59→2. skills: An array of 6-10 relevant technical skills for their trade (specific tools, techniques, materials)
    60→3. certifications: An array of 3-5 common/recommended certifications for their trade (actual Australian construction certifications)
    61→
    62→Make it sound professional but approachable. Focus on practical skills and experience.
    63→
    64→Return ONLY valid JSON in this exact format:
    65→{
    66→  "bio": "string",
    67→  "skills": ["skill1", "skill2", ...],
    68→  "certifications": ["cert1", "cert2", ...]
    69→}`;
    70→
    71→    const completion = await openai.chat.completions.create({
    72→      model: "gpt-4o-mini",
    73→      messages: [
    74→        {
    75→          role: "system",
    76→          content: "You are a construction industry expert. Always respond with valid JSON only.",
    77→        },
    78→        {
    79→          role: "user",
    80→          content: prompt,
    81→        },
    82→      ],
    83→      temperature: 0.7,
    84→      response_format: { type: "json_object" },
    85→    });
    86→
    87→    const content = completion.choices[0]?.message?.content;
    88→    if (!content) {
    89→      throw new Error("No response from AI");
    90→    }
    91→
    92→    const generated = JSON.parse(content);
    93→
    94→    return NextResponse.json({
    95→      bio: generated.bio || "",
    96→      skills: generated.skills || [],
    97→      certifications: generated.certifications || [],
    98→    });
    99→  } catch (error) {
   100→    console.error("AI generation error:", error);
   101→    return NextResponse.json(
   102→      { error: "Failed to generate profile" },
   103→      { status: 500 }
   104→    );
   105→  }
   106→}
   107→
   108→// Apply CSRF protection to the handler
   109→export const POST = withCSRFProtection(handler);
   110→

<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>


Let me continue reading the remaining files:


     1→import { NextRequest, NextResponse } from "next/server";
     2→import OpenAI from "openai";
     3→import { createClient } from "@/lib/supabase/server";
     4→import { checkRateLimit, getClientIP, createRateLimitResponse } from "@/lib/rate-limit";
     5→import { withErrorHandler, APIError, ErrorCodes } from "@/lib/api-errors";
     6→import { sanitizeInput } from "@/lib/sanitize";
     7→import { validateCSRFToken, isSameOrigin } from "@/lib/csrf";
     8→
     9→// Cast helper to use NextRequest methods while satisfying withErrorHandler signature
    10→type Handler = (request: Request) => Promise<Response>;
    11→
    12→const openai = new OpenAI({
    13→  apiKey: process.env.OPENAI_API_KEY || "",
    14→});
    15→
    16→const AI_RATE_LIMIT = 10;
    17→const AI_WINDOW_MS = 15 * 60 * 1000;
    18→
    19→const VALID_TRADES = [
    20→  "Labourer", "Steel Fixer", "Formworker", "Carpenter", "Electrician",
    21→  "Plumber", "Concreter", "Bricklayer", "Painter", "Roofer",
    22→  "Scaffolder", "Tiler", "Plasterer", "Welder", "Crane Operator",
    23→  "Excavator Operator",
    24→];
    25→
    26→const VALID_AVAILABILITY = ["full-time", "casual", "weekends", "flexible"];
    27→
    28→async function handler(request: Request) {
    29→  const nextRequest = request as NextRequest;
    30→  // CSRF protection: validate same-origin and token
    31→  if (!isSameOrigin(request)) {
    32→    return NextResponse.json({ error: 'Cross-origin request not allowed' }, { status: 403 });
    33→  }
    34→  const csrfValid = await validateCSRFToken(request);
    35→  if (!csrfValid) {
    36→    return NextResponse.json({ error: 'Invalid or missing CSRF token' }, { status: 403 });
    37→  }
    38→
    39→  // Auth check
    40→  const supabase = await createClient();
    41→  const { data: { user } } = await supabase.auth.getUser();
    42→  if (!user) {
    43→    throw new APIError("Unauthorized", 401, ErrorCodes.UNAUTHORIZED);
    44→  }
    45→
    46→  // Rate limit
    47→  const clientIP = getClientIP(request);
    48→  const rateLimit = checkRateLimit(`parse-voice:${clientIP}`, AI_RATE_LIMIT, AI_WINDOW_MS);
    49→  if (!rateLimit.allowed) {
    50→    const retryAfter = Math.ceil((rateLimit.resetTime - Date.now()) / 1000);
    51→    return createRateLimitResponse(clientIP, retryAfter);
    52→  }
    53→
    54→  const body = await request.json();
    55→  const { transcript } = body;
    56→
    57→  if (!transcript || typeof transcript !== "string") {
    58→    throw new APIError("Missing transcript", 400, ErrorCodes.INVALID_INPUT);
    59→  }
    60→
    61→  // Sanitize transcript to prevent prompt injection
    62→  const sanitizedTranscript = sanitizeInput(transcript, { maxLength: 5000 });
    63→
    64→  try {
    65→    const completion = await openai.chat.completions.create({
    66→      model: "gpt-4o-mini",
    67→      messages: [
    68→        {
    69→          role: "system",
    70→          content: `You are a profile parser for an Australian construction hiring app. Extract structured data from a worker's spoken description.
    71→
    72→Valid trades: ${VALID_TRADES.join(", ")}
    73→Valid availability: ${VALID_AVAILABILITY.join(", ")}
    74→
    75→Return ONLY valid JSON in this exact format:
    76→{
    77→  "trade": "one of the valid trades or null",
    78→  "experience_years": number or null,
    79→  "location": "suburb/area string or null",
    80→  "availability": "one of the valid availability options or null"
    81→}
    82→
    83→Match trades loosely — e.g. "sparky" = "Electrician", "chippie" = "Carpenter", "bricky" = "Bricklayer".
    84→For experience, extract the number of years mentioned.
    85→For location, extract any suburb, city, or area name mentioned.
    86→For availability, match to the closest option.
    87→If a field is not mentioned, set it to null.`,
    88→        },
    89→        {
    90→          role: "user",
    91→          content: sanitizedTranscript,
    92→        },
    93→      ],
    94→      temperature: 0.3,
    95→      response_format: { type: "json_object" },
    96→    });
    97→
    98→    const content = completion.choices[0]?.message?.content;
    99→    if (!content) {
   100→      throw new APIError("No response from AI", 500, ErrorCodes.EXTERNAL_SERVICE_ERROR);
   101→    }
   102→
   103→    const parsed = JSON.parse(content);
   104→
   105→    // Validate trade
   106→    if (parsed.trade && !VALID_TRADES.includes(parsed.trade)) {
   107→      parsed.trade = null;
   108→    }
   109→
   110→    // Validate availability
   111→    if (parsed.availability && !VALID_AVAILABILITY.includes(parsed.availability)) {
   112→      parsed.availability = null;
   113→    }
   114→
   115→    // Validate experience_years
   116→    if (parsed.experience_years !== null && (typeof parsed.experience_years !== "number" || parsed.experience_years < 0)) {
   117→      parsed.experience_years = null;
   118→    }
   119→
   120→    return Response.json(parsed);
   121→  } catch (error) {
   122→    console.error("OpenAI parsing error:", error);
   123→    if (error instanceof APIError) {
   124→      throw error;
   125→    }
   126→    throw new APIError(
   127→      "Failed to parse voice input",
   128→      500,
   129→      ErrorCodes.EXTERNAL_SERVICE_ERROR
   130→    );
   131→  }
   132→}
   133→
   134→export const POST = withErrorHandler(handler);
   135→

<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 { NextRequest, NextResponse } from "next/server";
     2→import OpenAI from "openai";
     3→import { createClient } from "@/lib/supabase/server";
     4→import { checkRateLimit, getClientIP, createRateLimitResponse } from "@/lib/rate-limit";
     5→import { withErrorHandler, APIError, ErrorCodes } from "@/lib/api-errors";
     6→import { validateCSRFToken, isSameOrigin } from "@/lib/csrf";
     7→
     8→const openai = new OpenAI({
     9→  apiKey: process.env.OPENAI_API_KEY || "",
    10→});
    11→
    12→const AI_RATE_LIMIT = 10;
    13→const AI_WINDOW_MS = 15 * 60 * 1000;
    14→
    15→async function handler(request: Request) {
    16→  // CSRF protection: validate same-origin and token
    17→  if (!isSameOrigin(request)) {
    18→    return NextResponse.json({ error: 'Cross-origin request not allowed' }, { status: 403 });
    19→  }
    20→  const csrfValid = await validateCSRFToken(request);
    21→  if (!csrfValid) {
    22→    return NextResponse.json({ error: 'Invalid or missing CSRF token' }, { status: 403 });
    23→  }
    24→
    25→  // Auth check
    26→  const supabase = await createClient();
    27→  const { data: { user } } = await supabase.auth.getUser();
    28→  if (!user) {
    29→    throw new APIError("Unauthorized", 401, ErrorCodes.UNAUTHORIZED);
    30→  }
    31→
    32→  // Rate limit
    33→  const clientIP = getClientIP(request);
    34→  const rateLimit = checkRateLimit(`transcribe:${clientIP}`, AI_RATE_LIMIT, AI_WINDOW_MS);
    35→  if (!rateLimit.allowed) {
    36→    const retryAfter = Math.ceil((rateLimit.resetTime - Date.now()) / 1000);
    37→    return createRateLimitResponse(clientIP, retryAfter);
    38→  }
    39→
    40→  const formData = await request.formData();
    41→  const audioFile = formData.get("audio") as File | null;
    42→
    43→  if (!audioFile) {
    44→    throw new APIError("No audio file provided", 400, ErrorCodes.INVALID_INPUT);
    45→  }
    46→
    47→  try {
    48→    const transcription = await openai.audio.transcriptions.create({
    49→      file: audioFile,
    50→      model: "whisper-1",
    51→      language: "en",
    52→    });
    53→
    54→    return Response.json({ text: transcription.text });
    55→  } catch (error) {
    56→    console.error("OpenAI transcription error:", error);
    57→    throw new APIError(
    58→      "Failed to transcribe audio",
    59→      500,
    60→      ErrorCodes.EXTERNAL_SERVICE_ERROR
    61→    );
    62→  }
    63→}
    64→
    65→export const POST = withErrorHandler(handler);
    66→

<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→/**
     2→ * Email verification API endpoint
     3→ * Placeholder for email verification
     4→ */
     5→
     6→import { NextRequest, NextResponse } from 'next/server'
     7→import { checkRateLimit, getClientIP, createRateLimitResponse } from '@/lib/rate-limit'
     8→import { withCSRFProtection } from '@/lib/csrf'
     9→
    10→// Rate limit: 5 requests per 15 minutes per IP (auth endpoint)
    11→const VERIFY_RATE_LIMIT = 5;
    12→const VERIFY_WINDOW_MS = 15 * 60 * 1000;
    13→
    14→const handler = async (request: NextRequest) => {
    15→  try {
    16→    // --- Rate limiting ---
    17→    const clientIP = getClientIP(request);
    18→    const rateLimit = checkRateLimit(`verify-email:${clientIP}`, VERIFY_RATE_LIMIT, VERIFY_WINDOW_MS);
    19→
    20→    if (!rateLimit.allowed) {
    21→      const retryAfter = Math.ceil((rateLimit.resetTime - Date.now()) / 1000);
    22→      return createRateLimitResponse(clientIP, retryAfter);
    23→    }
    24→
    25→    const { userId, token } = await request.json()
    26→
    27→    if (!userId || !token) {
    28→      return NextResponse.json({ error: 'Missing required fields' }, { status: 400 })
    29→    }
    30→
    31→    // In production, implement proper token validation
    32→    // For now, return success
    33→
    34→    return NextResponse.json({
    35→      success: true,
    36→      message: 'Email verification endpoint ready'
    37→    })
    38→
    39→  } catch (error) {
    40→    console.error('Email verification error:', error)
    41→    return NextResponse.json(
    42→      { error: 'Internal server error' },
    43→      { status: 500 }
    44→    )
    45→  }
    46→}
    47→
    48→// Apply CSRF protection to the handler
    49→export const POST = withCSRFProtection(handler);
    50→

<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 { createClient } from "@/lib/supabase/server";
     2→import { NextResponse } from "next/server";
     3→
     4→export async function GET() {
     5→  try {
     6→    const supabase = await createClient();
     7→
     8→    // Check if user is authenticated
     9→    const { data: { user }, error: authError } = await supabase.auth.getUser();
    10→
    11→    if (authError || !user) {
    12→      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    13→    }
    14→
    15→    // Get conversation previews using the database function
    16→    const { data: conversations, error } = await supabase
    17→      .rpc('get_conversation_previews', { user_id: user.id });
    18→
    19→    if (error) {
    20→      console.error("Error fetching conversations:", error);
    21→      return NextResponse.json(
    22→        { error: "Failed to fetch conversations" },
    23→        { status: 500 }
    24→      );
    25→    }
    26→
    27→    return NextResponse.json({ conversations: conversations || [] });
    28→  } catch (error) {
    29→    console.error("Error in messages API:", error);
    30→    return NextResponse.json(
    31→      { error: "Internal server error" },
    32→      { status: 500 }
    33→    );
    34→  }
    35→}

<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 { createClient } from "@/lib/supabase/server";
     2→import { NextResponse } from "next/server";
     3→
     4→export async function POST(request: Request) {
     5→  try {
     6→    const supabase = await createClient();
     7→
     8→    // Check if user is authenticated
     9→    const { data: { user }, error: authError } = await supabase.auth.getUser();
    10→
    11→    if (authError || !user) {
    12→      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    13→    }
    14→
    15→    const { recipientId, initialMessage } = await request.json();
    16→
    17→    if (!recipientId) {
    18→      return NextResponse.json(
    19→        { error: "Recipient ID is required" },
    20→        { status: 400 }
    21→      );
    22→    }
    23→
    24→    // Verify recipient exists
    25→    const { data: recipient, error: recipientError } = await supabase
    26→      .from('profiles')
    27→      .select('id, name')
    28→      .eq('id', recipientId)
    29→      .single();
    30→
    31→    if (recipientError || !recipient) {
    32→      return NextResponse.json(
    33→        { error: "Recipient not found" },
    34→        { status: 404 }
    35→      );
    36→    }
    37→
    38→    // Get or create conversation using the database function
    39→    const { data: conversationId, error: conversationError } = await supabase
    40→      .rpc('get_or_create_conversation', {
    41→        user1_id: user.id,
    42→        user2_id: recipientId
    43→      });
    44→
    45→    if (conversationError) {
    46→      console.error("Error creating conversation:", conversationError);
    47→      return NextResponse.json(
    48→        { error: "Failed to create conversation" },
    49→        { status: 500 }
    50→      );
    51→    }
    52→
    53→    // If there's an initial message, send it
    54→    if (initialMessage?.trim()) {
    55→      const { error: messageError } = await supabase
    56→        .from('messages')
    57→        .insert({
    58→          conversation_id: conversationId,
    59→          sender_id: user.id,
    60→          content: initialMessage.trim()
    61→        });
    62→
    63→      if (messageError) {
    64→        console.error("Error sending initial message:", messageError);
    65→        // Don't fail the whole request if message fails
    66→      }
    67→    }
    68→
    69→    return NextResponse.json({
    70→      conversationId,
    71→      message: "Conversation created successfully"
    72→    });
    73→  } catch (error) {
    74→    console.error("Error in start conversation API:", error);
    75→    return NextResponse.json(
    76→      { error: "Internal server error" },
    77→      { status: 500 }
    78→    );
    79→  }
    80→}
    81→

<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>


Let me continue with the remaining files:


     1→import { createClient } from "@/lib/supabase/server";
     2→import { NextRequest, NextResponse } from "next/server";
     3→import { validateCSRFToken, isSameOrigin } from "@/lib/csrf";
     4→
     5→export async function POST(
     6→  request: NextRequest,
     7→  { params }: { params: Promise<{ id: string }> }
     8→) {
     9→  try {
    10→    // CSRF protection: validate same-origin and token
    11→    if (!isSameOrigin(request)) {
    12→      return NextResponse.json({ error: 'Cross-origin request not allowed' }, { status: 403 });
    13→    }
    14→    const csrfValid = await validateCSRFToken(request);
    15→    if (!csrfValid) {
    16→      return NextResponse.json({ error: 'Invalid or missing CSRF token' }, { status: 403 });
    17→    }
    18→
    19→    const { id: conversationId } = await params;
    20→    const supabase = await createClient();
    21→
    22→    // Check if user is authenticated
    23→    const { data: { user }, error: authError } = await supabase.auth.getUser();
    24→
    25→    if (authError || !user) {
    26→      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    27→    }
    28→    const { content, attachments = [] } = await request.json();
    29→
    30→    if (!content?.trim()) {
    31→      return NextResponse.json(
    32→        { error: "Message content is required" },
    33→        { status: 400 }
    34→      );
    35→    }
    36→
    37→    // Verify user is a participant in this conversation
    38→    const { data: participant, error: participantError } = await supabase
    39→      .from('conversation_participants')
    40→      .select('id')
    41→      .eq('conversation_id', conversationId)
    42→      .eq('profile_id', user.id)
    43→      .single();
    44→
    45→    if (participantError || !participant) {
    46→      return NextResponse.json({ error: "Conversation not found" }, { status: 404 });
    47→    }
    48→
    49→    // Insert the message
    50→    const { data: message, error: messageError } = await supabase
    51→      .from('messages')
    52→      .insert({
    53→        conversation_id: conversationId,
    54→        sender_id: user.id,
    55→        content: content.trim(),
    56→        attachments: attachments
    57→      })
    58→      .select(`
    59→        id,
    60→        content,
    61→        sender_id,
    62→        status,
    63→        created_at,
    64→        attachments
    65→      `)
    66→      .single();
    67→
    68→    if (messageError) {
    69→      console.error("Error sending message:", messageError);
    70→      return NextResponse.json(
    71→        { error: "Failed to send message" },
    72→        { status: 500 }
    73→      );
    74→    }
    75→
    76→    // Get the other participant to send notification
    77→    const { data: otherParticipant } = await supabase
    78→      .from('conversation_participants')
    79→      .select('profile_id')
    80→      .eq('conversation_id', conversationId)
    81→      .neq('profile_id', user.id)
    82→      .single();
    83→
    84→    if (otherParticipant) {
    85→      // Create notification for the other participant
    86→      const { data: senderProfile } = await supabase
    87→        .from('profiles')
    88→        .select('name')
    89→        .eq('id', user.id)
    90→        .single();
    91→
    92→      await supabase.from('notifications').insert({
    93→        profile_id: otherParticipant.profile_id,
    94→        type: 'system',
    95→        title: 'New message',
    96→        body: `${senderProfile?.name || 'Someone'} sent you a message`,
    97→        data: {
    98→          conversationId: conversationId,
    99→          senderId: user.id
   100→        }
   101→      });
   102→    }
   103→
   104→    return NextResponse.json({
   105→      message: {
   106→        id: message.id,
   107→        content: message.content,
   108→        isOwn: true,
   109→        timestamp: message.created_at,
   110→        status: message.status,
   111→        attachments: message.attachments
   112→      }
   113→    });
   114→  } catch (error) {
   115→    console.error("Error in send message API:", error);
   116→    return NextResponse.json(
   117→      { error: "Internal server error" },
   118→      { status: 500 }
   119→    );
   120→  }
   121→}

<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 type { Metadata, Viewport } from "next";
     2→import { Geist, Geist_Mono } from "next/font/google";
     3→import { Toaster } from "react-hot-toast";
     4→import "./globals.css";
     5→
     6→const geistSans = Geist({
     7→  variable: "--font-geist-sans",
     8→  subsets: ["latin"],
     9→});
    10→
    11→const geistMono = Geist_Mono({
    12→  variable: "--font-geist-mono",
    13→  subsets: ["latin"],
    14→});
    15→
    16→export const metadata: Metadata = {
    17→  title: "RateRight — Hire Construction Workers Fast",
    18→  description:
    19→    "The simplest way to hire construction workers in Australia. AI does the work, you just tap. $50 flat fee per hire.",
    20→  keywords: [
    21→    "construction hiring",
    22→    "hire labourers",
    23→    "construction workers Australia",
    24→    "hire tradies",
    25→    "construction jobs Sydney",
    26→  ],
    27→  manifest: "/manifest.json",
    28→  appleWebApp: {
    29→    capable: true,
    30→    statusBarStyle: "default",
    31→    title: "RateRight",
    32→  },
    33→  other: {
    34→    "mobile-web-app-capable": "yes",
    35→    "apple-mobile-web-app-capable": "yes",
    36→    "application-name": "RateRight",
    37→    "msapplication-starturl": "/",
    38→    "msapplication-TileColor": "#2563eb",
    39→    "theme-color": "#2563eb",
    40→  },
    41→};
    42→
    43→export const viewport: Viewport = {
    44→  width: "device-width",
    45→  initialScale: 1,
    46→  maximumScale: 1,
    47→  themeColor: "#2563eb",
    48→};
    49→
    50→export default function RootLayout({
    51→  children,
    52→}: Readonly<{
    53→  children: React.ReactNode;
    54→}>) {
    55→  return (
    56→    <html lang="en-AU">
    57→      <head>
    58→        <link rel="manifest" href="/manifest.json" />
    59→        <link rel="apple-touch-icon" href="/apple-touch-icon.svg" />
    60→        <link rel="icon" type="image/svg+xml" href="/icon-192x192.svg" />
    61→        <meta name="apple-mobile-web-app-status-bar-style" content="default" />
    62→        <meta name="apple-mobile-web-app-title" content="RateRight" />
    63→        <meta name="mobile-web-app-capable" content="yes" />
    64→        <meta name="apple-mobile-web-app-capable" content="yes" />
    65→        <meta name="application-name" content="RateRight" />
    66→        <meta name="msapplication-starturl" content="/" />
    67→        <meta name="msapplication-TileColor" content="#2563eb" />
    68→        <meta name="theme-color" content="#2563eb" />
    69→      </head>
    70→      <body
    71→        className={`${geistSans.variable} ${geistMono.variable} font-sans antialiased`}
    72→      >
    73→        {children}
    74→        <Toaster position="top-right" />
    75→        <script
    76→          dangerouslySetInnerHTML={{
    77→            __html: `
    78→              if ('serviceWorker' in navigator) {
    79→                window.addEventListener('load', () => {
    80→                  navigator.serviceWorker.register('/sw.js').then(
    81→                    (registration) => {
    82→                      console.log('SW registered:', registration);
    83→                    },
    84→                    (err) => {
    85→                      console.log('SW registration failed:', err);
    86→                    }
    87→                  );
    88→                });
    89→              }
    90→            `,
    91→          }}
    92→        />
    93→      </body>
    94→    </html>
    95→  );
    96→}
    97→

<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 { redirect } from 'next/navigation'
     2→import { createClient } from '@/lib/supabase/server'
     3→import Link from 'next/link'
     4→import { Home, Briefcase, MessageSquare, User } from 'lucide-react'
     5→
     6→export default async function AuthenticatedLayout({
     7→  children,
     8→}: {
     9→  children: React.ReactNode
    10→}) {
    11→  const supabase = await createClient()
    12→  const { data: { user } } = await supabase.auth.getUser()
    13→
    14→  if (!user) {
    15→    redirect('/auth/login')
    16→  }
    17→
    18→  return (
    19→

    20→      {/* Header */}
    21→
    22→

    23→          <Link href="/dashboard" className="text-xl font-bold text-blue-600">
    24→            RateRight
    25→          </Link>
    26→          {user.email}
    27→

    28→
    29→
    30→      {/* Main Content */}
    31→
    32→        {children}
    33→
    34→
    35→      {/* Bottom Navigation */}
    36→
    37→

    38→          <Link href="/dashboard" className="flex flex-col items-center p-2 text-gray-600 hover:text-blue-600">
    39→            <Home className="w-6 h-6" />
    40→            Home
    41→          </Link>
    42→          <Link href="/contractor/post-job" className="flex flex-col items-center p-2 text-gray-600 hover:text-blue-600">
    43→            <Briefcase className="w-6 h-6" />
    44→            Jobs
    45→          </Link>
    46→          <Link href="/messages" className="flex flex-col items-center p-2 text-gray-600 hover:text-blue-600">
    47→            <MessageSquare className="w-6 h-6" />
    48→            Messages
    49→          </Link>
    50→          <Link href="/worker/profile/build" className="flex flex-col items-center p-2 text-gray-600 hover:text-blue-600">
    51→            <User className="w-6 h-6" />
    52→            Profile
    53→          </Link>
    54→

    55→
    56→

    57→  )
    58→}
    59→

<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 { redirect } from "next/navigation";
     2→import Link from "next/link";
     3→import { createClient } from "@/lib/supabase/server";
     4→import { Button } from "@/components/ui/button";
     5→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
     6→import { Badge } from "@/components/ui/badge";
     7→import { Building2, HardHat, Plus, Briefcase, Users } from "lucide-react";
     8→import { SignOutButton } from "@/components/sign-out-button";
     9→import { BottomNavWrapper } from "@/components/bottom-nav-wrapper";
    10→
    11→export default async function DashboardPage() {
    12→  const supabase = await createClient();
    13→
    14→  const {
    15→    data: { user },
    16→  } = await supabase.auth.getUser();
    17→
    18→  if (!user) {
    19→    redirect("/auth/login");
    20→  }
    21→
    22→  const { data: profile } = await supabase
    23→    .from("profiles")
    24→    .select("*")
    25→    .eq("id", user.id)
    26→    .single();
    27→
    28→  const isContractor = profile?.type === "contractor";
    29→
    30→  // Fetch stats
    31→  let jobCount = 0;
    32→  let matchCount = 0;
    33→
    34→  if (isContractor) {
    35→    const { data: company } = await supabase
    36→      .from("companies")
    37→      .select("id")
    38→      .eq("profile_id", user.id)
    39→      .single();
    40→
    41→    if (company) {
    42→      const { count } = await supabase
    43→        .from("jobs")
    44→        .select("*", { count: "exact", head: true })
    45→        .eq("company_id", company.id)
    46→        .eq("status", "active");
    47→      jobCount = count || 0;
    48→
    49→      const { count: mCount } = await supabase
    50→        .from("matches")
    51→        .select("*, jobs!inner(company_id)", { count: "exact", head: true })
    52→        .eq("jobs.company_id", company.id)
    53→        .in("status", ["applied", "suggested"]);
    54→      matchCount = mCount || 0;
    55→    }
    56→  }
    57→
    58→  return (
    59→

    60→
    61→

    62→          G'day{profile?.name ? `, ${profile.name}` : ""}!
    63→

    64→

    65→          {isContractor
    66→            ? "Ready to find your next worker?"
    67→            : "Let's get you on a job."}
    68→

    69→
    70→

    71→          {isContractor ? (
    72→            <>
    73→              {/* Quick stats */}
    74→

    75→                <Card>
    76→                  <CardContent className="flex flex-col items-center p-4">
    77→                    {jobCount}
    78→                    Active Jobs
    79→                  </CardContent>
    80→                </Card>
    81→                <Card>
    82→                  <CardContent className="flex flex-col items-center p-4">
    83→                    {matchCount}
    84→                    New Matches
    85→                  </CardContent>
    86→                </Card>
    87→

    88→
    89→              {/* Post a Job CTA */}
    90→              <Card className="border-primary/30 bg-primary/5">
    91→                <CardHeader className="pb-3">
    92→                  <CardTitle className="flex items-center gap-2 text-lg">
    93→                    <Plus className="h-6 w-6" />
    94→                    Post a Job
    95→                  </CardTitle>
    96→                  <CardDescription className="text-base">
    97→                    AI writes the post. You just tap.
    98→                  </CardDescription>
    99→                </CardHeader>
   100→                <CardContent>
   101→                  <Button asChild className="w-full h-16 text-lg font-semibold">
   102→                    <Link href="/contractor/post-job">Post a Job</Link>
   103→                  </Button>
   104→                </CardContent>
   105→              </Card>
   106→
   107→              {/* View Matches */}
   108→              {matchCount > 0 && (
   109→                <Card>
   110→                  <CardHeader className="pb-3">
   111→                    <CardTitle className="flex items-center gap-2 text-lg">
   112→                      <Users className="h-6 w-6" />
   113→                      {matchCount} Worker{matchCount > 1 ? "s" : ""} Matched
   114→                    </CardTitle>
   115→                    <CardDescription className="text-base">
   116→                      Workers have applied to your jobs
   117→                    </CardDescription>
   118→                  </CardHeader>
   119→                  <CardContent>
   120→                    <Button asChild variant="outline" className="w-full h-14 text-base font-semibold">
   121→                      <Link href="/contractor/matches">View Matches</Link>
   122→                    </Button>
   123→                  </CardContent>
   124→                </Card>
   125→              )}
   126→
   127→              {/* Company profile */}
   128→              <Card>
   129→                <CardHeader className="pb-3">
   130→                  <CardTitle className="flex items-center gap-2 text-lg">
   131→                    <Building2 className="h-6 w-6" />
   132→                    Company Profile
   133→                  </CardTitle>
   134→                  <CardDescription className="text-base">
   135→                    Manage your company details
   136→                  </CardDescription>
   137→                </CardHeader>
   138→                <CardContent>
   139→                  <Button variant="outline" className="w-full h-14 text-base">
   140→                    Edit Profile
   141→                  </Button>
   142→                </CardContent>
   143→              </Card>
   144→            </>
   145→          ) : (
   146→            <>
   147→              {/* Browse Jobs CTA */}
   148→              <Card className="border-primary/30 bg-primary/5">
   149→                <CardHeader className="pb-3">
   150→                  <CardTitle className="flex items-center gap-2 text-lg">
   151→                    <Briefcase className="h-6 w-6" />
   152→                    Find Work
   153→                  </CardTitle>
   154→                  <CardDescription className="text-base">
   155→                    Browse available construction jobs near you
   156→                  </CardDescription>
   157→                </CardHeader>
   158→                <CardContent>
   159→                  <Button asChild className="w-full h-16 text-lg font-semibold">
   160→                    <Link href="/worker/jobs">Browse Jobs</Link>
   161→                  </Button>
   162→                </CardContent>
   163→              </Card>
   164→
   165→              {/* Profile */}
   166→              <Card>
   167→                <CardHeader className="pb-3">
   168→                  <CardTitle className="flex items-center gap-2 text-lg">
   169→                    <HardHat className="h-6 w-6" />
   170→                    Your Profile
   171→                  </CardTitle>
   172→                  <CardDescription className="text-base">
   173→                    A strong profile gets you more matches
   174→                  </CardDescription>
   175→                </CardHeader>
   176→                <CardContent>
   177→                  <Button variant="outline" className="w-full h-14 text-base">
   178→                    Edit Profile
   179→                  </Button>
   180→                </CardContent>
   181→              </Card>
   182→            </>
   183→          )}
   184→

   185→
   186→
   187→      <BottomNavWrapper userType={profile?.type || "worker"} />
   188→

   189→  );
   190→}
   191→

<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>