import {
  ChevronDown,
  Heart,
  Menu,
  Search as SearchIcon,
  X,
} from "lucide-react";
import { AnimatePresence, m } from "framer-motion";
import { useEffect, useRef, useState } from "react";
import { Link, useLocation, useNavigate, useOutlet } from "react-router-dom";
import { pageTransition } from "../motion";
import { ArkLogoMark } from "../components/ArkLogoMark";
import { usePageTitle } from "../components/PageTitleContext";
import { prefetchPostStream } from "../components/messageStream/hooks/usePostStream";
import { BackToTop } from "../components/BackToTop";
import { DocumentMeta } from "../components/DocumentMeta";
import { SearchPanel } from "../components/SearchPanel";
import { useI18n, type Lang } from "../i18n";
import { LANG_OPTIONS } from "../i18nLanguages";
import {
  homePathForLang,
  isHomePathname,
  languageFromPathname,
  localizePath,
  stripLangPrefix,
} from "../languageRoutes";
import { useLocalizedPath } from "../useLocalizedPath";
import { WalletButton } from "../wallet/WalletButton";

type PublicNavWhich =
  | "home"
  | "browseAll"
  | "categories"
  | "browseLatest"
  | "browseRecommended"
  | "browsePopular"
  | "favorites";

function navIsActive(
  pathname: string,
  search: string,
  hash: string,
  which: PublicNavWhich,
): boolean {
  const sp = new URLSearchParams(search);
  const stripped = stripLangPrefix(pathname);
  switch (which) {
    case "home":
      return isHomePathname(pathname);
    case "browseAll":
      return stripped === "/browse" && !sp.has("sort");
    case "categories":
      return (
        stripped === "/categories" ||
        (isHomePathname(pathname) && hash === "#categories")
      );
    case "browseLatest":
      return stripped === "/browse" && sp.get("sort") === "latest";
    case "browseRecommended":
      return stripped === "/official-recommendations";
    case "browsePopular":
      return stripped === "/browse" && sp.get("sort") === "popular";
    case "favorites":
      return (
        stripped === "/favorites" ||
        (isHomePathname(pathname) && hash === "#favorites")
      );
    default:
      return false;
  }
}

function navClassName(active: boolean) {
  return [
    "relative shrink-0 rounded-sm px-2 py-2 text-[13px] font-medium leading-none whitespace-nowrap no-underline outline-none transition-colors",
    // Hover-only gold underline that slides in (resting/active look unchanged).
    "after:pointer-events-none after:absolute after:inset-x-2 after:bottom-1 after:h-[2px] after:origin-left after:scale-x-0 after:rounded-full after:bg-ark-gold after:transition-transform after:duration-300 hover:after:scale-x-100 motion-reduce:after:transition-none",
    "focus-visible:ring-2 focus-visible:ring-ark-gold/90 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg",
    active
      ? "text-ark-gold visited:text-ark-gold"
      : "text-[#d7d7dc] visited:text-[#d7d7dc] hover:text-ark-gold",
  ].join(" ");
}

const dropdownAnimationClass = "ark-header-popover-enter";
const headerMenuAnimationClass = "ark-header-menu-enter";

const publicMenuOpenChangeEvent = "ark:public-menu-open-change";

type LanguageDropdownProps = {
  lang: Lang;
  setLang: (lang: Lang) => void;
  ariaLabel: string;
  className?: string;
};

function flagSrc(code: Lang): string {
  return `/assets/ark-library/flags/${code}.svg`;
}

function FlagIcon({
  code,
  className = "",
}: {
  code: Lang;
  className?: string;
}) {
  return (
    <img
      src={flagSrc(code)}
      alt=""
      aria-hidden
      draggable={false}
      decoding="async"
      className={`shrink-0 rounded-full object-cover ${className}`}
    />
  );
}

