• MangaSearchPanel React component for searching and selecting AniList manga matches for a given Kenmei manga, with manual search and result selection features.

    Parameters

    Returns Element

    The rendered manga search panel React element.

    export function MangaSearchPanel({
    kenmeiManga,
    onClose,
    onSelectMatch,
    token,
    bypassCache,
    }: Readonly<MangaSearchPanelProps>) {
    const [searchQuery, setSearchQuery] = useState("");
    const [isSearching, setIsSearching] = useState(false);
    const [searchResults, setSearchResults] = useState<MangaMatch[]>([]);
    const [error, setError] = useState<string | null>(null);
    const [selectedIndex, setSelectedIndex] = useState(-1);
    const [page, setPage] = useState(1);
    const [hasNextPage, setHasNextPage] = useState(false);
    const [blurAdultContent, setBlurAdultContent] = useState(true);
    const [unblurredImages, setUnblurredImages] = useState<Set<string>>(
    new Set(),
    );

    const searchInputRef = useRef<HTMLInputElement>(null);
    const resultsContainerRef = useRef<HTMLDivElement>(null);

    // Load blur settings from match config
    useEffect(() => {
    const loadBlurSettings = async () => {
    const matchConfig = getMatchConfig();
    setBlurAdultContent(matchConfig.blurAdultContent);
    };
    loadBlurSettings();
    }, []);

    // Helper functions for adult content handling
    const isAdultContent = (manga: AniListManga) => {
    return manga?.isAdult === true;
    };

    const shouldBlurImage = (mangaId: string) => {
    return blurAdultContent && !unblurredImages.has(mangaId);
    };

    const toggleImageBlur = (mangaId: string) => {
    setUnblurredImages((prev) => {
    const newSet = new Set(prev);
    if (newSet.has(mangaId)) {
    newSet.delete(mangaId);
    } else {
    newSet.add(mangaId);
    }
    return newSet;
    });
    };

    // Style modifications to make everything larger
    const headerClasses = "text-2xl font-semibold tracking-tight"; // Elevated typography for hero header
    const titleClasses = "text-3xl font-semibold"; // Increased from text-lg for stronger emphasis

    useEffect(() => {
    // Focus the search input
    if (searchInputRef.current) {
    searchInputRef.current.focus();
    }
    }, [kenmeiManga?.id, kenmeiManga?.title]);

    useEffect(() => {
    setSelectedIndex(-1);
    }, [searchResults]);

    useEffect(() => {
    if (selectedIndex >= 0 && resultsContainerRef.current) {
    const selectedElement = resultsContainerRef.current.querySelector(
    `[data-index="${selectedIndex}"]`,
    ) as HTMLElement;
    if (selectedElement) {
    selectedElement.scrollIntoView({
    behavior: "smooth",
    block: "nearest",
    });
    }
    }
    }, [selectedIndex]);

    // Check if query is a valid AniList ID
    const isValidAniListId = (query: string) => {
    const isNumericId = /^\d+$/.test(query.trim());
    const mangaId = isNumericId ? Number.parseInt(query.trim(), 10) : null;
    return mangaId && mangaId > 0 && mangaId < 10000000 ? mangaId : null;
    };

    // Handle ID-based search
    const handleIdSearch = async (
    mangaId: number,
    pageNum: number,
    startTime: number,
    ) => {
    console.log(`🔎 Detected AniList ID search: ${mangaId}`);

    const idResults = await getMangaByIds([mangaId], token);

    if (idResults.length > 0) {
    console.log(`🔎 Found manga by ID ${mangaId}:`, idResults[0]);

    const idMatches: MangaMatch[] = idResults.map((manga) => ({
    manga,
    confidence: 100,
    }));

    updateSearchResults(idMatches, pageNum, {
    hasNextPage: false,
    currentPage: 1,
    });
    } else {
    console.log(`⚠️ No manga found for ID ${mangaId}`);
    if (pageNum === 1) {
    setSearchResults([]);
    }
    setHasNextPage(false);
    }

    const endTime = performance.now();
    console.log(
    `🔎 Search completed in ${(endTime - startTime).toFixed(2)}ms for ID ${mangaId}`,
    );
    };

    // Handle title-based search
    const handleTitleSearch = async (
    query: string,
    pageNum: number,
    startTime: number,
    ) => {
    console.log(`🔎 Performing title search for: "${query}"`);

    const searchConfig = {
    bypassCache: !!bypassCache,
    maxSearchResults: 30,
    searchPerPage: 50,
    exactMatchingOnly: false,
    };

    console.log(`🔎 Search config:`, searchConfig);

    const searchResponse = await searchMangaByTitle(
    query,
    token,
    searchConfig,
    undefined,
    pageNum,
    );

    const results = searchResponse.matches;
    const pageInfo = searchResponse.pageInfo;
    const endTime = performance.now();

    console.log(
    `🔎 Search completed in ${(endTime - startTime).toFixed(2)}ms for "${query}"`,
    );
    console.log(`🔎 Search returned ${results.length} results for "${query}"`);

    logSearchResults(results, query);
    updateSearchResults(results, pageNum, pageInfo);
    };

    // Log search results for debugging
    const logSearchResults = (results: MangaMatch[], query: string) => {
    if (results.length > 0) {
    console.log(
    `🔎 Titles received:`,
    results.map((m) => ({
    title: m.manga.title?.romaji || m.manga.title?.english || "unknown",
    confidence: m.confidence.toFixed(1),
    id: m.manga.id,
    })),
    );
    } else {
    console.log(
    `⚠️ No results found for "${query}" - this could indicate a cache or display issue`,
    );
    }
    };

    // Update search results with proper pagination handling
    const updateSearchResults = (
    results: MangaMatch[],
    pageNum: number,
    pageInfo?: { hasNextPage: boolean; currentPage: number },
    ) => {
    if (pageNum === 1) {
    console.log(`🔎 Resetting search results`);
    setSearchResults(results);
    } else {
    console.log(
    `🔎 Appending ${results.length} results to existing ${searchResults.length} results`,
    );
    setSearchResults((prev) => {
    const existingIds = new Set(prev.map((match) => match.manga.id));
    const newUniqueResults = results.filter(
    (match) => !existingIds.has(match.manga.id),
    );

    console.log(
    `🔎 Adding ${newUniqueResults.length} unique results (filtered ${results.length - newUniqueResults.length} duplicates)`,
    );

    return [...prev, ...newUniqueResults];
    });
    }

    if (pageInfo) {
    console.log(`🔎 Using API pagination info:`, pageInfo);
    setHasNextPage(pageInfo.hasNextPage);
    setPage(pageInfo.currentPage);
    } else {
    console.log(`🔎 No pagination info available, using fallback logic`);
    setHasNextPage(false);
    setPage(pageNum);
    }

    console.log(
    `🔎 UI state updated: searchResults.length=${results.length}, hasNextPage=${pageInfo?.hasNextPage || false}, page=${pageInfo?.currentPage || pageNum}`,
    );
    };

    const handleSearch = async (query: string, pageNum: number = 1) => {
    if (!query.trim()) return;

    setIsSearching(true);
    setError(null);

    try {
    console.log(
    `🔎 Starting search for: "${query}" with bypassCache=${!!bypassCache}, page=${pageNum}`,
    );
    const startTime = performance.now();

    // Check if query is a valid AniList ID
    const mangaId = isValidAniListId(query);

    if (mangaId) {
    await handleIdSearch(mangaId, pageNum, startTime);
    } else {
    await handleTitleSearch(query, pageNum, startTime);
    }

    const endTime = performance.now();
    console.log(
    `🔎 Search completed in ${(endTime - startTime).toFixed(2)}ms for "${query}"`,
    );
    } catch (error) {
    console.error("Error searching manga:", error);
    setError("Failed to search for manga. Please try again.");
    if (pageNum === 1) {
    setSearchResults([]);
    console.log(`⚠️ Search error - cleared results`);
    }
    } finally {
    setIsSearching(false);
    console.log(`🔎 Search complete, isSearching set to false`);
    }
    };

    const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    setPage(1);
    handleSearch(searchQuery);
    };

    const loadMoreResults = () => {
    handleSearch(searchQuery, page + 1);
    };

    const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === "Escape") {
    onClose();
    return;
    }

    if (e.target === searchInputRef.current) {
    if (e.key === "Enter") {
    e.preventDefault();
    handleSubmit(e);
    } else if (e.key === "ArrowDown" && searchResults.length > 0) {
    e.preventDefault();
    setSelectedIndex(0);
    }
    return;
    }

    if (searchResults.length > 0) {
    if (e.key === "ArrowDown") {
    e.preventDefault();
    setSelectedIndex((prev) =>
    Math.min(prev + 1, searchResults.length - 1),
    );
    } else if (e.key === "ArrowUp") {
    e.preventDefault();
    setSelectedIndex((prev) => {
    const newIndex = prev - 1;
    if (newIndex < 0) {
    searchInputRef.current?.focus();
    return -1;
    }
    return newIndex;
    });
    } else if (e.key === "Enter" && selectedIndex >= 0) {
    e.preventDefault();
    onSelectMatch(searchResults[selectedIndex].manga);
    }
    }
    };

    const handleSelectResult = (match: MangaMatch, index: number) => {
    setSelectedIndex(index);
    onSelectMatch(match.manga); // Pass the manga object to the parent
    };

    /**
    * Displays a panel for searching and selecting AniList manga matches for a given Kenmei manga, supporting manual search and result selection.
    *
    * @param props - The props for the MangaSearchPanel component.
    * @returns The rendered manga search panel React element.
    * @source
    * @example
    * ```tsx
    * <MangaSearchPanel
    * kenmeiManga={manga}
    * onClose={handleClose}
    * onSelectMatch={handleSelect}
    * token={token}
    * bypassCache={false}
    * />
    * ```
    */
    return (
    <div
    className="relative flex h-full flex-col overflow-hidden rounded-3xl border border-white/30 bg-gradient-to-br from-white/95 via-white/90 to-white/80 shadow-[0_30px_70px_-40px_rgba(15,23,42,0.55)] backdrop-blur-xl dark:border-slate-700/70 dark:from-slate-900/95 dark:via-slate-950/90 dark:to-slate-950/80"
    aria-modal="true"
    aria-labelledby="search-title"
    tabIndex={-1}
    >
    <div className="flex items-center justify-between border-b border-slate-200/70 bg-white/80 px-6 py-4 backdrop-blur dark:border-slate-800 dark:bg-slate-900/70">
    <div className="flex items-center gap-3">
    <button
    onClick={onClose}
    className="rounded-full border border-slate-200/70 bg-white/90 p-2 text-slate-500 transition hover:text-slate-700 focus-visible:ring-2 focus-visible:ring-blue-300 focus-visible:outline-none dark:border-slate-700 dark:bg-slate-900/80 dark:text-slate-300 dark:hover:text-white dark:focus-visible:ring-blue-500"
    aria-label="Go back"
    >
    <ArrowLeft size={22} aria-hidden="true" />
    </button>
    <h2
    id="search-title"
    className={`${headerClasses} text-slate-900 dark:text-white`}
    >
    AniList search
    </h2>
    </div>
    <button
    onClick={onClose}
    className="rounded-full border border-slate-200/70 bg-white/90 p-2 text-slate-500 transition hover:text-slate-700 focus-visible:ring-2 focus-visible:ring-blue-300 focus-visible:outline-none dark:border-slate-700 dark:bg-slate-900/80 dark:text-slate-300 dark:hover:text-white dark:focus-visible:ring-blue-500"
    aria-label="Close search panel"
    >
    <X size={22} aria-hidden="true" />
    </button>
    </div>

    {kenmeiManga && (
    <div className="mx-6 mt-6 rounded-2xl border border-white/30 bg-gradient-to-br from-blue-50/80 via-white/75 to-purple-50/70 p-6 shadow-[inset_0_0_1px_rgba(59,130,246,0.25)] dark:border-slate-700/60 dark:from-blue-900/40 dark:via-slate-900/60 dark:to-indigo-950/60">
    <h3 className="text-xs font-semibold tracking-[0.2em] text-blue-800/80 uppercase dark:text-blue-200/80">
    Matching for
    </h3>
    <p
    className={`${titleClasses} mt-2 text-blue-900 dark:text-blue-200`}
    >
    {kenmeiManga.title}
    </p>
    <div className="mt-4 flex flex-wrap gap-2 text-sm">
    <span className="inline-flex items-center gap-1 rounded-full bg-white/70 px-3 py-1 font-medium text-blue-800 shadow-sm ring-1 ring-blue-500/20 dark:bg-slate-900/60 dark:text-blue-200 dark:ring-blue-500/30">
    <span
    className="h-2 w-2 rounded-full bg-blue-500"
    aria-hidden="true"
    />
    {kenmeiManga.status}
    </span>
    <span className="inline-flex items-center gap-1 rounded-full bg-white/70 px-3 py-1 font-medium text-blue-800 shadow-sm ring-1 ring-blue-500/20 dark:bg-slate-900/60 dark:text-blue-200 dark:ring-blue-500/30">
    <span
    className="h-2 w-2 rounded-full bg-indigo-500"
    aria-hidden="true"
    />
    {kenmeiManga.chapters_read} chapters read
    </span>
    {kenmeiManga.score > 0 && (
    <span className="inline-flex items-center gap-1 rounded-full bg-white/70 px-3 py-1 font-medium text-blue-800 shadow-sm ring-1 ring-blue-500/20 dark:bg-slate-900/60 dark:text-blue-200 dark:ring-blue-500/30">
    <span
    className="h-2 w-2 rounded-full bg-purple-500"
    aria-hidden="true"
    />
    Score: {kenmeiManga.score}/10
    </span>
    )}
    </div>
    </div>
    )}

    <div className="border-b border-white/20 bg-white/60 p-6 shadow-inner dark:border-slate-700/70 dark:bg-slate-900/50">
    <form
    onSubmit={handleSubmit}
    className="flex flex-col gap-4 lg:flex-row lg:items-center"
    >
    <div className="relative flex-1 rounded-2xl bg-white/80 shadow-sm ring-1 ring-slate-200/70 transition focus-within:ring-2 focus-within:ring-blue-500 dark:bg-slate-900/80 dark:ring-slate-700/70">
    <div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-5">
    <Search className="h-6 w-6 text-slate-400" aria-hidden="true" />
    </div>
    <input
    ref={searchInputRef}
    type="text"
    className="block w-full rounded-2xl border-0 bg-transparent p-4 pl-14 text-base text-slate-900 placeholder:text-slate-400 focus:outline-none focus-visible:ring-0 dark:text-slate-100"
    placeholder="Search by manga title or AniList ID..."
    value={searchQuery}
    onChange={(e) => setSearchQuery(e.target.value)}
    disabled={isSearching}
    aria-label="Search manga title or AniList ID"
    autoComplete="off"
    onKeyDown={handleKeyDown}
    />
    <div className="pointer-events-none absolute inset-y-0 right-3 flex items-center">
    <span className="hidden text-xs font-semibold tracking-[0.3em] text-slate-400/70 uppercase sm:block">
    Enter
    </span>
    </div>
    </div>
    <button
    type="submit"
    className="inline-flex items-center justify-center rounded-2xl bg-gradient-to-r from-blue-600 via-indigo-600 to-purple-600 px-6 py-3 text-base font-semibold text-white shadow-lg shadow-blue-600/30 transition duration-200 hover:brightness-110 focus-visible:ring-2 focus-visible:ring-purple-300 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-60 dark:shadow-indigo-500/30"
    disabled={isSearching || !searchQuery.trim()}
    aria-label={isSearching ? "Searching..." : "Search for manga"}
    >
    {isSearching ? (
    <>
    <Loader2
    className="mr-2 h-5 w-5 animate-spin"
    aria-hidden="true"
    />
    Searching...
    </>
    ) : (
    "Search"
    )}
    </button>
    </form>

    {/* Search hint */}
    <div className="mt-3 flex flex-wrap items-center gap-2 rounded-2xl bg-white/80 px-4 py-3 text-xs font-medium text-slate-600 shadow-sm ring-1 ring-slate-200/60 dark:bg-slate-900/70 dark:text-slate-300 dark:ring-slate-700/60">
    <span className="inline-flex items-center gap-1 text-blue-600 dark:text-blue-300">
    <Sparkles className="h-4 w-4" aria-hidden="true" />
    Pro tip
    </span>
    <span className="text-slate-500 dark:text-slate-300">
    Search by title (e.g., “Attack on Titan”) or jump straight to an
    AniList ID (e.g., “53390”).
    </span>
    </div>
    </div>

    <section
    ref={resultsContainerRef}
    className="flex-1 overflow-y-auto p-6"
    aria-label="Search results"
    aria-live="polite"
    >
    {error && (
    <div
    className="mb-6 rounded-2xl border border-red-200/60 bg-red-50/80 p-4 text-base text-red-700 shadow-sm backdrop-blur dark:border-red-500/20 dark:bg-red-900/30 dark:text-red-200"
    role="alert"
    >
    <p>{error}</p>
    </div>
    )}

    {isSearching && (
    <div className="mb-6 flex items-center justify-center gap-3 rounded-3xl border border-blue-200/60 bg-blue-50/70 px-4 py-5 text-base font-medium text-blue-700 shadow-sm dark:border-blue-500/30 dark:bg-blue-900/20 dark:text-blue-200">
    <Loader2 className="h-5 w-5 animate-spin" aria-hidden="true" />
    Searching...
    </div>
    )}

    {searchResults.length === 0 && !isSearching && !error && (
    <div className="mx-auto flex max-w-lg flex-col items-center justify-center rounded-3xl border border-dashed border-slate-300/70 bg-white/60 p-8 text-center text-lg text-slate-500 shadow-sm dark:border-slate-700 dark:bg-slate-900/60 dark:text-slate-300">
    <Sparkles
    className="mb-3 h-9 w-9 text-blue-500"
    aria-hidden="true"
    />
    {searchQuery.trim()
    ? "No results just yettry refining your keywords or searching by AniList ID."
    : "Start by typing a title or AniList ID to discover the best match."}
    </div>
    )}

    <div className="space-y-6">
    {searchResults.map((result, index) => {
    const manga = result.manga; // Extract manga for easier access
    const mangaId = manga.id;
    const uniqueKey = mangaId
    ? `manga-${mangaId}`
    : `manga-${index}-${manga.title?.romaji?.replaceAll(" ", "") || "unknown"}`;
    const isSelected = index === selectedIndex;

    return (
    <div
    key={uniqueKey}
    data-index={index}
    className={`group relative overflow-hidden rounded-2xl border border-white/25 bg-white/85 p-6 shadow-[0_24px_45px_-35px_rgba(59,130,246,0.45)] backdrop-blur transition-all duration-300 dark:border-slate-700/60 dark:bg-slate-900/75 ${
    isSelected
    ? "ring-2 ring-blue-400/80 ring-offset-2 ring-offset-white dark:ring-blue-400/60 dark:ring-offset-slate-950"
    : "ring-1 ring-slate-200/70 hover:-translate-y-1 hover:border-blue-300/50 hover:ring-blue-300/60 dark:ring-slate-800/70"
    }`}
    aria-pressed={isSelected}
    >
    <div
    className="pointer-events-none absolute inset-0 opacity-0 transition duration-300 group-hover:opacity-100"
    aria-hidden="true"
    style={{
    background:
    "linear-gradient(135deg, rgba(59,130,246,0.14), transparent 60%)",
    }}
    />
    <div className="relative flex w-full flex-col gap-6 lg:flex-row lg:items-start lg:gap-8">
    {(manga.coverImage?.large || manga.coverImage?.medium) && (
    <div className="relative z-[1] flex-shrink-0">
    {isAdultContent(manga) ? (
    <button
    type="button"
    tabIndex={0}
    aria-label={
    shouldBlurImage(`search-${manga.id}`)
    ? "Reveal adult content cover"
    : "Hide adult content cover"
    }
    className="focus:outline-none"
    onClick={(e) => {
    e.stopPropagation();
    toggleImageBlur(`search-${manga.id}`);
    }}
    onKeyDown={(e) => {
    if (e.key === "Enter" || e.key === " ") {
    e.preventDefault();
    toggleImageBlur(`search-${manga.id}`);
    }
    }}
    >
    <img
    src={
    manga.coverImage.large || manga.coverImage.medium
    }
    alt={`Cover for ${manga.title?.english || manga.title?.romaji || "manga"}`}
    className={`h-44 w-32 rounded-2xl border border-white/40 object-cover shadow-xl transition duration-300 hover:scale-[1.03] hover:shadow-2xl dark:border-slate-700 ${
    shouldBlurImage(`search-${manga.id}`)
    ? "cursor-pointer blur-lg"
    : ""
    }`}
    loading="lazy"
    draggable={false}
    />
    </button>
    ) : (
    <img
    src={
    manga.coverImage.large || manga.coverImage.medium
    }
    alt={`Cover for ${manga.title?.english || manga.title?.romaji || "manga"}`}
    className="h-44 w-32 rounded-2xl border border-white/40 object-cover shadow-xl transition duration-300 hover:scale-[1.03] hover:shadow-2xl dark:border-slate-700"
    loading="lazy"
    draggable={false}
    />
    )}
    {/* Adult content warning badge */}
    {isAdultContent(manga) && (
    <div className="absolute top-1 left-1">
    <span
    className="inline-flex items-center rounded-md bg-red-600 px-1 py-0 text-xs font-medium text-white"
    title="Adult Content"
    >
    18+
    </span>
    </div>
    )}
    {/* Click to reveal hint for blurred images */}
    {isAdultContent(manga) &&
    shouldBlurImage(`search-${manga.id}`) && (
    <div className="absolute inset-0 flex items-center justify-center">
    <button
    type="button"
    tabIndex={0}
    className="inline-flex cursor-pointer items-center rounded-full bg-slate-900/80 px-3 py-1 text-xs font-semibold text-white shadow-lg shadow-slate-900/40"
    onClick={(e) => {
    e.stopPropagation();
    toggleImageBlur(`search-${manga.id}`);
    }}
    onKeyDown={(e) => {
    if (e.key === "Enter" || e.key === " ") {
    e.preventDefault();
    toggleImageBlur(`search-${manga.id}`);
    }
    }}
    aria-label={
    shouldBlurImage(`search-${manga.id}`)
    ? "Reveal adult content cover"
    : "Hide adult content cover"
    }
    >
    Click to reveal
    </button>
    </div>
    )}
    </div>
    )}

    <div className="relative z-[1] flex-1 space-y-5">
    <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
    <div className="space-y-3">
    <h3 className="text-2xl font-semibold text-slate-900 transition duration-200 group-hover:text-blue-700 dark:text-white dark:group-hover:text-blue-200">
    {manga.title?.english ||
    manga.title?.romaji ||
    "Unknown Title"}
    </h3>

    {manga.title?.romaji &&
    manga.title.romaji !== manga.title.english && (
    <p className="text-base text-slate-500 dark:text-slate-400">
    {manga.title.romaji}
    </p>
    )}

    {/* Alternative source badges */}
    <div className="flex flex-wrap items-center gap-2 text-xs">
    {/* Show unified sourceInfo badge */}
    {result.sourceInfo && (
    <div className="flex items-center gap-2">
    <span
    className={`inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold shadow-sm ring-1 ring-black/5 dark:ring-white/10 ${getSourceBadgeClasses(result.sourceInfo.source)}`}
    >
    📚 Found via{" "}
    {result.sourceInfo.source === "mangadex"
    ? "MangaDex"
    : result.sourceInfo.source}
    </span>
    <span className="text-xs text-slate-400 dark:text-slate-400">
    ({result.sourceInfo.title})
    </span>
    </div>
    )}

    {/* Show individual source badges when both exist */}
    {!result.sourceInfo && (
    <>
    {result.comickSource && (
    <div className="flex items-center gap-2">
    <span className="inline-flex items-center gap-2 rounded-full bg-orange-100/90 px-3 py-1 text-xs font-semibold text-orange-800 shadow-sm ring-1 ring-orange-200/60 dark:bg-orange-900/30 dark:text-orange-300 dark:ring-orange-400/30">
    📚 Found via Comick
    </span>
    <span className="text-xs text-slate-400 dark:text-slate-400">
    ({result.comickSource.title})
    </span>
    </div>
    )}
    {result.mangaDexSource && (
    <div className="flex items-center gap-2">
    <span className="inline-flex items-center gap-2 rounded-full bg-blue-100/90 px-3 py-1 text-xs font-semibold text-blue-800 shadow-sm ring-1 ring-blue-200/60 dark:bg-blue-900/30 dark:text-blue-300 dark:ring-blue-400/30">
    📚 Found via MangaDex
    </span>
    <span className="text-xs text-slate-400 dark:text-slate-400">
    ({result.mangaDexSource.title})
    </span>
    </div>
    )}
    </>
    )}
    </div>
    </div>

    <div className="flex shrink-0 flex-col items-end gap-3">
    {typeof result.confidence === "number" && (
    <span className="inline-flex items-center gap-2 rounded-full bg-gradient-to-r from-blue-600/90 to-purple-600/90 px-3 py-1 text-xs font-semibold text-white shadow-lg shadow-blue-600/30">
    <Gauge className="h-4 w-4" aria-hidden="true" />
    {Math.round(result.confidence)}% match
    </span>
    )}
    <a
    href={`https://anilist.co/manga/${manga.id}`}
    target="_blank"
    rel="noopener noreferrer"
    className="inline-flex items-center gap-2 rounded-full border border-blue-100/80 bg-white/80 px-3 py-2 text-xs font-semibold text-blue-700 transition hover:border-blue-400 hover:text-blue-900 focus-visible:ring-2 focus-visible:ring-blue-300 focus-visible:outline-none dark:border-blue-500/30 dark:bg-slate-900/70 dark:text-blue-200 dark:hover:border-blue-400/60"
    aria-label="View on AniList"
    onClick={(e) => e.stopPropagation()}
    >
    <ExternalLink size={16} aria-hidden="true" /> View on
    AniList
    </a>
    </div>
    </div>

    <div className="flex flex-wrap gap-2 pt-2">
    {manga.format && (
    <span className="inline-flex items-center gap-2 rounded-full bg-blue-100/90 px-3 py-1 text-sm font-medium text-blue-800 shadow-sm dark:bg-blue-900/20 dark:text-blue-200">
    {manga.format.replace("_", " ")}
    </span>
    )}

    {manga.status && (
    <span className="inline-flex items-center gap-2 rounded-full bg-green-100/90 px-3 py-1 text-sm font-medium text-green-800 shadow-sm dark:bg-green-900/20 dark:text-green-200">
    {manga.status.replace("_", " ")}
    </span>
    )}

    {manga.chapters && (
    <span className="inline-flex items-center gap-2 rounded-full bg-purple-100/90 px-3 py-1 text-sm font-medium text-purple-800 shadow-sm dark:bg-purple-900/20 dark:text-purple-200">
    {manga.chapters} chapters
    </span>
    )}
    </div>

    {/* User's current list status (if on their list) */}
    {(() => {
    const mediaListEntry = manga.mediaListEntry;

    if (!mediaListEntry || !isOnUserList(mediaListEntry)) {
    return null;
    }

    return (
    <div className="rounded-2xl border border-blue-400/30 bg-blue-500/10 p-4 text-sm shadow-inner dark:border-blue-400/20 dark:bg-blue-900/30">
    <div className="flex flex-wrap items-center gap-2">
    <span className="text-sm font-semibold text-blue-900 dark:text-blue-100">
    On your list
    </span>
    <span
    className={`inline-flex items-center rounded-full px-2.5 py-1 text-xs font-semibold ${getStatusBadgeColor(mediaListEntry.status)}`}
    >
    {formatMediaListStatus(mediaListEntry.status)}
    </span>
    </div>
    <div className="mt-2 flex flex-wrap items-center gap-3 text-sm text-blue-800 dark:text-blue-200">
    <span>
    Progress: {mediaListEntry.progress || 0}
    {result.chapters &&
    result.chapters > 0 &&
    ` / ${result.chapters}`}
    </span>
    <span>
    Score: {formatScore(mediaListEntry.score)}
    </span>
    </div>
    </div>
    );
    })()}

    <div className="flex flex-wrap gap-2 pt-2">
    {manga.genres
    ?.slice(0, 3)
    .map((genre: string, i: number) => (
    <span
    key={`${uniqueKey}-genre-${i}`}
    className="inline-flex items-center gap-2 rounded-full bg-slate-100/90 px-3 py-1 text-sm font-medium text-slate-700 shadow-sm dark:bg-slate-800/70 dark:text-slate-200"
    >
    {genre}
    </span>
    ))}
    {manga.genres && manga.genres.length > 3 && (
    <span className="inline-flex items-center gap-2 rounded-full bg-slate-100/90 px-3 py-1 text-sm font-medium text-slate-700 shadow-sm dark:bg-slate-800/70 dark:text-slate-200">
    +{manga.genres.length - 3} more
    </span>
    )}
    </div>
    </div>

    <div className="relative z-[1] flex flex-col items-end justify-between gap-4 self-stretch lg:gap-5">
    <button
    className={`inline-flex items-center gap-2 rounded-full border border-blue-400/40 px-4 py-2 text-sm font-semibold text-blue-700 transition focus-visible:ring-2 focus-visible:ring-blue-300 focus-visible:outline-none dark:border-blue-500/30 dark:text-blue-200 ${
    isSelected
    ? "bg-blue-500/20"
    : "bg-white/70 hover:bg-blue-50 dark:bg-slate-900/60 dark:hover:bg-slate-800/70"
    }`}
    onClick={(e) => {
    e.stopPropagation();
    handleSelectResult(result, index);
    }}
    aria-label="Select this manga match"
    >
    <Check size={18} aria-hidden="true" /> Select match
    </button>
    </div>
    </div>
    </div>
    );
    })}

    {hasNextPage && (
    <button
    className="group relative w-full overflow-hidden rounded-2xl border border-white/40 bg-white/70 px-4 py-3 text-center text-base font-semibold text-blue-700 shadow-md shadow-blue-200/40 transition hover:-translate-y-0.5 hover:border-blue-300/60 hover:text-blue-900 dark:border-slate-700/70 dark:bg-slate-900/70 dark:text-blue-200 dark:hover:border-blue-400/60"
    onClick={loadMoreResults}
    disabled={isSearching}
    >
    {isSearching ? (
    <>
    <Loader2
    className="mr-2 inline h-5 w-5 animate-spin"
    aria-hidden="true"
    />
    Loading more...
    </>
    ) : (
    <span className="inline-flex items-center justify-center gap-2">
    <Sparkles className="h-4 w-4" aria-hidden="true" />
    Load more results
    </span>
    )}
    </button>
    )}
    </div>
    </section>
    </div>
    );
    }