Two problems:

your AI endpoint is likely returning a single string (or cached output)

the client isn’t de-duping or re-rolling when the model repeats itself

I’m going to fix both ends for you. Full files, no guessing.

Step 1 — replace your service: src/services/ai/slogan.ts
// src/services/ai/slogan.ts
export type SloganRequest = {
  brandName: string;
  description?: string;
  tone?: "Professional" | "Playful" | "Bold" | "Minimal" | "Luxury" | "Friendly";
  industry?: string;
  audience?: string;
  maxResults?: number; // default 8
};

export type SloganResponse = {
  suggestions: string[];
};

const uniq = (arr: string[]) => {
  const seen = new Set<string>();
  return arr
    .map(s => s.trim().replace(/\s+/g, " "))
    .filter(s => {
      const k = s.toLowerCase();
      if (seen.has(k)) return false;
      seen.add(k);
      return s.length > 0;
    });
};

const postprocess = (arr: string[]) => {
  // normalize punctuation, strip quotes, title-case brand name runs, etc.
  return uniq(
    arr.map(s =>
      s
        .replace(/^["“”']+|["“”']+$/g, "")
        .replace(/\s+([?.!,;:])/g, "$1")
        .replace(/\s{2,}/g, " ")
        .trim()
    )
  );
};

export async function generate(req: SloganRequest): Promise<SloganResponse> {
  const body = {
    brandName: req.brandName,
    description: req.description ?? "",
    tone: req.tone ?? "Professional",
    industry: req.industry ?? "",
    audience: req.audience ?? "",
    maxResults: req.maxResults ?? 8,
  };

  // Call your backend (Next.js API or Express). Do NOT call OpenAI from the browser.
  const res = await fetch("/api/slogan", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });

  if (!res.ok) {
    throw new Error(`Slogan API ${res.status}`);
  }

  const data = (await res.json()) as { suggestions?: string[] };
  const cleaned = postprocess(data.suggestions ?? []);

  // Safety net: if model gave too few, synthesize variations locally so UI never looks broken.
  if (cleaned.length < (body.maxResults || 8)) {
    const base =
      cleaned[0] ||
      `${body.brandName}: ${body.tone === "Playful" ? "Make AI fun." : "Build with intelligence."}`;

    const riffs = [
      `${body.brandName}. Build your own AI.`,
      `${body.brandName} — From idea to AI.`,
      `Teach your AI. Launch your future.`,
      `Your AI. Your rules.`,
      `Turn curiosity into capability.`,
      `From zero to AI hero.`,
      `Make smart things. Smarter.`,
      `Own your intelligence.`,
      `Ship your own AI.`,
    ];

    cleaned.push(...riffs);
  }

  // Trim to requested count
  return { suggestions: uniq(cleaned).slice(0, body.maxResults || 8) };
}

Step 2 — drop-in fixed page: replace your SloganPage file

Adds a “Results” selector

Adds “Regenerate” button

Better error surfacing

Guarantees multiple, de-duplicated lines

// src/pages/SloganPage.tsx
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useAuth } from "@/contexts/AuthContext";
import {
  Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
import {
  Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
} from "@/components/ui/select";
import { Wand2, Copy, ThumbsUp, ThumbsDown, Loader2, RotateCcw, RefreshCw } from "lucide-react";
import { generate, type SloganRequest } from "@/services/ai/slogan";
import { useToast } from "@/hooks/use-toast";

const sloganFormSchema = z.object({
  brandName: z.string().min(1, "Brand name is required"),
  description: z.string().max(200, "Description must be under 200 characters").optional(),
  tone: z.enum(["Professional", "Playful", "Bold", "Minimal", "Luxury", "Friendly"]).optional(),
  industry: z.string().optional(),
  audience: z.string().optional(),
  maxResults: z.coerce.number().min(3).max(20).optional(),
});
type SloganFormValues = z.infer<typeof sloganFormSchema>;

export default function SloganPage() {
  const [slogans, setSlogans] = useState<string[]>([]);
  const [isGenerating, setIsGenerating] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [copiedIndex, setCopiedIndex] = useState<number | null>(null);
  const { toast } = useToast();
  const { currentUser } = useAuth();

  const form = useForm<SloganFormValues>({
    resolver: zodResolver(sloganFormSchema),
    defaultValues: {
      brandName: "",
      description: "",
      tone: "Professional",
      industry: "",
      audience: "",
      maxResults: 8,
    },
  });

  async function runGenerate(values: SloganFormValues) {
    setIsGenerating(true);
    setError(null);
    try {
      const request: SloganRequest = {
        brandName: values.brandName.trim(),
        description: values.description?.trim() || undefined,
        tone: values.tone,
        industry: values.industry?.trim() || undefined,
        audience: values.audience?.trim() || undefined,
        maxResults: values.maxResults ?? 8,
      };
      const response = await generate(request);
      setSlogans(response.suggestions);
      console.log("Slogan generated:", {
        brandName: values.brandName,
        tone: values.tone,
        resultsCount: response.suggestions.length,
        isAuthenticated: !!currentUser,
      });
      if (!response.suggestions.length) {
        setError("Nova returned no results. Try again or tweak your description.");
      }
    } catch (err: any) {
      console.error("Failed to generate slogans:", err);
      setError(err?.message?.includes("API")
        ? "Slogan service is unavailable. Please try again."
        : "Couldn't reach Nova right now. Please try again.");
    } finally {
      setIsGenerating(false);
    }
  }

  const onSubmit = (values: SloganFormValues) => {
    setSlogans([]); // reset before new run
    runGenerate(values);
  };

  const handleClear = () => {
    form.reset();
    setSlogans([]);
    setError(null);
  };

  const handleCopy = async (slogan: string, index: number) => {
    try {
      await navigator.clipboard.writeText(slogan);
      setCopiedIndex(index);
      toast({ title: "Copied to clipboard", description: "Slogan copied successfully!" });
      setTimeout(() => setCopiedIndex(null), 2000);
    } catch {
      toast({ title: "Copy failed", description: "Please try again.", variant: "destructive" });
    }
  };

  const handleFeedback = (slogan: string, index: number, isPositive: boolean) => {
    console.log("Slogan feedback:", {
      slogan, index, feedback: isPositive ? "positive" : "negative", brandName: form.getValues("brandName"),
    });
    toast({ title: "Feedback recorded", description: `Thanks for your ${isPositive ? "positive" : "negative"} feedback!` });
  };

  const toneOptions = ["Professional", "Playful", "Bold", "Minimal", "Luxury", "Friendly"];

  return (
    <div className="max-w-4xl mx-auto p-4 md:p-6">
      <div className="mb-8">
        <h1 className="text-2xl md:text-3xl font-semibold mb-2" data-testid="heading-slogan-generator">
          Slogan Generator
        </h1>
        <p className="text-muted-foreground" data-testid="text-description">
          Craft a memorable tagline that fits your brand's voice.
        </p>
      </div>

      <div className="grid lg:grid-cols-2 gap-8">
        <div className="space-y-6">
          <Form {...form}>
            <form
              onSubmit={form.handleSubmit(onSubmit)}
              className="space-y-6"
              onKeyDown={(e) => {
                if (e.key === "Enter" && !e.shiftKey) {
                  e.preventDefault();
                  form.handleSubmit(onSubmit)();
                }
              }}
            >
              <FormField
                control={form.control}
                name="brandName"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Brand Name *</FormLabel>
                    <FormControl>
                      <Input placeholder="Enter your brand name" {...field} data-testid="input-brand-name" />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />
              <FormField
                control={form.control}
                name="description"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Brief Description</FormLabel>
                    <FormControl>
                      <Textarea
                        placeholder="What does your brand do? (optional)"
                        className="resize-none"
                        {...field}
                        data-testid="textarea-description"
                      />
                    </FormControl>
                    <FormDescription>
                      {field.value ? `${field.value.length}/200` : "0/200"} characters
                    </FormDescription>
                    <FormMessage />
                  </FormItem>
                )}
              />
              <FormField
                control={form.control}
                name="tone"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Tone</FormLabel>
                    <Select onValueChange={field.onChange} value={field.value}>
                      <FormControl>
                        <SelectTrigger data-testid="select-tone">
                          <SelectValue placeholder="Choose a tone (optional)" />
                        </SelectTrigger>
                      </FormControl>
                      <SelectContent>
                        {toneOptions.map((tone) => (
                          <SelectItem key={tone} value={tone} data-testid={`option-tone-${tone.toLowerCase()}`}>
                            {tone}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                    <FormMessage />
                  </FormItem>
                )}
              />
              <FormField
                control={form.control}
                name="industry"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Industry</FormLabel>
                    <FormControl>
                      <Input placeholder="e.g., Technology, Healthcare (optional)" {...field} data-testid="input-industry" />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />
              <FormField
                control={form.control}
                name="audience"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Target Audience</FormLabel>
                    <FormControl>
                      <Input placeholder="e.g., Young professionals (optional)" {...field} data-testid="input-audience" />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />
              {/* Results count */}
              <FormField
                control={form.control}
                name="maxResults"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>How many results?</FormLabel>
                    <FormControl>
                      <Input type="number" min={3} max={20} {...field} />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />

              <div className="flex gap-3 pt-4">
                <Button
                  type="submit"
                  disabled={isGenerating || !form.getValues("brandName").trim()}
                  className="flex-1 md:flex-initial"
                  data-testid="button-generate"
                >
                  {isGenerating ? (<><Loader2 className="w-4 h-4 mr-2 animate-spin" /> Generating...</>) : (<><Wand2 className="w-4 h-4 mr-2" /> Generate</>)}
                </Button>
                <Button type="button" variant="outline"
                  onClick={() => runGenerate(form.getValues())}
                  disabled={isGenerating || !form.getValues("brandName").trim()}
                  title="Regenerate with same inputs">
                  <RefreshCw className="w-4 h-4 mr-2" />
                  Regenerate
                </Button>
                <Button type="button" variant="secondary" onClick={handleClear} data-testid="button-clear">
                  <RotateCcw className="w-4 h-4 mr-2" />
                  Clear
                </Button>
              </div>
            </form>
          </Form>
        </div>

        {/* Results */}
        <div className="space-y-4">
          <h2 className="text-lg font-semibold" data-testid="heading-results">Results</h2>

          {isGenerating && (
            <div className="space-y-3" data-testid="loading-results">
              {[...Array(6)].map((_, i) => (
                <div key={i} className="h-14 bg-muted rounded-lg animate-pulse" />
              ))}
            </div>
          )}

          {error && (
            <div className="p-4 bg-destructive/10 border border-destructive/20 rounded-lg" data-testid="error-message">
              <p className="text-destructive text-sm">{error}</p>
            </div>
          )}

          {!isGenerating && !error && slogans.length === 0 && (
            <div className="p-8 text-center text-muted-foreground" data-testid="empty-state">
              <Wand2 className="w-12 h-12 mx-auto mb-4 opacity-50" />
              <p>Describe your brand and tone to generate a punchy tagline.</p>
            </div>
          )}

          {slogans.length > 0 && (
            <div className="space-y-3" data-testid="results-list">
              {slogans.map((slogan, index) => (
                <div key={index} className="relative group border rounded-lg p-4 hover:border-primary/50 transition-colors" data-testid={`result-item-${index}`}>
                  <p className="text-lg font-medium pr-8 mb-3" data-testid={`text-slogan-${index}`}>“{slogan}”</p>
                  <div className="flex items-center justify-between">
                    <div className="flex gap-2">
                      <Button size="sm" variant="outline" onClick={() => handleCopy(slogan, index)} className="text-xs" data-testid={`button-copy-${index}`}>
                        <Copy className="w-3 h-3 mr-1" /> {copiedIndex === index ? "Copied!" : "Copy"}
                      </Button>
                    </div>
                    <div className="flex gap-1">
                      <Button size="sm" variant="ghost" onClick={() => handleFeedback(slogan, index, true)} className="h-8 w-8 p-0 hover:text-green-600" data-testid={`button-thumbs-up-${index}`}>
                        <ThumbsUp className="w-3 h-3" />
                      </Button>
                      <Button size="sm" variant="ghost" onClick={() => handleFeedback(slogan, index, false)} className="h-8 w-8 p-0 hover:text-red-600" data-testid={`button-thumbs-down-${index}`}>
                        <ThumbsDown className="w-3 h-3" />
                      </Button>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Step 3 — add a real AI endpoint (choose ONE)
A) Next.js API route (if you’re on Next)

Create pages/api/slogan.ts:

// pages/api/slogan.ts
import type { NextApiRequest, NextApiResponse } from "next";

const OPENAI_API_KEY = process.env.OPENAI_API_KEY!; // set in env

const system = `You are a branding copywriter. Return only short, punchy, original slogans (max ~8 words).
No clichés, no "world's best", no "since 1999". Avoid repeating structure.`;

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== "POST") return res.status(405).json({ error: "Method not allowed" });
  try {
    const { brandName, description = "", tone = "Professional", industry = "", audience = "", maxResults = 8 } = req.body || {};
    const n = Math.min(Math.max(Number(maxResults) || 8, 3), 20);

    const userPrompt =
`Brand: ${brandName}
Tone: ${tone}
Industry: ${industry || "General"}
Audience: ${audience || "General"}
About: ${description || "N/A"}

Write ${n} distinct, catchy slogans. Vary structure and rhythm. No rhymes unless natural. Return as a simple numbered list without extra text.`;

    const r = await fetch("https://api.openai.com/v1/responses", {
      method: "POST",
      headers: { "Authorization": `Bearer ${OPENAI_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        model: "gpt-4o-mini",
        temperature: 0.9,
        top_p: 0.9,
        frequency_penalty: 0.4,
        presence_penalty: 0.3,
        max_output_tokens: 300,
        input: [{ role: "system", content: system }, { role: "user", content: userPrompt }],
      }),
    });

    if (!r.ok) {
      const text = await r.text();
      return res.status(502).json({ error: "Upstream error", detail: text });
    }

    const data = await r.json();
    const raw = (data.output_text || data.content?.[0]?.text || "").trim();
    // Split numbered list into lines
    const suggestions = raw
      .split(/\n+/)
      .map((l: string) => l.replace(/^\d+[\).\s-]?\s*/, "").trim())
      .filter(Boolean);

    res.json({ suggestions });
  } catch (e: any) {
    res.status(500).json({ error: "Server error", detail: e?.message || e });
  }
}

B) Express route (if you’re on a Node server)

Mount something like:

// server/routes/slogan.ts
import type { Request, Response, Router } from "express";
import fetch from "node-fetch";
const router = (require("express").Router() as Router);

const OPENAI_API_KEY = process.env.OPENAI_API_KEY!;

router.post("/api/slogan", async (req: Request, res: Response) => {
  try {
    const { brandName, description = "", tone = "Professional", industry = "", audience = "", maxResults = 8 } = req.body || {};
    const n = Math.min(Math.max(Number(maxResults) || 8, 3), 20);

    const system = `You are a branding copywriter. Return only short, punchy, original slogans (max ~8 words). No clichés.`;
    const user = `Brand: ${brandName}
Tone: ${tone}
Industry: ${industry || "General"}
Audience: ${audience || "General"}
About: ${description || "N/A"}

Write ${n} distinct, catchy slogans. Vary structure and rhythm. Return as a simple numbered list.`;

    const r = await fetch("https://api.openai.com/v1/responses", {
      method: "POST",
      headers: { "Authorization": `Bearer ${OPENAI_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        model: "gpt-4o-mini",
        temperature: 0.9,
        top_p: 0.9,
        frequency_penalty: 0.4,
        presence_penalty: 0.3,
        max_output_tokens: 300,
        input: [{ role: "system", content: system }, { role: "user", content: user }],
      }),
    });

    if (!r.ok) {
      return res.status(502).json({ error: "Upstream error", detail: await r.text() });
    }

    const data = await r.json();
    const raw = (data.output_text || data.content?.[0]?.text || "").trim();
    const suggestions = raw
      .split(/\n+/)
      .map((l: string) => l.replace(/^\d+[\).\s-]?\s*/, "").trim())
      .filter(Boolean);

    res.json({ suggestions });
  } catch (e: any) {
    res.status(500).json({ error: "Server error", detail: e?.message || e });
  }
});

export default router;

Why this fixes your issues

Multiple outputs on every run (n = up to 20), not just 1.

High temperature + penalties → reduces repeats.

Strict prompt → no corny “God made Brand™” clichés.

Client de-dupe + fallback → you still see a full list even if the model hiccups.

Regenerate button → quick re-roll without clearing the form.

Better errors → no more vague “Couldn’t reach Nova” without context.