function LanguageDropdown({
  lang,
  setLang,
  ariaLabel,
  className = "",
}: LanguageDropdownProps) {
  const [open, setOpen] = useState(false);
  const rootRef = useRef<HTMLDivElement>(null);
  const selected = LANG_OPTIONS.find((option) => option.code === lang);

  useEffect(() => {
    if (!open) return;

    const closeOnOutside = (event: MouseEvent | TouchEvent) => {
      if (!rootRef.current?.contains(event.target as Node)) setOpen(false);
    };
    const closeOnEscape = (event: KeyboardEvent) => {
      if (event.key === "Escape") setOpen(false);
    };

    document.addEventListener("mousedown", closeOnOutside);
    document.addEventListener("touchstart", closeOnOutside);
    window.addEventListener("keydown", closeOnEscape);
    return () => {
      document.removeEventListener("mousedown", closeOnOutside);
      document.removeEventListener("touchstart", closeOnOutside);
      window.removeEventListener("keydown", closeOnEscape);
    };
  }, [open]);

  return (
    <div ref={rootRef} className={`relative ${className}`}>
      <button
        type="button"
        onClick={() => setOpen((value) => !value)}
        className="flex h-full w-full items-center gap-2 rounded-full border border-ark-line bg-[#1a1b20] px-3 py-2 text-sm text-neutral-200 shadow-inner outline-none transition hover:border-ark-gold/50 focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg"
        aria-label={ariaLabel}
        aria-haspopup="listbox"
        aria-expanded={open}
      >
        <FlagIcon code={lang} className="h-5 w-5" />
        <span className="min-w-0 flex-1 truncate text-left">
          {selected?.label ?? lang}
        </span>
        <ChevronDown
          size={16}
          className={`shrink-0 text-neutral-400 transition ${
            open ? "rotate-180 text-ark-gold" : ""
          }`}
          aria-hidden
        />
      </button>

      {open ? (
        <div
          className={`${dropdownAnimationClass} absolute left-0 right-0 top-[calc(100%+0.5rem)] z-50 overflow-hidden rounded-2xl border border-white/10 bg-[#1c1c21]/95 p-1.5 shadow-2xl shadow-black/70 ring-1 ring-ark-line/80 backdrop-blur-xl`}
          role="listbox"
          aria-label={ariaLabel}
        >
          {LANG_OPTIONS.map((option) => {
            const active = option.code === lang;
            return (
              <button
                key={option.code}
                type="button"
                role="option"
                aria-selected={active}
                onClick={() => {
                  setLang(option.code as Lang);
                  setOpen(false);
                }}
                className={`flex w-full items-center gap-2 rounded-xl px-3 py-2 text-left text-sm transition ${
                  active
                    ? "bg-ark-gold/10 text-ark-gold2"
                    : "text-neutral-200 hover:bg-white/10 hover:text-white"
                }`}
              >
                <FlagIcon code={option.code} className="h-5 w-5" />
                <span className="truncate font-medium">{option.label}</span>
              </button>
            );
          })}
        </div>
      ) : null}
    </div>
  );
}

type MobileLanguageButtonProps = {
  lang: Lang;
  setLang: (lang: Lang) => void;
  ariaLabel: string;
  onOpen?: () => void;
};

function MobileLanguageButton({
  lang,
  setLang,
  ariaLabel,
  onOpen,
}: MobileLanguageButtonProps) {
  const [open, setOpen] = useState(false);
  const rootRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;

    const closeOnOutside = (event: MouseEvent | TouchEvent) => {
      if (!rootRef.current?.contains(event.target as Node)) setOpen(false);
    };
    const closeOnEscape = (event: KeyboardEvent) => {
      if (event.key === "Escape") setOpen(false);
    };

    document.addEventListener("mousedown", closeOnOutside);
    document.addEventListener("touchstart", closeOnOutside);
    window.addEventListener("keydown", closeOnEscape);
    return () => {
      document.removeEventListener("mousedown", closeOnOutside);
      document.removeEventListener("touchstart", closeOnOutside);
      window.removeEventListener("keydown", closeOnEscape);
    };
  }, [open]);

  return (
    <div ref={rootRef} className="relative shrink-0">
      <button
        type="button"
        onClick={() => {
          onOpen?.();
          setOpen((value) => !value);
        }}
        className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-[#191921] outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070c]"
        aria-label={ariaLabel}
        aria-haspopup="listbox"
        aria-expanded={open}
      >
        <FlagIcon code={lang} className="h-7 w-7" />
      </button>

      {open ? (
        <div
          className={`${dropdownAnimationClass} absolute right-0 top-[calc(100%+0.5rem)] z-50 w-44 overflow-hidden rounded-2xl border border-white/10 bg-[#1c1c21]/95 p-1.5 shadow-2xl shadow-black/70 ring-1 ring-ark-line/80 backdrop-blur-xl`}
          role="listbox"
          aria-label={ariaLabel}
        >
          {LANG_OPTIONS.map((option) => {
            const active = option.code === lang;
            return (
              <button
                key={option.code}
                type="button"
                role="option"
                aria-selected={active}
                onClick={() => {
                  setLang(option.code as Lang);
                  setOpen(false);
                }}
                className={`flex w-full items-center gap-2 rounded-xl px-3 py-2 text-left text-sm transition ${
                  active
                    ? "bg-ark-gold/10 text-ark-gold2"
                    : "text-neutral-200 hover:bg-white/10 hover:text-white"
                }`}
              >
                <FlagIcon code={option.code} className="h-5 w-5" />
                <span className="truncate font-medium">{option.label}</span>
              </button>
            );
          })}
        </div>
      ) : null}
    </div>
  );
}

export function PublicLayout() {
  const { t, lang, setLang } = useI18n();
  const { pathname, search, hash } = useLocation();
  const outlet = useOutlet();
  const [open, setOpen] = useState(false);
  const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
  const [desktopSearchOpen, setDesktopSearchOpen] = useState(false);
  const [q, setQ] = useState("");
  const menuRef = useRef<HTMLDivElement>(null);
  const mobileMenuButtonRef = useRef<HTMLButtonElement>(null);
  const desktopMenuButtonRef = useRef<HTMLButtonElement>(null);
  const desktopSearchRef = useRef<HTMLDivElement>(null);
  const desktopSearchPanelRef = useRef<HTMLDivElement>(null);
  const nav = useNavigate();
  const lp = useLocalizedPath();

  // Keep i18n state in sync with URL so deep links (`/ms/browse`) flip the
  // UI language even if the user navigated via address bar or shared link.
  useEffect(() => {
    const urlLang = languageFromPathname(pathname);
    if (urlLang !== lang) setLang(urlLang);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [pathname]);

  const na = (which: PublicNavWhich) =>
    navIsActive(pathname, search, hash, which);
  const isHome = isHomePathname(pathname);
  const homePath = homePathForLang(lang);
  const changeLang = (nextLang: Lang) => {
    setLang(nextLang);
    if (isHome) {
      nav(homePathForLang(nextLang), { replace: true });
    } else {
      // Preserve sub-path and query/hash; only swap the language prefix.
      const canonical = stripLangPrefix(pathname);
      nav(localizePath(canonical, nextLang) + search + hash, {
        replace: true,
      });
    }
  };
  // Routes that render a full-bleed asset stream and manage their own inner
  // width / padding via `MessageStream`. Both 全部资料 (/browse) and the
  // per-category view (/category/<slug>) reuse the same component, so they
  // need the same zero outer padding here — otherwise the category page's
  // bubbles render narrower than the all-resources page.
  const strippedPath = stripLangPrefix(pathname);
  const footerInContentFlow =
    strippedPath === "/browse" || strippedPath.startsWith("/category/");
  // Current page name shown in the header brand slot (falls back to the brand).
  const pageTitle = usePageTitle();

  // Warm the common stream views (全部资料 / 热门资料 / 最新) so tapping them
  // shows content immediately. The default "all" stream is the most common
  // destination (banners, Home cards) and fires right on mount so a fast tap
  // still hits a warm cache. Popular / latest stay deferred to idle time so
  // they don't compete with the current page on low-end phones.
  useEffect(() => {
    const base = { scope: { kind: "all" as const }, type: "all", q: "", lang };
    prefetchPostStream({ ...base, sort: "" });

    const jobs = [
      () => prefetchPostStream({ ...base, sort: "popular" }),
      () => prefetchPostStream({ ...base, sort: "latest" }),
    ];
    const ric = window as typeof window & {
      requestIdleCallback?: (cb: () => void) => number;
      cancelIdleCallback?: (id: number) => void;
    };

    let i = 0;
    let stepTimer = 0;
    let idleId = 0;
    const runNext = () => {
      if (i >= jobs.length) return;
      jobs[i++]();
      stepTimer = window.setTimeout(schedule, 400); // space requests apart
    };
    const schedule = () => {
      if (ric.requestIdleCallback) idleId = ric.requestIdleCallback(runNext);
      else stepTimer = window.setTimeout(runNext, 200);
    };

    const startTimer = window.setTimeout(schedule, 300);
    return () => {
      window.clearTimeout(startTimer);
      window.clearTimeout(stepTimer);
      if (idleId) ric.cancelIdleCallback?.(idleId);
    };
  }, [lang]);
  const popularHref = lp("/browse?sort=popular");

  const goSearch = () => {
    const s = q.trim();
    if (!s) return;
    nav(lp(`/browse?q=${encodeURIComponent(s)}`));
    setOpen(false);
    setMobileSearchOpen(false);
    setDesktopSearchOpen(false);
  };

  useEffect(() => {
    window.dispatchEvent(
      new CustomEvent<boolean>(publicMenuOpenChangeEvent, { detail: open }),
    );
  }, [open]);

  useEffect(() => {
    if (!desktopSearchOpen) return;

    const closeOnOutside = (event: MouseEvent | TouchEvent) => {
      const target = event.target as Node;
      if (
        desktopSearchRef.current?.contains(target) ||
        desktopSearchPanelRef.current?.contains(target)
      ) {
        return;
      }
      setDesktopSearchOpen(false);
    };
    const closeOnEscape = (event: KeyboardEvent) => {
      if (event.key === "Escape") setDesktopSearchOpen(false);
    };

    document.addEventListener("mousedown", closeOnOutside);
    document.addEventListener("touchstart", closeOnOutside);
    window.addEventListener("keydown", closeOnEscape);
    return () => {
      document.removeEventListener("mousedown", closeOnOutside);
      document.removeEventListener("touchstart", closeOnOutside);
      window.removeEventListener("keydown", closeOnEscape);
    };
  }, [desktopSearchOpen]);

  useEffect(() => {
    if (!open) return;

    // Opening the menu from the burger also closes the search overlay, whose
    // scroll-lock cleanup fires a programmatic scroll. Ignore scroll-to-close
    // for a brief window so that restore scroll doesn't shut the menu we just
    // opened; genuine user scrolls afterwards still close it.
    const openedAt = Date.now();

    const closeOnOutside = (event: MouseEvent | TouchEvent) => {
      const target = event.target as Node;
      if (
        menuRef.current?.contains(target) ||
        mobileMenuButtonRef.current?.contains(target) ||
        desktopMenuButtonRef.current?.contains(target)
      ) {
        return;
      }
      setOpen(false);
    };
    const closeOnScroll = () => {
      if (Date.now() - openedAt < 250) return;
      setOpen(false);
    };

    document.addEventListener("mousedown", closeOnOutside);
    document.addEventListener("touchstart", closeOnOutside);
    window.addEventListener("scroll", closeOnScroll);
    return () => {
      document.removeEventListener("mousedown", closeOnOutside);
      document.removeEventListener("touchstart", closeOnOutside);
      window.removeEventListener("scroll", closeOnScroll);
    };
  }, [open]);

  // Lock background scroll while the mobile search overlay is open.
  // Uses the iOS-compatible position-fixed pattern so the underlying page
  // doesn't move at all (overflow:hidden alone is not enough on iOS Safari).
  useEffect(() => {
    if (!mobileSearchOpen) return;
    const scrollY = window.scrollY;
    const body = document.body;
    const prev = {
      position: body.style.position,
      top: body.style.top,
      left: body.style.left,
      right: body.style.right,
      width: body.style.width,
    };
    body.style.position = "fixed";
    body.style.top = `-${scrollY}px`;
    body.style.left = "0";
    body.style.right = "0";
    body.style.width = "100%";
    return () => {
      body.style.position = prev.position;
      body.style.top = prev.top;
      body.style.left = prev.left;
      body.style.right = prev.right;
      body.style.width = prev.width;
      window.scrollTo(0, scrollY);
    };
  }, [mobileSearchOpen]);

  // Lock background scroll while the full-screen mobile menu drawer is open.
  useEffect(() => {
    if (!open) return;
    const scrollY = window.scrollY;
    const body = document.body;
    const prev = {
      position: body.style.position,
      top: body.style.top,
      left: body.style.left,
      right: body.style.right,
      width: body.style.width,
    };
    body.style.position = "fixed";
    body.style.top = `-${scrollY}px`;
    body.style.left = "0";
    body.style.right = "0";
    body.style.width = "100%";
    return () => {
      body.style.position = prev.position;
      body.style.top = prev.top;
      body.style.left = prev.left;
      body.style.right = prev.right;
      body.style.width = prev.width;
      window.scrollTo(0, scrollY);
    };
  }, [open]);

  return (
    <div className="flex min-h-[100dvh] flex-col bg-ark-bg">
      <DocumentMeta />
      <header className="sticky top-0 z-40 select-none bg-[#08070c] backdrop-blur-md md:border-b md:border-ark-line md:bg-ark-nav/98">
        <div className="flex h-[64px] items-center justify-between bg-[#08070c] px-4 py-3 md:hidden">
          <div className="flex h-8 min-w-0 shrink items-center gap-2 text-[20px] font-black leading-5 tracking-tight text-ark-gold">
            {/* Logo → home; page-name text → scroll to top of the current page. */}
            <Link
              to={homePath}
              aria-label={t("brand")}
              onClick={(e) => {
                if (isHome) {
                  e.preventDefault();
                  window.scrollTo({ top: 0, behavior: "smooth" });
                }
              }}
              className="shrink-0 rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070c]"
            >
              <ArkLogoMark className="h-8 w-8 shrink-0" />
            </Link>
            <button
              type="button"
              onClick={() => window.scrollTo({ top: 0, behavior: "smooth" })}
              className="truncate rounded-sm text-left text-ark-gold outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070c]"
            >
              {pageTitle || t("brand")}
            </button>
          </div>

          <div className="flex h-[40px] w-[136px] shrink-0 items-center gap-[8px]">
            <button
              type="button"
              onClick={() => {
                setOpen(false);
                setDesktopSearchOpen(false);
                setMobileSearchOpen((value) => !value);
              }}
              className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-[#191921] outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070c]"
              aria-label={t("search")}
              aria-expanded={mobileSearchOpen}
            >
              <img
                src="/assets/ark-library/header-search.svg"
                alt=""
                className="h-6 w-6"
                width={24}
                height={24}
                aria-hidden
                draggable={false}
              />
            </button>
            <MobileLanguageButton
              lang={lang}
              setLang={changeLang}
              ariaLabel={t("langLabel")}
              onOpen={() => {
                setOpen(false);
                setDesktopSearchOpen(false);
                setMobileSearchOpen(false);
              }}
            />
            <button
              ref={mobileMenuButtonRef}
              type="button"
              className="inline-flex h-[40px] w-[40px] shrink-0 items-center justify-center rounded-full bg-[#191921] text-[#a8a9ae] outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070c]"
              onClick={() => {
                setMobileSearchOpen(false);
                setDesktopSearchOpen(false);
                setOpen((v) => !v);
              }}
              aria-label="menu"
              aria-expanded={open}
            >
              {open ? (
                <X size={24} strokeWidth={2.4} />
              ) : (
                <img
                  src="/assets/ark-library/header-menu.svg"
                  alt=""
                  className="h-6 w-6"
                  width={24}
                  height={24}
                  aria-hidden
                  draggable={false}
                />
              )}
            </button>
          </div>
        </div>

        <div className="mx-auto hidden max-w-[1280px] px-4 py-[15px] min-[440px]:px-5 sm:px-6 md:block md:px-9 xl:px-6">
          {/* Single row (md+): logo | scrollable nav (左對齊，可橫向滑動) | 搜尋 + 語言 */}
          <div className="flex h-10 items-center gap-2 min-[1000px]:gap-0 lg:gap-4">
            <div className="flex min-w-0 shrink items-center gap-2.5 text-xl font-bold tracking-wide text-ark-gold">
              {/* Logo → home; page-name text → scroll to top of the current page. */}
              <Link
                to={homePath}
                aria-label={t("brand")}
                onClick={(e) => {
                  if (isHome) {
                    e.preventDefault();
                    window.scrollTo({ top: 0, behavior: "smooth" });
                  }
                }}
                className="shrink-0 rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg"
              >
                <ArkLogoMark className="h-10 w-10 shrink-0" />
              </Link>
              <button
                type="button"
                onClick={() => window.scrollTo({ top: 0, behavior: "smooth" })}
                className="max-w-[14rem] truncate rounded-sm text-left text-ark-gold outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg sm:inline"
              >
                {pageTitle || t("brand")}
              </button>
            </div>

            <nav
              className="header-nav-scroll hidden min-w-0 flex-1 items-center justify-center gap-4 overflow-x-auto overflow-y-hidden py-0.5 min-[1000px]:flex lg:gap-5"
              aria-label={t("mainNav")}
            >
              <Link
                to={lp("/browse")}
                className={navClassName(na("browseAll"))}
                aria-current={na("browseAll") ? "page" : undefined}
              >
                {t("all")}
              </Link>
              <Link
                to={lp("/categories")}
                className={navClassName(na("categories"))}
                aria-current={na("categories") ? "page" : undefined}
              >
                {t("categories")}
              </Link>
              <Link
                to={lp("/official-recommendations")}
                className={navClassName(na("browseRecommended"))}
                aria-current={na("browseRecommended") ? "page" : undefined}
              >
                {t("official")}
              </Link>
              <Link
                to={lp("/browse?sort=latest")}
                className={navClassName(na("browseLatest"))}
                aria-current={na("browseLatest") ? "page" : undefined}
              >
                {t("latest")}
              </Link>
              <Link
                to={popularHref}
                className={navClassName(na("browsePopular"))}
                aria-current={na("browsePopular") ? "page" : undefined}
              >
                {t("popular")}
              </Link>
            </nav>

            <div className="flex min-w-0 flex-1 items-center justify-end gap-2 min-[1000px]:flex-none">
              <div ref={desktopSearchRef} className="hidden md:block">
                <button
                  type="button"
                  onClick={() => setDesktopSearchOpen(true)}
                  className={`inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full border bg-[#1a1b20] outline-none transition hover:border-ark-gold/50 hover:text-ark-gold focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg ${
                    desktopSearchOpen
                      ? "border-ark-gold/70 text-ark-gold"
                      : "border-ark-line text-neutral-200"
                  }`}
                  aria-label={t("searchNow")}
                  aria-expanded={desktopSearchOpen}
                >
                  <SearchIcon size={18} />
                </button>
              </div>
              <LanguageDropdown
                lang={lang}
                setLang={changeLang}
                ariaLabel={t("langLabel")}
                className="hidden h-10 w-36 md:block lg:w-40"
              />
              <Link
                to={lp("/favorites")}
                onClick={() => window.scrollTo({ top: 0, left: 0 })}
                className={`hidden h-10 shrink-0 items-center justify-center gap-2 rounded-full border px-3 text-sm font-bold outline-none transition focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg md:inline-flex ${
                  na("favorites")
                    ? "border-ark-gold bg-ark-gold text-black"
                    : "border-ark-line bg-[#1a1b20] text-neutral-200 hover:border-ark-gold/50 hover:text-ark-gold"
                }`}
                aria-current={na("favorites") ? "page" : undefined}
              >
                <Heart className="h-[18px] w-[18px]" strokeWidth={2.2} />
                <span className="hidden xl:inline">{t("favorites")}</span>
              </Link>
              <div className="hidden md:block">
                <WalletButton />
              </div>
              <button
                ref={desktopMenuButtonRef}
                type="button"
                className="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-ark-line bg-[#1a1b20] text-neutral-200 outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg min-[1000px]:hidden"
                onClick={() => {
                  setDesktopSearchOpen(false);
                  setOpen((v) => !v);
                }}
                aria-label="menu"
              >
                {open ? <X size={18} /> : <Menu size={18} />}
              </button>
            </div>
          </div>
        </div>
      </header>

      {open ? (
        <div
          ref={menuRef}
          className={`${headerMenuAnimationClass} fixed inset-x-0 bottom-0 top-[64px] z-50 flex flex-col bg-ark-bg/90 backdrop-blur-xl md:top-[70px] min-[1000px]:hidden`}
        >
          <nav className="flex-1 overflow-y-auto px-5">
            {(
              [
                {
                  to: lp("/browse"),
                  label: t("all"),
                  active: na("browseAll"),
                },
                {
                  to: lp("/categories"),
                  label: t("categories"),
                  active: na("categories"),
                },
                {
                  to: lp("/official-recommendations"),
                  label: t("official"),
                  active: na("browseRecommended"),
                },
                {
                  to: lp("/browse?sort=latest"),
                  label: t("latest"),
                  active: na("browseLatest"),
                },
                {
                  to: popularHref,
                  label: t("popular"),
                  active: na("browsePopular"),
                },
              ] as const
            ).map((item) => (
              <Link
                key={item.to}
                to={item.to}
                aria-current={item.active ? "page" : undefined}
                onClick={() => setOpen(false)}
                className={`flex h-[68px] items-center border-b border-[#2B2B37] text-[15px] font-medium leading-[20px] outline-none transition-colors focus-visible:text-ark-gold ${
                  item.active
                    ? "text-ark-gold"
                    : "text-[#A8A9AE] [@media(hover:hover)]:hover:text-ark-gold"
                }`}
              >
                {item.label}
              </Link>
            ))}
          </nav>
          <div className="px-5 pb-[max(env(safe-area-inset-bottom),34px)] pt-4">
            <WalletButton compact onOpenLogin={() => setOpen(false)} />
          </div>
        </div>
      ) : null}

      {mobileSearchOpen ? (
        <SearchPanel
          lang={lang}
          t={t}
          query={q}
          onQueryChange={setQ}
          onSearch={goSearch}
          onResultClick={() => setMobileSearchOpen(false)}
        />
      ) : null}

      {desktopSearchOpen ? (
        <SearchPanel
          panelRef={desktopSearchPanelRef}
          lang={lang}
          t={t}
          query={q}
          onQueryChange={setQ}
          onSearch={goSearch}
          onResultClick={() => setDesktopSearchOpen(false)}
          variant="desktop"
        />
      ) : null}

      <main
        className={`mx-auto w-full max-w-[1280px] max-md:pb-[calc(68px+max(env(safe-area-inset-bottom),0px)+1rem)] ${
          isHome
            ? "flex-1 px-0 pb-6 pt-0 md:px-9 md:pb-10 md:pt-10 xl:px-0"
            : footerInContentFlow
              ? "flex-1 px-0 pb-0 pt-0 md:px-9 xl:px-0"
              : "flex-1 px-4 pb-6 pt-6 min-[440px]:px-5 sm:px-6 md:px-9 md:pb-10 md:pt-10 xl:px-0"
        }`}
      >
        <AnimatePresence mode="wait" initial={false}>
          <m.div
            key={`${pathname}${search}`}
            variants={pageTransition}
            initial="initial"
            animate="enter"
            exit="exit"
          >
            {outlet}
          </m.div>
        </AnimatePresence>
      </main>

      <nav className="fixed inset-x-0 bottom-0 z-40 select-none bg-[#0C0D0F]/95 pb-[max(env(safe-area-inset-bottom),0px)] backdrop-blur md:hidden">
        <div className="grid h-[68px] grid-cols-4 gap-2 px-4 py-[10px] text-center text-[11px] leading-[17.6px]">
          <BottomNavIcon
            to={homePath}
            label={t("home")}
            icon="home"
            active={isHome}
          />
          <BottomNavIcon
            to={lp("/browse")}
            label={t("all")}
            icon="document"
            active={
              stripLangPrefix(pathname) === "/browse" &&
              !new URLSearchParams(search).get("sort")
            }
          />
          <BottomNavIcon
            to={lp("/favorites")}
            label={t("favorites")}
            icon="bookmark"
            active={na("favorites")}
          />
          <BottomNavIcon
            to={popularHref}
            label={t("popular")}
            icon="update"
            active={na("browsePopular")}
          />
        </div>
      </nav>

      {stripLangPrefix(pathname) === "/browse" ? <BackToTop /> : null}
    </div>
  );
}

const NAVBAR_ICON_BASE = "/assets/ark-library/navbar";

function BottomNavIcon({
  to,
  label,
  icon,
  active,
}: {
  to: string;
  label: string;
  icon: "home" | "document" | "bookmark" | "update";
  active: boolean;
}) {
  const activeSrc = `${NAVBAR_ICON_BASE}/${icon}-active.svg`;
  const inactiveSrc = `${NAVBAR_ICON_BASE}/${icon}-inactive.svg`;
  return (
    <Link
      to={to}
      className={[
        "flex min-w-0 flex-col items-center gap-1 rounded-lg outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg",
        active ? "text-ark-gold" : "text-[#908F92]",
      ].join(" ")}
    >
      <span className="relative h-6 w-6" aria-hidden>
        <img
          src={inactiveSrc}
          alt=""
          className={`absolute inset-0 h-6 w-6 object-contain ${
            active ? "opacity-0" : "opacity-100"
          }`}
          width={24}
          height={24}
          loading="eager"
          decoding="sync"
        />
        <img
          src={activeSrc}
          alt=""
          className={`absolute inset-0 h-6 w-6 object-contain ${
            active ? "opacity-100" : "opacity-0"
          }`}
          width={24}
          height={24}
          loading="eager"
          decoding="sync"
        />
      </span>
      <span className="leading-tight">{label}</span>
    </Link>
  );
}
