"use client";

import { useRouter } from "next/navigation";
import Link from "next/link";
import dynamic from "next/dynamic";
import type {
  CSSProperties,
  DragEvent as ReactDragEvent,
  FormEvent as ReactFormEvent,
  MouseEvent as ReactMouseEvent,
  PointerEvent as ReactPointerEvent,
  ReactNode,
  RefObject
} from "react";
import {
  Fragment,
  useDeferredValue,
  useEffect,
  useEffectEvent,
  useLayoutEffect,
  useRef,
  useState,
  useTransition
} from "react";
import { createPortal, useFormStatus } from "react-dom";
import {
  mdiFileCode,
  mdiFileDelimited,
  mdiFileDocument,
  mdiFileExcelBox,
  mdiFileImage,
  mdiFileMusic,
  mdiFileOutline,
  mdiFilePdfBox,
  mdiFilePowerpointBox,
  mdiFileVideo,
  mdiFileWordBox,
  mdiFolderZip
} from "@mdi/js";
import {
  logoutAction,
  markMailboxMessagesReadAction,
  saveMailboxCredentialsAction,
  updateMailboxSenderSpamRuleAction
} from "@/app/actions/session";
import { BottomToast } from "@/components/bottom-toast";
import { CloseIconButton } from "@/components/close-icon-button";
import { useI18n } from "@/components/i18n-provider";
import { MailAutoSendPanel } from "@/components/mail-auto-send-panel";
import { MailComposeForm } from "@/components/mail-compose-form";
import { MailExitDraftRecovery } from "@/components/mail-exit-draft-recovery";
import { MailServiceNoticeDialog } from "@/components/mail-service-notice-dialog";
import { MailSenderProfilePopover, type MailSenderClassificationInput } from "@/components/mail-sender-profile-popover";
import { MailSetupWorkspace, type MailSetupSection } from "@/components/mail-setup-workspace";
import type { MemberMailboxViewState } from "@/components/mail-members-panel";
import { SubmitButton } from "@/components/submit-button";
import type { MailBillingCancellationOverview } from "@/lib/billing-cancellation";
import type { MailPlanSummary } from "@/lib/mail-billing";
import { translateStaticUiText, type TranslationKey } from "@/lib/i18n-dictionaries";
import type { MailBillingHistoryState, MailBillingTab } from "@/lib/mail-billing-history";
import { MAIL_QUOTE_BASE_CSS } from "@/lib/mail-quote-style";
import {
  MAIL_LAYOUT_LIMITS,
  normalizeMailLayoutPreferences,
  type MailListSortOrder,
  type MailListViewMode,
  type MailLayoutPreferences
} from "@/lib/mail-layout";
import type { MailAiAssistOverview, ManagedMailboxMember } from "@/lib/mail-automation";
import type { MailAutoSendOverview } from "@/lib/mail-auto-send";
import type {
  MailAppOverview,
  MailMessageAttachment,
  MailMessageDetail,
  MailMessageFilter,
  MailMessageSummary,
  MailboxSenderMoveRule,
  MailSearchField,
  MailSearchSuggestion,
  MailSetupOverview
} from "@/lib/official-mail";
import {
  DEFAULT_MAILBOX_PAGE_SIZE,
  MAILBOX_PAGE_SIZE_OPTIONS,
  normalizeMailboxPageSize
} from "@/lib/mailbox-page-size";
import type { MailBillingHistoryPage, MailBillingOverview } from "@/lib/toss-pay";
import { OFFICIAL_MAIL_GUIDE_URL } from "@/lib/mail-urls";
import { getPublicMailTransportErrorMessage } from "@/lib/mail-error-messages";
import type { MailSignatureSettings } from "@/lib/mail-signature";
import type { MailMessageTranslationResult } from "@/lib/mail-message-translation";
import { toolbarMenuPosition } from "@/lib/toolbar-menu-position";
import type { MailBillingProvider } from "@/lib/billing-region";
import type { PolarBillingOverview } from "@/lib/polar-billing";

const MailFileDrawer = dynamic(() => import("@/components/mail-file-drawer").then((module) => module.MailFileDrawer), {
  loading: () => <div className="h-full animate-pulse bg-[#f8faff]" />
});

type MailGateProps = {
  currentPanel: "auto-send" | "mail" | "setup";
  dkimRegenerated: boolean;
  showDkimRotationNotice: boolean;
  setupSection: MailSetupSection;
  setupOverview: MailSetupOverview | null;
  managedMembers: ManagedMailboxMember[];
  memberMailboxView: MemberMailboxViewState | null;
  aiAssistOverview: MailAiAssistOverview | null;
  autoSendOverview: MailAutoSendOverview | null;
  billingOverview: MailBillingOverview | null;
  billingProvider: MailBillingProvider;
  usesTossPayments: boolean;
  polarBillingOverview: PolarBillingOverview | null;
  billingCancellationOverview: MailBillingCancellationOverview | null;
  billingHistoryPage: MailBillingHistoryPage | null;
  billingHistoryState: MailBillingHistoryState;
  billingTab: MailBillingTab;
  planSummary: MailPlanSummary;
  canManageBilling: boolean;
  domainSetupAssistanceAvailable: boolean;
  mailConfigured: boolean;
  userEmail: string;
  userNotificationEmail: string;
  userRecoveryEmail: string;
  userRecoveryEmailVerified: boolean;
  userDisplayName: string;
  mailbox: string;
  companyName: string;
  domain: string;
  data: MailAppOverview | null;
  initialMessageTranslation: MailMessageTranslationResult | null;
  currentFolder: string;
  currentView: "list" | "compose" | "files";
  composeMode: "draft" | "forward" | "new" | "reply" | "replyAll";
  sourceDraftMessageId: number | null;
  detailView: "body" | "source" | "logs";
  detailOpen: boolean;
  currentSearchField: MailSearchField;
  currentSearchQuery: string;
  layoutPreferences: MailLayoutPreferences;
  composeBccDefault: string;
  composeCcDefault: string;
  composeToDefault: string;
  composeSubjectDefault: string;
  composeBodyDefault: string;
  composeBodyHtmlDefault: string;
  composeSignatureHtml: string;
  composeSignatureText: string;
  signatureSettings: MailSignatureSettings;
  notice: { tone: "error" | "success" | "warning"; message: string } | null;
};

type NavItem = {
  id: string;
  label: string;
  count?: number;
  icon: "inbox" | "sent" | "drafts" | "trash" | "spam" | "star" | "all" | "folder";
};

type SidebarFolderNavItem = {
  count: number;
  depth: number;
  href: string;
  icon: "inbox" | "sent" | "drafts" | "trash" | "spam" | "folder";
  id: number;
  kind: "custom" | "system";
  label: string;
  parentSystemName: string | null;
  systemName: string;
};

type SidebarFolderDropPosition = "after" | "before" | "inside";

type SettingsMenuItem = {
  id: MailSetupSection | "logout";
  label: string;
};

type ComposeToolbarMessage = {
  bodyHtml?: string | null;
  id: number;
  bodyText?: string | null;
  fromAddress: string;
  fromName: string | null;
  receivedAt: string;
  snippet?: string;
  subject: string;
  toAddresses: string;
};

type MailNotice = {
  tone: "error" | "success" | "warning";
  message: string;
};

type SenderSpamPromptState = {
  scope: string;
  senderAddress: string;
  senderLabel: string;
};

type AttachmentPreviewState = {
  collection: "images" | "previewable";
  currentIndex: number;
  messageId: number;
  scope: string;
};

type MailboxDataFetchOptions = {
  folder?: string;
  messageId?: number | null;
  page?: number;
  pageSize?: number;
  preserveSelection?: boolean;
  refresh?: boolean;
  searchField?: MailSearchField;
  searchQuery?: string;
  silent?: boolean;
  sortOrder?: MailListSortOrder;
  staleRefresh?: boolean;
  syncUrl?: boolean;
  tab?: MailMessageFilter;
};

type MailboxMoveResult = {
  deletedCount: number;
  deletedMessageIds: number[];
  movedCount: number;
  movedMessageIds: number[];
  spamSenderCount: number;
};

type MailboxMoveResponse = {
  error?: string;
  existingTargetFolder?: string | null;
  jobId?: number;
  notice?: MailNotice | null;
  ok: boolean;
  queued?: boolean;
  result?: MailboxMoveResult;
};

type MailboxMoveJobResponse = {
  job?: {
    error: string | null;
    id: number;
    result?: MailboxMoveResult | null;
    status: "pending" | "processing" | "completed" | "failed";
  };
  ok: boolean;
};

type MailboxMoveUndoResponse = {
  cancelled?: boolean;
  error?: string;
  jobIds?: number[];
  message?: string;
  ok: boolean;
  queued?: boolean;
  retry?: boolean;
};

type PendingMailboxMoveProjection = {
  key: string;
  messageIds: number[];
  targetFolder: string;
  moveFolderCounts?: MailSenderClassificationInput["moveFolderCounts"];
};

async function retryMailboxDataFetch(input: {
  fetchData: () => Promise<boolean>;
  isFetchInFlight: () => boolean;
}) {
  const deadline = Date.now() + 5_000;
  const waitForIdle = async () => {
    while (input.isFetchInFlight() && Date.now() < deadline) {
      await new Promise<void>((resolve) => window.setTimeout(resolve, 50));
    }

    return !input.isFetchInFlight();
  };

  for (let attempt = 0; attempt < 3; attempt += 1) {
    if (!(await waitForIdle())) {
      continue;
    }

    if (await input.fetchData()) {
      return true;
    }

    await new Promise<void>((resolve) => window.setTimeout(resolve, 150 * (attempt + 1)));
  }

  return false;
}

type MailboxUndoAction = {
  activeMessageId: number | null;
  detailView: "body" | "source" | "logs";
  jobId: number;
  moveKey: string;
  previousData: MailAppOverview | null;
  previousSelectionIds: number[];
  restoreFolder: string;
  sourceFolder: string;
  sourceQueryKey: string;
};

type ContinueMovePromptState = {
  formId: string;
  messageIds: number[];
  newFolderName: string;
  senderAddress: string;
  targetFolder: string;
  targetLabel: string;
};

type ContinueMoveReplacementPromptState = ContinueMovePromptState & {
  existingTargetLabel: string;
  existingTargetFolder: string;
  includeExisting: boolean;
};

type MailboxMoveRulesResponse = {
  ok: boolean;
  settings?: {
    rules: MailboxSenderMoveRule[];
  };
};

type SidebarFolderAction = "backup" | "clear" | "delete" | "mark-read";

type SidebarFolderInlineEditorState =
  | {
      errorMessage?: string;
      insertAfterSystemName: string | null;
      insertAtTop: boolean;
      mode: "create";
      parentSystemName: string | null;
      value: string;
    }
  | {
      errorMessage?: string;
      folderId: number;
      mode: "rename";
      systemName: string;
      value: string;
    };

type MailboxLiveUpdatePayload = {
  at: string;
  delta: number;
  mailbox: string;
  reason: "exists" | "expunge" | "flags";
};

type MailboxLiveErrorPayload = {
  at: string;
  mailbox: string;
  message: string;
};

type MailboxFolderStatusResponse = {
  ok?: boolean;
  status?: {
    currentFolderChanged: boolean;
    folders: Array<{
      count: number;
      name: string;
      systemName: string;
      unreadCount: number;
    }>;
    syncedFolders: string[];
  };
};

type MailboxAutoSendCompletedPayload = {
  at: string;
  deliveryCount: number;
  mailbox: string;
  recipientEmail: string;
  subject: string;
};

type FolderSearchParamsOptions = {
  page?: number;
  pageSize?: number;
  searchField?: MailSearchField;
  searchQuery?: string;
  sortOrder?: MailListSortOrder;
  tab?: MailMessageFilter;
};

type PreviewMessage = {
  attachments: MailMessageAttachment[];
  bodyHtml: string | null;
  bodyHtmlDisplay: string | null;
  bodyText: string;
  direction: "inbound" | "outbound" | "draft";
  fromAddress: string;
  fromName: string | null;
  senderContactName: string | null;
  senderIsVip: boolean;
  id: number;
  isRead: boolean;
  isStarred: boolean;
  messageIdHeader: string | null;
  rawSourceAvailable: boolean;
  receivedAt: string;
  snippet: string;
  subject: string;
  toAddresses: string;
  transportResponse: string | null;
  transportStatus: "saved" | "queued" | "sent" | "failed";
};

type MessageTranslationData = MailMessageTranslationResult;

type MessageTranslationState = {
  data: MessageTranslationData | null;
  error: string | null;
  loading: boolean;
  messageId: number | null;
  targetLocale: string | null;
};

type LayoutResizePanel = "sidebar" | "list" | "list-height";
type ToolbarMenuKind = "filter" | "pageSize" | "sort" | "view";

type LayoutDragState = {
  containerWidth: number;
  containerHeight: number;
  isXlLayout: boolean;
  panel: LayoutResizePanel;
  startListHeight: number;
  startListWidth: number;
  startSidebarWidth: number;
  startX: number;
  startY: number;
};

function padNumber(value: number) {
  return String(value).padStart(2, "0");
}

function formatFullDate(value: string) {
  const date = new Date(value);
  const weekdays = ["일", "월", "화", "수", "목", "금", "토"];
  const hours = date.getHours();
  const period = hours >= 12 ? "PM" : "AM";
  const hour12 = hours % 12 || 12;

  return `${date.getFullYear()}.${padNumber(date.getMonth() + 1)}.${padNumber(
    date.getDate()
  )} (${weekdays[date.getDay()]}) ${hour12}:${padNumber(date.getMinutes())} ${period}`;
}

function formatInboxTime(value: string) {
  const date = new Date(value);
  const now = new Date();
  const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  const targetMidnight = new Date(date.getFullYear(), date.getMonth(), date.getDate());
  const diff = midnight.getTime() - targetMidnight.getTime();
  const oneDay = 24 * 60 * 60 * 1000;

  if (diff === 0) {
    const hours = date.getHours();
    const period = hours >= 12 ? "오후" : "오전";
    const hour12 = hours % 12 || 12;
    return `${period} ${hour12}:${padNumber(date.getMinutes())}`;
  }

  if (diff === oneDay) {
    return "어제";
  }

  return `${padNumber(date.getMonth() + 1)}.${padNumber(date.getDate())}`;
}

function formatCount(value: number) {
  return value.toLocaleString("ko-KR");
}

function formatStorage(bytes: number | null) {
  if (bytes === null || !Number.isFinite(bytes)) {
    return "집계 중";
  }

  if (bytes <= 0) {
    return "0B";
  }

  const kib = 1024;
  const mib = kib * 1024;
  const gib = mib * 1024;

  if (bytes >= gib) {
    return `${(bytes / gib).toFixed(1)}GB`;
  }

  if (bytes >= mib) {
    const fractionDigits = bytes >= mib * 100 ? 0 : 1;
    return `${(bytes / mib).toFixed(fractionDigits)}MB`;
  }

  if (bytes >= kib) {
    return `${Math.round(bytes / kib)}KB`;
  }

  return `${Math.round(bytes)}B`;
}

function senderLabel(fromName: string | null, fromAddress: string) {
  return fromName?.trim() || fromAddress.split("@")[0] || fromAddress;
}

function primaryRecipient(toAddresses: string) {
  const firstRecipient =
    toAddresses
      .split(",")
      .map((recipient) => recipient.trim())
      .find(Boolean) ?? "";
  const matchedAddress = firstRecipient.match(/^(.*?)\s*<\s*([^>]+)\s*>$/);
  const displayName = matchedAddress?.[1]?.trim().replace(/^"|"$/g, "");
  const address = (matchedAddress?.[2] ?? firstRecipient).trim();

  return {
    address,
    label: displayName || address.split("@")[0] || address || "받는사람"
  };
}

function compactCount(count: number | undefined) {
  if (!count) return "";
  if (count > 999) return "999+";
  return String(count);
}

type UiTranslator = (key: TranslationKey) => string;

function folderLabelForSidebar(systemName: string, t: UiTranslator) {
  if (systemName === "inbox") return t("mail.inbox");
  if (systemName === "sent") return t("mail.sent");
  if (systemName === "drafts") return t("mail.drafts");
  if (systemName === "spam") return t("mail.spam");
  if (systemName === "trash") return t("mail.trash");
  return t("mail.mailbox");
}

function folderLabelForHeader(systemName: string, fallback: string, t: UiTranslator) {
  if (systemName === "all") return t("mail.all");
  if (["inbox", "sent", "drafts", "spam", "trash"].includes(systemName)) {
    return folderLabelForSidebar(systemName, t);
  }
  return fallback || folderLabelForSidebar(systemName, t);
}

function isPrimaryMailboxFolder(systemName: string) {
  return systemName === "inbox" || systemName === "sent" || systemName === "drafts";
}

function isPersonalMailboxFolder(systemName: string, kind: "custom" | "system") {
  return kind === "custom";
}

function isSecondarySystemMailboxFolder(systemName: string) {
  return systemName === "spam" || systemName === "trash";
}

function searchFieldLabel(field: MailSearchField) {
  if (field === "correspondent") return "주고받은 메일";
  if (field === "from") return "보낸사람";
  if (field === "to") return "받는사람";
  if (field === "subject") return "제목";
  if (field === "body") return "내용";
  return "전체";
}

function sameNumberArray(left: number[], right: number[]) {
  if (left.length !== right.length) {
    return false;
  }

  for (let index = 0; index < left.length; index += 1) {
    if (left[index] !== right[index]) {
      return false;
    }
  }

  return true;
}

function buildMailPreviewDocument(bodyHtml: string, blockRemoteContent: boolean) {
  const imagePolicy = blockRemoteContent ? "img-src 'self' data:;" : "img-src 'self' https: data:;";
  const privacyStyles = blockRemoteContent
    ? "a[href]{pointer-events:none!important;color:inherit!important;text-decoration:none!important}img[src^='http'],img[src^='//']{display:none!important}"
    : "";
  return [
    "<!doctype html>",
    '<html><head><meta charset="utf-8">',
    '<meta name="viewport" content="width=device-width, initial-scale=1">',
    `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; base-uri 'none'; connect-src 'none'; font-src https: data:; form-action 'none'; frame-src 'none'; ${imagePolicy} media-src https: data:; object-src 'none'; script-src 'none'; style-src 'unsafe-inline' https:">`,
    `<style>html,body{overflow:hidden!important}${MAIL_QUOTE_BASE_CSS}${privacyStyles}</style>`,
    "</head>",
    `<body>${bodyHtml}</body>`,
    "</html>"
  ].join("");
}

function MailHtmlPreview({ blockRemoteContent, html }: { blockRemoteContent: boolean; html: string }) {
  const iframeRef = useRef<HTMLIFrameElement | null>(null);
  const cleanupRef = useRef<Array<() => void>>([]);
  const mutationObserverRef = useRef<MutationObserver | null>(null);
  const resizeObserverRef = useRef<ResizeObserver | null>(null);
  const [frameHeight, setFrameHeight] = useState(420);
  const frameDocument = buildMailPreviewDocument(html, blockRemoteContent);

  const clearFrameObservers = () => {
    cleanupRef.current.forEach((cleanup) => cleanup());
    cleanupRef.current = [];
    mutationObserverRef.current?.disconnect();
    mutationObserverRef.current = null;
    resizeObserverRef.current?.disconnect();
    resizeObserverRef.current = null;
  };

  const syncFrameHeight = () => {
    const iframe = iframeRef.current;
    const doc = iframe?.contentDocument;

    if (!iframe || !doc) {
      return;
    }

    const bodyHeight = Math.max(doc.body?.scrollHeight ?? 0, doc.body?.offsetHeight ?? 0);
    const documentHeight = Math.max(doc.documentElement?.scrollHeight ?? 0, doc.documentElement?.offsetHeight ?? 0);
    const nextHeight = Math.max(bodyHeight, documentHeight, 240);

    setFrameHeight((current) => (Math.abs(current - nextHeight) > 1 ? nextHeight : current));
  };

  const bindFrameObservers = useEffectEvent(() => {
    clearFrameObservers();
    syncFrameHeight();

    const iframe = iframeRef.current;
    const doc = iframe?.contentDocument;
    const win = iframe?.contentWindow;

    if (!iframe || !doc || !win) {
      return;
    }

    const scheduleHeightSync = (delay = 0) => {
      if (delay > 0) {
        const timeoutId = window.setTimeout(syncFrameHeight, delay);
        cleanupRef.current.push(() => window.clearTimeout(timeoutId));
        return;
      }

      const animationFrameId = window.requestAnimationFrame(syncFrameHeight);
      cleanupRef.current.push(() => window.cancelAnimationFrame(animationFrameId));
    };

    win.addEventListener("resize", syncFrameHeight);
    cleanupRef.current.push(() => win.removeEventListener("resize", syncFrameHeight));

    for (const image of Array.from(doc.images)) {
      image.addEventListener("load", syncFrameHeight);
      image.addEventListener("error", syncFrameHeight);
      cleanupRef.current.push(() => {
        image.removeEventListener("load", syncFrameHeight);
        image.removeEventListener("error", syncFrameHeight);
      });

      if (image.complete) {
        scheduleHeightSync();
      }
    }

    if (typeof ResizeObserver !== "undefined") {
      const observer = new ResizeObserver(() => syncFrameHeight());
      resizeObserverRef.current = observer;
      observer.observe(doc.documentElement);

      if (doc.body) {
        observer.observe(doc.body);
      }
    }

    if (typeof MutationObserver !== "undefined") {
      const observer = new MutationObserver(() => scheduleHeightSync());
      mutationObserverRef.current = observer;
      observer.observe(doc.documentElement, {
        attributes: true,
        childList: true,
        subtree: true
      });
    }

    void doc.fonts?.ready.then(() => scheduleHeightSync());
    scheduleHeightSync();
    scheduleHeightSync(80);
    scheduleHeightSync(320);
    scheduleHeightSync(1000);
  });

  useEffect(() => {
    const iframe = iframeRef.current;

    if (!iframe) {
      return;
    }

    const handleLoad = () => bindFrameObservers();
    const animationFrameId = window.requestAnimationFrame(() => {
      if (iframe.contentDocument?.readyState === "complete") {
        bindFrameObservers();
      }
    });

    iframe.addEventListener("load", handleLoad);

    return () => {
      window.cancelAnimationFrame(animationFrameId);
      iframe.removeEventListener("load", handleLoad);
      cleanupRef.current.forEach((cleanup) => cleanup());
      cleanupRef.current = [];
      mutationObserverRef.current?.disconnect();
      mutationObserverRef.current = null;
      resizeObserverRef.current?.disconnect();
      resizeObserverRef.current = null;
    };
  }, [frameDocument]);

  return (
    <iframe
      ref={iframeRef}
      title="메일 본문"
      srcDoc={frameDocument}
      sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin"
      referrerPolicy="no-referrer"
      scrolling="no"
      className="block min-h-[240px] w-full border-0 bg-white"
      style={{ height: `${frameHeight}px`, overflow: "hidden" }}
    />
  );
}

function buildComposeHref(input: {
  action: "forward" | "reply" | "replyAll";
  currentFolder: string;
  ownAddresses: string[];
  message: ComposeToolbarMessage | null;
}) {
  if (!input.message) {
    return "";
  }

  const params = new URLSearchParams();
  params.set("view", "compose");
  params.set("folder", input.currentFolder === "all" ? "inbox" : input.currentFolder);
  params.set("message", String(input.message.id));

  if (input.action === "reply") {
    params.set("composeMode", "reply");
    return `/mail?${params.toString()}`;
  }

  if (input.action === "replyAll") {
    params.set("composeMode", "replyAll");
    return `/mail?${params.toString()}`;
  }

  params.set("composeMode", "forward");
  return `/mail?${params.toString()}`;
}

function buildDirectComposeHref(currentFolder: string, toAddress: string) {
  const params = new URLSearchParams();
  params.set("view", "compose");
  params.set("folder", currentFolder === "all" ? "inbox" : currentFolder);
  params.set("to", toAddress.trim().toLowerCase());
  return `/mail?${params.toString()}`;
}

function buildFolderSearchParams(folder: string, options?: FolderSearchParamsOptions) {
  const params = new URLSearchParams();
  const searchQuery = options?.searchQuery?.trim() ?? "";

  params.set("folder", folder);

  if (options?.tab && options.tab !== "all") {
    params.set("tab", options.tab);
  }

  if (options?.page && options.page > 1) {
    params.set("page", String(options.page));
  }

  if (options?.pageSize && normalizeMailboxPageSize(options.pageSize) !== DEFAULT_MAILBOX_PAGE_SIZE) {
    params.set("pageSize", String(normalizeMailboxPageSize(options.pageSize)));
  }

  if (options?.sortOrder === "oldest") {
    params.set("sort", "oldest");
  }

  if (searchQuery) {
    params.set("q", searchQuery);
  }

  if (searchQuery && options?.searchField && options.searchField !== "all") {
    params.set("field", options.searchField);
  }

  return params;
}

function folderHref(folder: string, options?: FolderSearchParamsOptions) {
  return `/mail?${buildFolderSearchParams(folder, options).toString()}`;
}

function pageHref(
  folder: string,
  page: number,
  tab: MailMessageFilter,
  options?: Pick<FolderSearchParamsOptions, "pageSize" | "searchField" | "searchQuery" | "sortOrder">
) {
  return folderHref(folder, {
    page,
    pageSize: options?.pageSize,
    searchField: options?.searchField,
    searchQuery: options?.searchQuery,
    sortOrder: options?.sortOrder,
    tab
  });
}

function setupHref(section: MailSetupSection = "domain") {
  return `/mail?panel=setup&section=${section}`;
}

function autoSendHref() {
  return "/mail?panel=auto-send";
}

function shouldPreventMailRowOpen(target: EventTarget | null) {
  if (!(target instanceof HTMLElement)) {
    return false;
  }

  return Boolean(target.closest("[data-row-action-stop='true']"));
}

function shouldBypassClientNavigation(event: ReactMouseEvent<HTMLElement>) {
  return (
    event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey
  );
}

function messageHref(
  folder: string,
  messageId: number,
  detailView: "body" | "source" | "logs",
  options?: FolderSearchParamsOptions
) {
  const params = buildFolderSearchParams(folder, options);

  params.set("message", String(messageId));
  params.set("detail", detailView);

  return `/mail?${params.toString()}`;
}

function messageBodyHref(messageId: number) {
  const params = new URLSearchParams();
  params.set("message", String(messageId));
  return `/mail/message-body?${params.toString()}`;
}

function buildPaginationItems(currentPage: number, totalPages: number) {
  if (totalPages <= 1) {
    return [1];
  }

  const pages = new Set<number>([1, totalPages, currentPage]);

  for (let offset = 1; offset <= 1; offset += 1) {
    if (currentPage - offset > 1) {
      pages.add(currentPage - offset);
    }

    if (currentPage + offset < totalPages) {
      pages.add(currentPage + offset);
    }
  }

  const sortedPages = [...pages].sort((a, b) => a - b);
  const items: Array<number | "ellipsis"> = [];

  sortedPages.forEach((page, index) => {
    const previous = sortedPages[index - 1];

    if (previous && page - previous > 1) {
      items.push("ellipsis");
    }

    items.push(page);
  });

  return items;
}

function looksLikeAttachment(message: MailMessageSummary) {
  return /pdf|첨부|제안서|계약서|정리/i.test(`${message.subject} ${message.snippet}`);
}

function formatAttachmentSize(bytes: number) {
  if (!Number.isFinite(bytes) || bytes <= 0) {
    return "0B";
  }

  const units = ["B", "KB", "MB", "GB"];
  let value = bytes;
  let unitIndex = 0;

  while (value >= 1024 && unitIndex < units.length - 1) {
    value /= 1024;
    unitIndex += 1;
  }

  const fixed = value >= 10 || unitIndex === 0 ? value.toFixed(0) : value.toFixed(1);
  return `${fixed}${units[unitIndex]}`;
}

function attachmentIconAppearance(attachment: MailMessageAttachment) {
  const extension = attachment.extension.toLowerCase();
  const contentType = attachment.contentType.toLowerCase();

  if (contentType === "application/pdf" || extension === "pdf") {
    return { colorClass: "bg-[#ef513f]", path: mdiFilePdfBox };
  }

  if (extension === "hwp" || extension === "hwpx") {
    return { colorClass: "bg-[#35a6df]", path: mdiFileDocument };
  }

  if (["doc", "docx", "odt", "rtf"].includes(extension)) {
    return { colorClass: "bg-[#3478c9]", path: mdiFileWordBox };
  }

  if (["xls", "xlsx", "xlsm", "ods"].includes(extension)) {
    return { colorClass: "bg-[#24945f]", path: mdiFileExcelBox };
  }

  if (["ppt", "pptx", "pps", "ppsx", "odp"].includes(extension)) {
    return { colorClass: "bg-[#e06d36]", path: mdiFilePowerpointBox };
  }

  if (["zip", "7z", "rar", "tar", "gz", "bz2", "xz"].includes(extension)) {
    return { colorClass: "bg-[#9a7650]", path: mdiFolderZip };
  }

  if (contentType.startsWith("image/")) {
    return { colorClass: "bg-[#bd4f9c]", path: mdiFileImage };
  }

  if (contentType.startsWith("video/")) {
    return { colorClass: "bg-[#7159bd]", path: mdiFileVideo };
  }

  if (contentType.startsWith("audio/")) {
    return { colorClass: "bg-[#248aa8]", path: mdiFileMusic };
  }

  if (extension === "csv" || contentType === "text/csv") {
    return { colorClass: "bg-[#2c9b76]", path: mdiFileDelimited };
  }

  if (
    ["html", "htm", "css", "js", "jsx", "ts", "tsx", "json", "xml", "yaml", "yml", "sql"].includes(
      extension
    )
  ) {
    return { colorClass: "bg-[#586b89]", path: mdiFileCode };
  }

  if (contentType.startsWith("text/") || ["txt", "md", "log"].includes(extension)) {
    return { colorClass: "bg-[#71819a]", path: mdiFileDocument };
  }

  return { colorClass: "bg-[#6f86aa]", path: mdiFileOutline };
}

function isImageAttachmentPreview(attachment: MailMessageAttachment) {
  return attachment.contentType.toLowerCase().startsWith("image/");
}

function splitAttachmentFilename(filename: string) {
  const trimmed = filename.trim();
  const dotIndex = trimmed.lastIndexOf(".");

  if (dotIndex <= 0 || dotIndex === trimmed.length - 1) {
    return {
      extension: "",
      name: trimmed || "attachment"
    };
  }

  return {
    extension: trimmed.slice(dotIndex),
    name: trimmed.slice(0, dotIndex) || trimmed
  };
}

function previewBodyFromSummary(message: MailMessageSummary) {
  if (message.snippet.trim()) {
    return `${message.snippet.trim()}\n\n메일 상세를 열면 전체 원문과 전송 로그를 확인할 수 있습니다.`;
  }

  return "메일 상세를 열면 본문 전체를 확인할 수 있습니다.";
}

function aiSummaryStatusLabel(message: MailMessageSummary) {
  if (message.aiSummaryStatus === "failed") {
    return message.aiSummaryError ? `AI 릴레이 실패: ${message.aiSummaryError}` : "AI 릴레이 실패";
  }

  if (message.aiSummaryStatus === "sending") {
    return message.aiSummaryResponse ? `AI 릴레이 처리 중: ${message.aiSummaryResponse}` : "AI 릴레이 처리 중";
  }

  return "";
}

function createPreviewMessage(selectedMessage: MailMessageDetail | null, fallbackMessage: MailMessageSummary | null) {
  if (selectedMessage) {
    return {
      attachments: selectedMessage.attachments,
      bodyHtml: selectedMessage.bodyHtml,
      bodyHtmlDisplay: selectedMessage.bodyHtmlDisplay,
      bodyText: selectedMessage.bodyText,
      direction: selectedMessage.direction,
      fromAddress: selectedMessage.fromAddress,
      fromName: selectedMessage.fromName,
      senderContactName: selectedMessage.senderContactName,
      senderIsVip: selectedMessage.senderIsVip,
      id: selectedMessage.id,
      isRead: selectedMessage.isRead,
      isStarred: selectedMessage.isStarred,
      messageIdHeader: selectedMessage.messageIdHeader,
      rawSourceAvailable: selectedMessage.rawSourceAvailable,
      receivedAt: selectedMessage.receivedAt,
      snippet: selectedMessage.snippet,
      subject: selectedMessage.subject,
      toAddresses: selectedMessage.toAddresses,
      transportResponse: selectedMessage.transportResponse,
      transportStatus: selectedMessage.transportStatus
    } satisfies PreviewMessage;
  }

  if (!fallbackMessage) {
    return null;
  }

  return {
    attachments: [],
    bodyHtml: null,
    bodyHtmlDisplay: null,
    bodyText: previewBodyFromSummary(fallbackMessage),
    direction: fallbackMessage.direction,
    fromAddress: fallbackMessage.fromAddress,
    fromName: fallbackMessage.fromName,
    senderContactName: fallbackMessage.senderContactName,
    senderIsVip: fallbackMessage.senderIsVip,
    id: fallbackMessage.id,
    isRead: fallbackMessage.isRead,
    isStarred: fallbackMessage.isStarred,
    messageIdHeader: null,
    rawSourceAvailable: false,
    receivedAt: fallbackMessage.receivedAt,
    snippet: fallbackMessage.snippet,
    subject: fallbackMessage.subject,
    toAddresses: fallbackMessage.toAddresses,
    transportResponse: null,
    transportStatus: fallbackMessage.direction === "outbound" ? "sent" : "saved"
  } satisfies PreviewMessage;
}

function resolveMessageStarState(currentData: MailAppOverview | null, messageId: number) {
  const listMessage = currentData?.messages.find((message) => message.id === messageId);

  if (listMessage) {
    return listMessage.isStarred;
  }

  if (currentData?.selectedMessage?.id === messageId) {
    return currentData.selectedMessage.isStarred;
  }

  return null;
}

function buildOptimisticStarToggledMailData(
  currentData: MailAppOverview | null,
  messageId: number,
  nextStarred: boolean
) {
  if (!currentData) {
    return currentData;
  }

  const currentStarred = resolveMessageStarState(currentData, messageId);

  if (currentStarred === null || currentStarred === nextStarred) {
    return currentData;
  }

  const shouldRemoveFromImportant = currentData.messageFilter === "important" && !nextStarred;
  const nextMessages = shouldRemoveFromImportant
    ? currentData.messages.filter((message) => message.id !== messageId)
    : currentData.messages.map((message) =>
        message.id === messageId ? { ...message, isStarred: nextStarred } : message
      );
  const importantDelta = nextStarred ? 1 : -1;
  const nextImportantCount = Math.max(0, currentData.messageFilterCounts.important + importantDelta);
  const nextTotalMessages = shouldRemoveFromImportant
    ? Math.max(0, currentData.totalMessages - 1)
    : currentData.totalMessages;
  const nextTotalPages =
    shouldRemoveFromImportant && currentData.pageSize > 0
      ? Math.max(1, Math.ceil(nextTotalMessages / currentData.pageSize))
      : currentData.totalPages;
  const selectedMessageRemoved = shouldRemoveFromImportant && currentData.selectedMessage?.id === messageId;

  return {
    ...currentData,
    currentPage: Math.min(currentData.currentPage, nextTotalPages),
    messageFilterCounts: {
      ...currentData.messageFilterCounts,
      important: nextImportantCount
    },
    messages: nextMessages,
    selectedMessage:
      currentData.selectedMessage?.id !== messageId
        ? currentData.selectedMessage
        : selectedMessageRemoved
          ? null
          : { ...currentData.selectedMessage, isStarred: nextStarred },
    selectedMessageLogs: selectedMessageRemoved ? [] : currentData.selectedMessageLogs,
    totalMessages: nextTotalMessages,
    totalPages: nextTotalPages
  } satisfies MailAppOverview;
}

function buildOptimisticReadStateMailData(
  currentData: MailAppOverview | null,
  messageIds: number[],
  nextReadState: boolean
) {
  if (!currentData || messageIds.length === 0) {
    return currentData;
  }

  const messageIdSet = new Set(messageIds);
  const currentReadStateById = new Map<number, boolean>();
  const folderSystemNameById = new Map<number, string>();

  for (const message of currentData.messages) {
    if (!messageIdSet.has(message.id)) {
      continue;
    }

    currentReadStateById.set(message.id, message.isRead);
    folderSystemNameById.set(message.id, message.folderSystemName);
  }

  if (currentData.selectedMessage && messageIdSet.has(currentData.selectedMessage.id)) {
    currentReadStateById.set(currentData.selectedMessage.id, currentData.selectedMessage.isRead);
    folderSystemNameById.set(currentData.selectedMessage.id, currentData.selectedMessage.folderSystemName);
  }

  const changedMessageIds = [...messageIdSet].filter((messageId) => {
    const currentReadState = currentReadStateById.get(messageId);

    return typeof currentReadState === "boolean" && currentReadState !== nextReadState;
  });

  if (changedMessageIds.length === 0) {
    return currentData;
  }

  const changedMessageIdSet = new Set(changedMessageIds);
  const unreadDeltaByFolder = new Map<string, number>();
  let unreadDelta = 0;

  for (const messageId of changedMessageIds) {
    const delta = nextReadState ? -1 : 1;
    const folderSystemName = folderSystemNameById.get(messageId);

    unreadDelta += delta;

    if (folderSystemName) {
      unreadDeltaByFolder.set(folderSystemName, (unreadDeltaByFolder.get(folderSystemName) ?? 0) + delta);
    }
  }

  const shouldRemoveFromUnreadFilter = currentData.messageFilter === "unread" && nextReadState;
  const removedFromCurrentListCount = shouldRemoveFromUnreadFilter
    ? currentData.messages.filter((message) => changedMessageIdSet.has(message.id)).length
    : 0;
  const nextTotalMessages = shouldRemoveFromUnreadFilter
    ? Math.max(0, currentData.totalMessages - removedFromCurrentListCount)
    : currentData.totalMessages;
  const nextTotalPages =
    shouldRemoveFromUnreadFilter && currentData.pageSize > 0
      ? Math.max(1, Math.ceil(nextTotalMessages / currentData.pageSize))
      : currentData.totalPages;

  return {
    ...currentData,
    currentPage: Math.min(currentData.currentPage, nextTotalPages),
    folders: currentData.folders.map((folder) => {
      const folderUnreadDelta = unreadDeltaByFolder.get(folder.systemName) ?? 0;

      if (folderUnreadDelta === 0) {
        return folder;
      }

      return {
        ...folder,
        unreadCount: Math.max(0, folder.unreadCount + folderUnreadDelta)
      };
    }),
    messageFilterCounts: {
      ...currentData.messageFilterCounts,
      unread: Math.max(0, currentData.messageFilterCounts.unread + unreadDelta)
    },
    messages: shouldRemoveFromUnreadFilter
      ? currentData.messages.filter((message) => !changedMessageIdSet.has(message.id))
      : currentData.messages.map((message) =>
          changedMessageIdSet.has(message.id) ? { ...message, isRead: nextReadState } : message
        ),
    selectedMessage:
      currentData.selectedMessage && changedMessageIdSet.has(currentData.selectedMessage.id)
        ? { ...currentData.selectedMessage, isRead: nextReadState }
        : currentData.selectedMessage,
    totalMessages: nextTotalMessages,
    totalPages: nextTotalPages
  } satisfies MailAppOverview;
}

function IconBase({ children, className }: { children: ReactNode; className?: string }) {
  return (
    <svg
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="1.8"
      strokeLinecap="round"
      strokeLinejoin="round"
      className={className}
    >
      {children}
    </svg>
  );
}

function AttachmentFileIcon({ attachment }: { attachment: MailMessageAttachment }) {
  const appearance = attachmentIconAppearance(attachment);

  return (
    <span
      aria-hidden="true"
      className={`inline-flex h-6 w-6 shrink-0 items-center justify-center text-white ${appearance.colorClass}`}
    >
      <svg viewBox="0 0 24 24" className="h-[17px] w-[17px]" fill="currentColor">
        <path d={appearance.path} />
      </svg>
    </span>
  );
}

function LogoMark({ compact = false, responsive = false }: { compact?: boolean; responsive?: boolean }) {
  const wrapperClass = responsive ? "h-8 w-8 rounded-[10px]" : compact ? "h-8 w-8 rounded-[10px]" : "h-10 w-10";
  const iconClass = responsive ? "h-6 w-6" : compact ? "h-6 w-6" : "h-[22px] w-[22px]";

  return (
    <div className={`flex items-center justify-center ${wrapperClass}`}>
      <IconBase className={`${iconClass} text-[#2f6df6]`}>
        <rect x="3.5" y="5.5" width="17" height="13" rx="2.5" />
        <path d="m4.8 7 7.2 5.6L19.2 7" />
      </IconBase>
    </div>
  );
}

function MenuIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M4.5 7h15" />
      <path d="M4.5 12h15" />
      <path d="M4.5 17h15" />
    </IconBase>
  );
}

function SearchIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <circle cx="11" cy="11" r="5.6" />
      <path d="m19 19-3.6-3.6" />
    </IconBase>
  );
}

function RecentSearchIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M4 11a8 8 0 1 1 2.1 5.4" />
      <path d="M4 5v6h6" />
      <path d="M12 7.5v4.8l3 1.8" />
    </IconBase>
  );
}

function HelpIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <circle cx="12" cy="12" r="8" />
      <path d="M9.8 9.6a2.5 2.5 0 1 1 3.6 2.2c-.9.5-1.4 1-1.4 2" />
      <path d="M12 16.7h.01" />
    </IconBase>
  );
}

function SparkIcon({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" className={className}>
      <path
        d="m12 3 1.8 4.7L18.5 9.5l-4.7 1.8L12 16l-1.8-4.7L5.5 9.5l4.7-1.8z"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      <path
        d="m18.6 14.6.8 2.1 2.1.8-2.1.8-.8 2.1-.8-2.1-2.1-.8 2.1-.8z"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      <path
        d="m5.3 14.7.6 1.6 1.6.6-1.6.6-.6 1.6-.6-1.6-1.6-.6 1.6-.6z"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function ChevronDownIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="m7 10 5 5 5-5" />
    </IconBase>
  );
}

function MenuCheckIcon({ className }: { className?: string }) {
  return (
    <svg
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="2"
      strokeLinecap="round"
      strokeLinejoin="round"
      className={className}
    >
      <path d="m5 12 4.2 4.2L19 6.7" />
    </svg>
  );
}

function ComposeIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M4 16.5V20h3.5L18.7 8.8a1.8 1.8 0 0 0 0-2.6l-.9-.9a1.8 1.8 0 0 0-2.6 0z" />
      <path d="m13.8 5.7 4.5 4.5" />
    </IconBase>
  );
}

function InboxIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M4 7.8A1.8 1.8 0 0 1 5.8 6h12.4A1.8 1.8 0 0 1 20 7.8v8.4A1.8 1.8 0 0 1 18.2 18H5.8A1.8 1.8 0 0 1 4 16.2z" />
      <path d="M4 13h4l2 3h4l2-3h4" />
    </IconBase>
  );
}

function SentIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M4.5 11.5 19 5l-3 14-4.8-4.8L8 17z" />
    </IconBase>
  );
}

function DraftIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <rect x="5" y="4.5" width="14" height="15" rx="2.5" />
      <path d="m8 9 4 3 4-3" />
    </IconBase>
  );
}

function TrashIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M4.8 7h14.4" />
      <path d="m9 7 .6-1.6A1.8 1.8 0 0 1 11.3 4h1.4a1.8 1.8 0 0 1 1.7 1.4L15 7" />
      <path d="M6.5 7.5V18a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2V7.5" />
      <path d="M10 11v5M14 11v5" />
    </IconBase>
  );
}

function SpamIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M12 3.8 18.5 6v5.8c0 3.8-2.6 6.6-6.5 8.4-3.9-1.8-6.5-4.6-6.5-8.4V6z" />
      <path d="M12 9v3.4" />
      <path d="M12 16h.01" />
    </IconBase>
  );
}

function StarIcon({ className, filled = false }: { className?: string; filled?: boolean }) {
  return (
    <IconBase className={className}>
      <path
        d="m12 4 2.3 4.7 5.2.8-3.7 3.7.9 5.1L12 16l-4.7 2.3.9-5.1L4.5 9.5l5.2-.8z"
        fill={filled ? "currentColor" : "none"}
      />
    </IconBase>
  );
}

function FolderIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M4.5 7.5A1.5 1.5 0 0 1 6 6h3l1.7 2H18a1.5 1.5 0 0 1 1.5 1.5v6.8A1.7 1.7 0 0 1 17.8 18H6.2a1.7 1.7 0 0 1-1.7-1.7z" />
    </IconBase>
  );
}

function FileDrawerIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M4.5 7.5h15v11h-15z" />
      <path d="M7 4.5h10l2.5 3H4.5z" />
      <path d="M9 11.5h6" />
    </IconBase>
  );
}

function UserIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <circle cx="12" cy="8" r="3.2" />
      <path d="M6.5 18.2a5.8 5.8 0 0 1 11 0" />
    </IconBase>
  );
}

function GlobeIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <circle cx="12" cy="12" r="8" />
      <path d="M4.5 12h15" />
      <path d="M12 4a12 12 0 0 1 0 16" />
      <path d="M12 4a12 12 0 0 0 0 16" />
    </IconBase>
  );
}

function UsersIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M9 11a2.8 2.8 0 1 0-2.8-2.8A2.8 2.8 0 0 0 9 11Z" />
      <path d="M15.5 10.3a2.3 2.3 0 1 0-2.3-2.3 2.3 2.3 0 0 0 2.3 2.3Z" />
      <path d="M4.8 18a4.8 4.8 0 0 1 8.4-2.9" />
      <path d="M14 18a4 4 0 0 1 5.2-2.6" />
    </IconBase>
  );
}

function LockIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <rect x="6.2" y="10.4" width="11.6" height="8.1" rx="2.2" />
      <path d="M8.8 10.4V8.8a3.2 3.2 0 0 1 6.4 0v1.6" />
      <path d="M12 13.7v1.7" />
    </IconBase>
  );
}

function BillingIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <rect x="4" y="6" width="16" height="12" rx="2.6" />
      <path d="M4 10h16" />
      <path d="M8 14h3" />
    </IconBase>
  );
}

function IntegrationIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M8.4 8.3 6.7 6.6a2.7 2.7 0 0 0-3.8 3.8l2.5 2.5a2.7 2.7 0 0 0 3.8 0l1.4-1.4" />
      <path d="m15.6 15.7 1.7 1.7a2.7 2.7 0 0 0 3.8-3.8l-2.5-2.5a2.7 2.7 0 0 0-3.8 0l-1.4 1.4" />
      <path d="m8.6 15.4 6.8-6.8" />
    </IconBase>
  );
}

function ScheduleIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <rect x="4.5" y="5.5" width="15" height="14" rx="3" />
      <path d="M8 3.8v3.3" />
      <path d="M16 3.8v3.3" />
      <path d="M4.5 10h15" />
      <path d="m10.2 14.5 1.5 1.5 3.1-3.4" />
    </IconBase>
  );
}

function LogIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M7 4.8h7l3 3V19a1.8 1.8 0 0 1-1.8 1.8H7A1.8 1.8 0 0 1 5.2 19V6.6A1.8 1.8 0 0 1 7 4.8Z" />
      <path d="M14 4.8v3.5h3.5" />
      <path d="M8.5 12h7" />
      <path d="M8.5 15.5h5.5" />
    </IconBase>
  );
}

function LogoutIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M10 4.8H7.2A1.8 1.8 0 0 0 5.4 6.6v10.8a1.8 1.8 0 0 0 1.8 1.8H10" />
      <path d="M14 8.2 18 12l-4 3.8" />
      <path d="M9.2 12H18" />
    </IconBase>
  );
}

function RefreshIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M20 11.5A8 8 0 0 0 6.4 5.9L4 8.3" />
      <path d="M4 4.6v3.7h3.7" />
      <path d="M4 12.5A8 8 0 0 0 17.6 18l2.4-2.4" />
      <path d="M20 19.4v-3.7h-3.7" />
    </IconBase>
  );
}

function MoreIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <circle cx="12" cy="6" r="1" fill="currentColor" stroke="none" />
      <circle cx="12" cy="12" r="1" fill="currentColor" stroke="none" />
      <circle cx="12" cy="18" r="1" fill="currentColor" stroke="none" />
    </IconBase>
  );
}

function BackIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="m15 6-6 6 6 6" />
    </IconBase>
  );
}

function FilterIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M4.5 6h15" />
      <path d="M7.5 12h9" />
      <path d="M10.5 18h3" />
    </IconBase>
  );
}

function ListOnlyViewIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <rect x="5" y="6" width="14" height="3" rx="1.2" />
      <rect x="5" y="10.5" width="14" height="3" rx="1.2" />
      <rect x="5" y="15" width="14" height="3" rx="1.2" />
    </IconBase>
  );
}

function SplitHorizontalViewIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <rect x="4.5" y="5" width="5.5" height="14" rx="1.4" />
      <rect x="11.5" y="5" width="8" height="14" rx="1.4" />
    </IconBase>
  );
}

function SplitVerticalViewIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <rect x="5" y="5" width="14" height="5.5" rx="1.4" />
      <rect x="5" y="12" width="14" height="7" rx="1.4" />
    </IconBase>
  );
}

function GripIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <circle cx="9" cy="7" r="1" fill="currentColor" stroke="none" />
      <circle cx="15" cy="7" r="1" fill="currentColor" stroke="none" />
      <circle cx="9" cy="12" r="1" fill="currentColor" stroke="none" />
      <circle cx="15" cy="12" r="1" fill="currentColor" stroke="none" />
      <circle cx="9" cy="17" r="1" fill="currentColor" stroke="none" />
      <circle cx="15" cy="17" r="1" fill="currentColor" stroke="none" />
    </IconBase>
  );
}

function ReplyIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="m10 8-5 4 5 4" />
      <path d="M5 12h9a5 5 0 0 1 5 5" />
    </IconBase>
  );
}

function ReplyAllIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="m9 8-5 4 5 4" />
      <path d="m13 8-5 4 5 4" />
      <path d="M8 12h6a5 5 0 0 1 5 5" />
    </IconBase>
  );
}

function ForwardIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="m14 8 5 4-5 4" />
      <path d="M19 12h-9a5 5 0 0 0-5 5" />
    </IconBase>
  );
}

function ExternalIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M14 5h5v5" />
      <path d="M19 5 9 15" />
      <path d="M11 5H7a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-4" />
    </IconBase>
  );
}

function DownloadIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M12 4v10" />
      <path d="m8.5 10.5 3.5 3.5 3.5-3.5" />
      <path d="M5 18.5h14" />
    </IconBase>
  );
}

function CloseIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="m7 7 10 10" />
      <path d="m17 7-10 10" />
    </IconBase>
  );
}

function PlusIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M12 5v14" />
      <path d="M5 12h14" />
    </IconBase>
  );
}

function PaperclipIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="m9.5 13.5 5.2-5.1a2.6 2.6 0 1 1 3.6 3.7l-7 6.9a4.2 4.2 0 0 1-6-6l7.4-7.3" />
    </IconBase>
  );
}

function SpinnerIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <circle cx="12" cy="12" r="7.2" className="opacity-20" />
      <path d="M12 4.8a7.2 7.2 0 0 1 7.2 7.2" />
    </IconBase>
  );
}

function AlertIcon({ className }: { className?: string }) {
  return (
    <IconBase className={className}>
      <path d="M12 8v4" />
      <path d="M12 16h.01" />
      <path d="M10.2 4.7 3.5 16.3A2 2 0 0 0 5.2 19h13.6a2 2 0 0 0 1.7-2.7L13.8 4.7a2 2 0 0 0-3.6 0Z" />
    </IconBase>
  );
}

function MailRowIcon({ className, unread }: { className?: string; unread: boolean }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" className={className}>
      <rect x="4.5" y="6" width="15" height="12" rx="2.4" stroke="currentColor" strokeWidth="1.7" />
      <path
        d="m5.8 7.4 6.2 4.8 6.2-4.8"
        stroke="currentColor"
        strokeWidth="1.7"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      {unread ? <circle cx="18.5" cy="7" r="2.2" fill="currentColor" stroke="none" /> : null}
    </svg>
  );
}

function iconForNav(type: NavItem["icon"], className: string) {
  if (type === "inbox") return <InboxIcon className={className} />;
  if (type === "sent") return <SentIcon className={className} />;
  if (type === "drafts") return <DraftIcon className={className} />;
  if (type === "trash") return <TrashIcon className={className} />;
  if (type === "spam") return <SpamIcon className={className} />;
  if (type === "star") return <StarIcon className={className} />;
  if (type === "folder") return <FolderIcon className={className} />;
  return <InboxIcon className={className} />;
}

function iconForSetupNav(section: MailSetupSection | "logout", className: string) {
  if (section === "account") return <UserIcon className={className} />;
  if (section === "contacts") return <UsersIcon className={className} />;
  if (section === "classification") return <FilterIcon className={className} />;
  if (section === "spam") return <SpamIcon className={className} />;
  if (section === "storage") return <FolderIcon className={className} />;
  if (section === "domain") return <GlobeIcon className={className} />;
  if (section === "assistant") return <SparkIcon className={className} />;
  if (section === "integrations") return <IntegrationIcon className={className} />;
  if (section === "members") return <UsersIcon className={className} />;
  if (section === "billing") return <BillingIcon className={className} />;
  if (section === "language") return <GlobeIcon className={className} />;
  if (section === "logout") return <LogoutIcon className={className} />;
  return <LogIcon className={className} />;
}

const SIDEBAR_MENU_ICON_CLASS = "h-[18px] w-[18px] shrink-0 text-[#303038]";
const SIDEBAR_MENU_TEXT_GAP_CLASS = "gap-[5px]";
const VIEW_MODE_LABEL: Record<MailListViewMode, string> = {
  "list-only": "메일 목록만 보기",
  "split-horizontal": "좌우 분할로 보기",
  "split-vertical": "상하 분할로 보기"
};
const VIEW_MODE_OPTIONS: readonly MailListViewMode[] = ["list-only", "split-horizontal", "split-vertical"];
const FILTER_OPTIONS = [
  { id: "all", label: "전체" },
  { id: "unread", label: "안읽음" },
  { id: "important", label: "중요" },
  { id: "attachments", label: "첨부" }
] as const;

const MAIL_SORT_OPTIONS: Array<{ label: string; value: MailListSortOrder }> = [
  { label: "최신순", value: "latest" },
  { label: "오래된순", value: "oldest" }
];

function iconForViewMode(mode: MailListViewMode, className: string) {
  if (mode === "list-only") return <ListOnlyViewIcon className={className} />;
  if (mode === "split-horizontal") return <SplitHorizontalViewIcon className={className} />;
  return <SplitVerticalViewIcon className={className} />;
}

function circleIconButtonClass() {
  return "inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-full border border-transparent bg-transparent text-[#72829f] transition-all duration-200 hover:bg-white/70 hover:text-[#2d3d5c] hover:backdrop-blur-xl hover:backdrop-saturate-150";
}

function detailHeaderActionClass() {
  return "inline-flex h-10 w-10 cursor-pointer items-center justify-center rounded-full text-[#7b8ba7] transition-all duration-200 hover:-translate-y-0.5 hover:bg-[#f4f7fc] hover:text-[#2f6df6] hover:shadow-[0_10px_24px_rgba(25,38,72,0.08)]";
}

function mailboxBulkActionClass(options?: { enabled?: boolean; gapClassName?: string }) {
  const enabled = options?.enabled ?? true;
  const gapClassName = options?.gapClassName ? ` ${options.gapClassName}` : "";

  return `inline-flex h-7 items-center rounded-[10px] transition-colors${gapClassName} ${
    enabled
      ? "cursor-pointer text-[#5e6b81] hover:text-[#2f6df6]"
      : "cursor-not-allowed pointer-events-none text-[#c1c9d7]"
  }`;
}

function toolbarMenuButtonClass(active = false, options?: { mobileIconOnly?: boolean }) {
  const mobileIconOnlyClass = options?.mobileIconOnly ? "w-7 justify-center px-0 xl:w-auto xl:px-2.5" : "px-2.5";

  return `inline-flex h-7 cursor-pointer items-center gap-2 rounded-full ${mobileIconOnlyClass} text-[14px] transition-colors ${
    active ? "bg-[#eef4ff] text-[#2f6df6]" : "text-[#5f6f89] hover:bg-[#f4f7fc] hover:text-[#2f6df6]"
  }`;
}

function toolbarTextMenuButtonClass(active = false) {
  return `inline-flex h-7 shrink-0 cursor-pointer appearance-none items-center gap-1.5 border-0 bg-transparent px-0 text-[14px] font-medium leading-none outline-none transition-colors duration-150 focus-visible:outline-none focus-visible:ring-0 ${
    active ? "text-[#2f6df6]" : "text-[#5f6f89] hover:text-[#2f6df6]"
  }`;
}

function settingsMenuItemClass(active = false, disabled = false) {
  return `group flex w-full items-center gap-3 rounded-[16px] px-3 py-2 text-left text-[14px] transition-all duration-200 ${
    disabled
      ? "cursor-not-allowed pointer-events-none text-[#9aa8bf]"
      : active
        ? "bg-[#edf4ff] font-semibold text-[#2f6df6]"
        : "cursor-pointer text-[#465977] hover:bg-[#f7faff] hover:text-[#2f6df6]"
  }`;
}

function settingsMenuButtonClass(active = false, disabled = false) {
  return `group flex w-full appearance-none items-center gap-3 rounded-[16px] border-0 bg-transparent px-3 py-2 text-left text-[14px] transition-all duration-200 ${
    disabled
      ? "cursor-not-allowed pointer-events-none text-[#9aa8bf]"
      : active
        ? "bg-[#edf4ff] font-semibold text-[#2f6df6]"
        : "cursor-pointer text-[#465977] hover:bg-[#f7faff] hover:text-[#2f6df6]"
  }`;
}

function settingsMenuItemIconClass(active = false, disabled = false) {
  return `h-[18px] w-[18px] shrink-0 transition-colors duration-200 ${
    disabled ? "text-[#aab6c9]" : active ? "text-[#2f6df6]" : "text-[#7a8ba7] group-hover:text-[#2f6df6]"
  }`;
}

function popoverMenuSurfaceClass(extraClassName = "") {
  return `overflow-hidden rounded-[22px] border border-[#dfe8f8] bg-white p-2.5 text-[14px] shadow-[0_28px_60px_rgba(18,32,65,0.16)] ${extraClassName}`.trim();
}

function popoverMenuItemClass(options?: { active?: boolean; disabled?: boolean; gapClassName?: string }) {
  const active = options?.active ?? false;
  const disabled = options?.disabled ?? false;
  const gapClassName = options?.gapClassName ? ` ${options.gapClassName}` : "";

  return `group flex w-full appearance-none items-center rounded-[16px] border-0 bg-transparent px-3 py-2 text-left text-[14px] transition-all duration-200${gapClassName} ${
    disabled
      ? "cursor-not-allowed text-[#b8c3d5]"
      : active
        ? "bg-[#edf4ff] font-semibold text-[#2f6df6]"
        : "cursor-pointer text-[#465977] hover:bg-[#f7faff] hover:text-[#2f6df6]"
  }`;
}

function viewModeMenuItemClass(active = false) {
  return `group flex w-full appearance-none items-center gap-3 rounded-[16px] border-0 bg-transparent px-3 py-2 text-left text-[14px] transition-all duration-200 whitespace-nowrap pr-4 ${
    active
      ? "cursor-default bg-[#edf4ff] font-semibold text-[#2f6df6]"
      : "cursor-pointer text-[#465977] hover:bg-[#f7faff] hover:text-[#2f6df6]"
  }`;
}

function ActionTooltip({ children, label }: { children: ReactNode; label: string }) {
  const anchorRef = useRef<HTMLSpanElement | null>(null);
  const [tooltipPosition, setTooltipPosition] = useState<{
    left: number;
    placement: "bottom" | "top";
    top: number;
  } | null>(null);

  const showTooltip = () => {
    const anchor = anchorRef.current;

    if (!anchor) {
      return;
    }

    const rect = anchor.getBoundingClientRect();
    const placement = rect.top > 48 ? "top" : "bottom";

    setTooltipPosition({
      left: rect.left + rect.width / 2,
      placement,
      top: placement === "top" ? rect.top - 10 : rect.bottom + 10
    });
  };

  const hideTooltip = () => {
    setTooltipPosition(null);
  };

  useEffect(() => {
    if (!tooltipPosition) {
      return;
    }

    const dismissTooltip = () => {
      setTooltipPosition(null);
    };

    window.addEventListener("resize", dismissTooltip);
    window.addEventListener("scroll", dismissTooltip, true);

    return () => {
      window.removeEventListener("resize", dismissTooltip);
      window.removeEventListener("scroll", dismissTooltip, true);
    };
  }, [tooltipPosition]);

  return (
    <span
      ref={anchorRef}
      className="inline-flex"
      onBlur={hideTooltip}
      onFocus={showTooltip}
      onMouseEnter={showTooltip}
      onMouseLeave={hideTooltip}
    >
      {children}
      {tooltipPosition && typeof document !== "undefined"
        ? createPortal(
            <span
              role="tooltip"
              className={`pointer-events-none fixed z-[9999] flex -translate-x-1/2 flex-col items-center ${
                tooltipPosition.placement === "top" ? "-translate-y-full" : ""
              }`}
              style={{
                left: tooltipPosition.left,
                top: tooltipPosition.top
              }}
            >
              {tooltipPosition.placement === "bottom" ? (
                <span className="-mb-px h-2 w-2 rotate-45 bg-[#182742]" />
              ) : null}
              <span className="whitespace-nowrap rounded-[10px] bg-[#182742] px-3 py-1.5 text-[11px] font-medium leading-none text-white shadow-[0_14px_34px_rgba(24,39,66,0.24)]">
                {label}
              </span>
              {tooltipPosition.placement === "top" ? <span className="-mt-px h-2 w-2 rotate-45 bg-[#182742]" /> : null}
            </span>,
            document.body
          )
        : null}
    </span>
  );
}

function DraggableHorizontalScroll({
  children,
  className,
  innerClassName
}: {
  children: ReactNode;
  className?: string;
  innerClassName?: string;
}) {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const dragStateRef = useRef<{
    moved: boolean;
    pointerId: number;
    startScrollLeft: number;
    startX: number;
  } | null>(null);
  const suppressClickRef = useRef(false);
  const [dragging, setDragging] = useState(false);

  const isInteractiveDragTarget = (target: EventTarget | null) =>
    target instanceof HTMLElement &&
    Boolean(
      target.closest(
        [
          "a",
          "button",
          "input",
          "label",
          "select",
          "textarea",
          "[role='button']",
          "[data-draggable-scroll-ignore='true']",
          "[data-move-menu-root='true']",
          "[data-toolbar-menu-root='true']"
        ].join(",")
      )
    );

  const finishDrag = () => {
    if (dragStateRef.current?.moved) {
      suppressClickRef.current = true;
      window.setTimeout(() => {
        suppressClickRef.current = false;
      }, 0);
    }

    dragStateRef.current = null;
    setDragging(false);
  };

  const handlePointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {
    if (event.pointerType === "touch" || event.button !== 0) {
      return;
    }

    if (isInteractiveDragTarget(event.target)) {
      return;
    }

    const container = containerRef.current;

    if (!container || container.scrollWidth <= container.clientWidth) {
      return;
    }

    dragStateRef.current = {
      moved: false,
      pointerId: event.pointerId,
      startScrollLeft: container.scrollLeft,
      startX: event.clientX
    };
    container.setPointerCapture(event.pointerId);
  };

  const handlePointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {
    const dragState = dragStateRef.current;
    const container = containerRef.current;

    if (!dragState || !container || dragState.pointerId !== event.pointerId) {
      return;
    }

    const deltaX = event.clientX - dragState.startX;

    if (!dragState.moved && Math.abs(deltaX) < 6) {
      return;
    }

    if (!dragState.moved) {
      dragState.moved = true;
      setDragging(true);
    }

    container.scrollLeft = dragState.startScrollLeft - deltaX;
    event.preventDefault();
  };

  const handlePointerUp = (event: ReactPointerEvent<HTMLDivElement>) => {
    if (dragStateRef.current?.pointerId !== event.pointerId) {
      return;
    }

    finishDrag();
  };

  const handlePointerCancel = (event: ReactPointerEvent<HTMLDivElement>) => {
    if (dragStateRef.current?.pointerId !== event.pointerId) {
      return;
    }

    finishDrag();
  };

  const handleClickCapture = (event: ReactMouseEvent<HTMLDivElement>) => {
    if (!suppressClickRef.current) {
      return;
    }

    suppressClickRef.current = false;
    event.preventDefault();
    event.stopPropagation();
  };

  return (
    <div
      ref={containerRef}
      onPointerDown={handlePointerDown}
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
      onPointerCancel={handlePointerCancel}
      onClickCapture={handleClickCapture}
      className={`${className ?? ""} ${dragging ? "cursor-grabbing select-none" : "cursor-grab"}`.trim()}
    >
      <div className={innerClassName}>{children}</div>
    </div>
  );
}

function ToolbarPortalMenu({
  align = "left",
  anchorElement = null,
  anchorRef,
  children,
  className,
  open,
  rootKind = "toolbar"
}: {
  align?: "left" | "right";
  anchorElement?: HTMLElement | null;
  anchorRef?: RefObject<HTMLElement | null>;
  children: ReactNode;
  className: string;
  open: boolean;
  rootKind?: "move" | "toolbar" | "detail";
}) {
  const menuRef = useRef<HTMLDivElement>(null);

  useLayoutEffect(() => {
    if (!open) {
      return;
    }

    const updatePosition = () => {
      const anchor = anchorElement ?? anchorRef?.current;

      const menu = menuRef.current;
      if (!anchor || !menu) {
        return;
      }

      const rect = anchor.getBoundingClientRect();
      const position = toolbarMenuPosition(
        rect,
        { width: menu.offsetWidth, height: menu.scrollHeight },
        {
          width: document.documentElement.clientWidth,
          height: window.innerHeight
        },
        align
      );
      Object.assign(menu.style, {
        position: "fixed",
        left: `${position.left}px`,
        top: `${position.top}px`,
        maxHeight: `${position.maxHeight}px`,
        maxWidth: `${position.maxWidth}px`,
        overflowY: "auto",
        overscrollBehavior: "contain",
        visibility: "visible"
      });
    };

    updatePosition();
    window.addEventListener("resize", updatePosition);
    window.addEventListener("scroll", updatePosition, true);
    const observer = new ResizeObserver(updatePosition);
    if (menuRef.current) observer.observe(menuRef.current);

    return () => {
      window.removeEventListener("resize", updatePosition);
      window.removeEventListener("scroll", updatePosition, true);
      observer.disconnect();
    };
  }, [align, anchorElement, anchorRef, open]);

  if (!open) {
    return null;
  }

  return createPortal(
    <div
      ref={menuRef}
      {...(rootKind === "move"
        ? { "data-move-menu-root": "true" }
        : rootKind === "detail"
          ? { "data-detail-menu-root": "true" }
          : { "data-toolbar-menu-root": "true" })}
      className={className}
      style={{ position: "fixed", visibility: "hidden" }}
    >
      {children}
    </div>,
    document.body
  );
}

function MoveMenuFieldset({ children }: { children: ReactNode }) {
  return <fieldset className="min-w-0">{children}</fieldset>;
}

function MoveMenuContinueButton({ onClick }: { onClick: () => void }) {
  return (
    <button
      type="button"
      onClick={onClick}
      className="inline-flex h-10 flex-1 cursor-pointer items-center justify-center rounded-[14px] border border-[#dbe5f7] bg-white px-4 text-[13px] font-semibold text-[#344767] transition-all duration-200 hover:bg-[#f7faff] hover:text-[#2f6df6]"
    >
      계속 이동
    </button>
  );
}

function ContinueMoveDialog({
  includeExisting,
  onCancel,
  onConfirm,
  onIncludeExistingChange,
  pending,
  prompt
}: {
  includeExisting: boolean;
  onCancel: () => void;
  onConfirm: () => void;
  onIncludeExistingChange: (value: boolean) => void;
  pending: boolean;
  prompt: ContinueMovePromptState | null;
}) {
  const { tr } = useI18n();

  if (!prompt || typeof document === "undefined") {
    return null;
  }

  return createPortal(
    <div
      data-move-menu-root="true"
      className="fixed inset-0 z-[12000] flex items-center justify-center bg-[rgba(15,23,42,0.28)] px-4 backdrop-blur-[2px]"
      role="presentation"
      onMouseDown={(event) => {
        if (!pending && event.currentTarget === event.target) onCancel();
      }}
    >
      <section
        role="dialog"
        aria-modal="true"
        aria-busy={pending}
        aria-labelledby="continue-move-title"
        className="relative w-full max-w-[410px] overflow-hidden rounded-[16px] bg-white px-6 py-6 shadow-[0_28px_70px_rgba(20,31,55,0.24)]"
      >
        <h2 id="continue-move-title" className="text-[18px] font-semibold tracking-[-0.03em] text-[#1c2940]">
          {tr("메일 계속 이동")}
        </h2>
        <p className="mt-5 text-[14px] leading-6 text-[#465671]">
          {tr("동일한 주소에서 메일이 올 경우 {folder}(으)로 계속 이동합니다.").replace("{folder}", prompt.targetLabel)}
        </p>
        <p className="mt-1 break-all text-[12px] text-[#8290a7]">{prompt.senderAddress}</p>
        <label className="mt-5 flex cursor-pointer items-center gap-2.5 text-[13px] font-medium text-[#4c5d78]">
          <input
            type="checkbox"
            checked={includeExisting}
            onChange={(event) => onIncludeExistingChange(event.target.checked)}
            className="h-4 w-4 cursor-pointer rounded-[4px] border-[#cad5e6] accent-[#2f6df6]"
          />
          {tr("이미 받은 메일도 같이 이동")}
        </label>
        <div className="mt-6 flex justify-end gap-2">
          <button
            type="button"
            onClick={onCancel}
            className="h-9 cursor-pointer rounded-[9px] border border-[#dfe6f1] bg-white px-4 text-[13px] font-semibold text-[#5a6980] transition-colors hover:bg-[#f7f9fc]"
          >
            {tr("취소")}
          </button>
          <button
            type="button"
            onClick={onConfirm}
            className="h-9 cursor-pointer rounded-[9px] bg-[#2f6df6] px-4 text-[13px] font-semibold text-white transition-colors hover:bg-[#245de3]"
          >
            {tr("확인")}
          </button>
        </div>
        {pending ? (
          <div className="absolute inset-0 z-10 flex items-center justify-center bg-white/78 backdrop-blur-[1px]">
            <span className="inline-flex items-center gap-2 rounded-full bg-white px-4 py-2 text-[13px] font-semibold text-[#2f6df6] shadow-[0_10px_30px_rgba(39,78,150,0.14)]">
              <SpinnerIcon className="h-4 w-4 animate-spin" />
              {tr("처리 중...")}
            </span>
          </div>
        ) : null}
      </section>
    </div>,
    document.body
  );
}

function ContinueMoveCompletionDialog({ onClose, open }: { onClose: () => void; open: boolean }) {
  const { tr } = useI18n();

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

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

    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [onClose, open]);

  if (!open || typeof document === "undefined") return null;

  return createPortal(
    <div
      className="fixed inset-0 z-[12100] flex items-center justify-center bg-[rgba(15,23,42,0.28)] px-4 backdrop-blur-[2px]"
      role="presentation"
      onMouseDown={(event) => {
        if (event.currentTarget === event.target) onClose();
      }}
    >
      <section
        role="dialog"
        aria-modal="true"
        aria-labelledby="continue-move-completion-message"
        className="w-full max-w-[446px] rounded-[12px] bg-white px-7 pb-7 pt-8 text-center shadow-[0_28px_70px_rgba(20,31,55,0.24)]"
      >
        <span
          aria-hidden="true"
          className="mx-auto inline-flex h-9 w-9 items-center justify-center rounded-full bg-[#ffedef] text-[19px] font-semibold text-[#ff4f63]"
        >
          !
        </span>
        <p
          id="continue-move-completion-message"
          className="mx-auto mt-5 max-w-[360px] text-[17px] font-medium leading-[1.55] tracking-[-0.025em] text-[#252c38]"
        >
          {tr("요청이 완료되었습니다. 정리할 메일의 개수에 따라 처리 시간이 길어질 수 있습니다.")}
        </p>
        <button
          type="button"
          onClick={onClose}
          className="mt-5 h-9 cursor-pointer rounded-[7px] bg-[#2f6df6] px-5 text-[14px] font-semibold text-white transition-colors hover:bg-[#245de3]"
        >
          {tr("확인")}
        </button>
      </section>
    </div>,
    document.body
  );
}

function ContinueMoveReplacementDialog({
  onCancel,
  onConfirm,
  onIncludeExistingChange,
  pending,
  prompt
}: {
  onCancel: () => void;
  onConfirm: () => void;
  onIncludeExistingChange: (value: boolean) => void;
  pending: boolean;
  prompt: ContinueMoveReplacementPromptState | null;
}) {
  const { tr } = useI18n();

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

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape" && !pending) onCancel();
    };

    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [onCancel, pending, prompt]);

  if (!prompt || typeof document === "undefined") return null;

  return createPortal(
    <div
      data-move-menu-root="true"
      className="fixed inset-0 z-[12050] flex items-center justify-center bg-[rgba(15,23,42,0.28)] px-4 backdrop-blur-[2px]"
      role="presentation"
      onMouseDown={(event) => {
        if (!pending && event.currentTarget === event.target) onCancel();
      }}
    >
      <section
        role="dialog"
        aria-modal="true"
        aria-busy={pending}
        aria-labelledby="replace-continue-move-title"
        className="relative w-full max-w-[410px] overflow-hidden rounded-[16px] bg-white px-6 py-6 shadow-[0_28px_70px_rgba(20,31,55,0.24)]"
      >
        <h2 id="replace-continue-move-title" className="text-[18px] font-semibold tracking-[-0.03em] text-[#1c2940]">
          {tr("메일 계속 이동")}
        </h2>
        <p className="mt-5 text-[14px] leading-6 text-[#465671]">
          {tr("동일한 자동분류 항목이 있습니다. 이전 항목을 삭제하고 현재 설정을 등록하시겠습니까?")}
        </p>
        <div className="mt-3 rounded-[10px] bg-[#f7f9fc] px-3 py-2 text-[12px] leading-5 text-[#718198]">
          <span className="break-all">{prompt.senderAddress}</span>
          <span className="mx-1.5">·</span>
          <span>{prompt.existingTargetLabel}</span>
          <span className="mx-1">→</span>
          <span className="font-semibold text-[#2f6df6]">{prompt.targetLabel}</span>
        </div>
        <label className="mt-4 flex cursor-pointer items-center gap-2.5 text-[13px] font-medium text-[#4c5d78]">
          <input
            type="checkbox"
            checked={prompt.includeExisting}
            onChange={(event) => onIncludeExistingChange(event.target.checked)}
            className="h-4 w-4 cursor-pointer rounded-[4px] border-[#cad5e6] accent-[#2f6df6]"
          />
          {tr("이미 받은 메일도 같이 이동")}
        </label>
        <Link
          href="/mail?panel=setup&section=classification"
          prefetch={false}
          onClick={onCancel}
          className="mt-4 inline-flex cursor-pointer text-[12px] font-medium text-[#7b8799] underline-offset-4 hover:text-[#2f6df6] hover:underline"
        >
          {tr("자동분류 설정 보기")}
          <span aria-hidden="true">›</span>
        </Link>
        <div className="mt-5 flex justify-end gap-2">
          <button
            type="button"
            onClick={onCancel}
            className="h-9 cursor-pointer rounded-[9px] border border-[#dfe6f1] bg-white px-4 text-[13px] font-semibold text-[#5a6980] transition-colors hover:bg-[#f7f9fc]"
          >
            {tr("취소")}
          </button>
          <button
            type="button"
            onClick={onConfirm}
            className="h-9 cursor-pointer rounded-[9px] bg-[#2f6df6] px-4 text-[13px] font-semibold text-white transition-colors hover:bg-[#245de3]"
          >
            {tr("확인")}
          </button>
        </div>
        {pending ? (
          <div className="absolute inset-0 z-10 flex items-center justify-center bg-white/78 backdrop-blur-[1px]">
            <span className="inline-flex items-center gap-2 rounded-full bg-white px-4 py-2 text-[13px] font-semibold text-[#2f6df6] shadow-[0_10px_30px_rgba(39,78,150,0.14)]">
              <SpinnerIcon className="h-4 w-4 animate-spin" />
              {tr("처리 중...")}
            </span>
          </div>
        ) : null}
      </section>
    </div>,
    document.body
  );
}

function SenderRulePromptFieldset({ children }: { children: ReactNode }) {
  const { pending } = useFormStatus();

  return (
    <fieldset
      disabled={pending}
      className={`min-w-0 transition-opacity duration-200 ${
        pending ? "pointer-events-none opacity-60" : "opacity-100"
      }`}
    >
      {children}
    </fieldset>
  );
}

function SenderRulePromptSubmitButton({
  label,
  pendingLabel,
  variant,
  name,
  value
}: {
  label: string;
  pendingLabel: string;
  variant: "primary" | "secondary";
  name?: string;
  value?: string;
}) {
  const { pending } = useFormStatus();
  const baseClassName =
    "inline-flex h-10 items-center justify-center gap-2 rounded-[14px] px-4 text-[13px] font-semibold transition-all duration-200 disabled:cursor-not-allowed";
  const variantClassName =
    variant === "primary"
      ? "bg-white text-[#12203a] hover:bg-white/92"
      : "border border-white/14 bg-white/6 text-white/88 hover:bg-white/10";

  return (
    <button
      type="submit"
      name={name}
      value={value}
      disabled={pending}
      className={`${baseClassName} ${variantClassName}`}
    >
      {pending ? (
        <>
          <SpinnerIcon className="h-[14px] w-[14px] animate-spin" />
          {pendingLabel}
        </>
      ) : (
        label
      )}
    </button>
  );
}

function SenderRulePromptPendingOverlay() {
  const { pending } = useFormStatus();

  if (!pending) {
    return null;
  }

  return (
    <div className="pointer-events-none absolute inset-0 z-20 flex items-center justify-center rounded-[26px] bg-[rgba(9,13,21,0.46)] backdrop-blur-[6px]">
      <div className="flex items-center gap-2 rounded-full bg-[rgba(10,14,22,0.86)] px-4 py-2 text-[12px] font-semibold text-white ring-1 ring-white/10">
        <SpinnerIcon className="h-4 w-4 animate-spin" />
        설정을 적용하는 중입니다...
      </div>
    </div>
  );
}

function mailboxCheckboxClass() {
  return "h-[15px] w-[15px] rounded-[4px] border-[#cfd7e4] text-[#2f6df6] focus:ring-2 focus:ring-[#d7e5ff] focus:ring-offset-0";
}

function clamp(value: number, min: number, max: number) {
  return Math.min(max, Math.max(min, value));
}

function clampSidebarWidth(input: {
  containerWidth: number;
  isSetupPanel: boolean;
  isXlLayout: boolean;
  value: number;
}) {
  const minimumRemainingWidth =
    input.isSetupPanel || !input.isXlLayout
      ? MAIL_LAYOUT_LIMITS.contentMinWidth
      : MAIL_LAYOUT_LIMITS.list.min + MAIL_LAYOUT_LIMITS.detailMinWidth;
  const dynamicMax = Math.max(
    MAIL_LAYOUT_LIMITS.sidebar.min,
    Math.min(MAIL_LAYOUT_LIMITS.sidebar.max, input.containerWidth - minimumRemainingWidth)
  );

  return clamp(input.value, MAIL_LAYOUT_LIMITS.sidebar.min, dynamicMax);
}

function clampListWidth(input: { containerWidth: number; sidebarWidth: number; value: number }) {
  const dynamicMax = Math.max(
    MAIL_LAYOUT_LIMITS.list.min,
    Math.min(MAIL_LAYOUT_LIMITS.list.max, input.containerWidth - input.sidebarWidth - MAIL_LAYOUT_LIMITS.detailMinWidth)
  );

  return clamp(input.value, MAIL_LAYOUT_LIMITS.list.min, dynamicMax);
}

function clampListHeight(input: { containerHeight: number; value: number }) {
  const dynamicMax = Math.max(
    MAIL_LAYOUT_LIMITS.listHeight.min,
    Math.min(MAIL_LAYOUT_LIMITS.listHeight.max, input.containerHeight - MAIL_LAYOUT_LIMITS.detailMinHeight)
  );

  return clamp(input.value, MAIL_LAYOUT_LIMITS.listHeight.min, dynamicMax);
}

function ResizeHandle({
  active,
  ariaLabel,
  className,
  left,
  onPointerDown
}: {
  active: boolean;
  ariaLabel: string;
  className: string;
  left: number;
  onPointerDown: (event: ReactPointerEvent<HTMLButtonElement>) => void;
}) {
  return (
    <div className={`pointer-events-none absolute inset-y-0 z-30 ${className}`} style={{ left: `${left}px` }}>
      <button
        type="button"
        aria-label={ariaLabel}
        onPointerDown={onPointerDown}
        className="pointer-events-auto absolute inset-y-0 left-1/2 w-4 -translate-x-1/2 cursor-col-resize touch-none group"
      >
        <span
          className={`absolute inset-y-0 left-1/2 w-px -translate-x-1/2 transition-colors ${
            active ? "bg-[#2f6df6]" : "bg-[#dfe7f5] group-hover:bg-[#7fa5ff]"
          }`}
        />
        <span
          className={`absolute left-1/2 top-1/2 flex h-14 w-[10px] -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border shadow-[0_10px_26px_rgba(28,44,77,0.08)] transition-all ${
            active
              ? "border-[#cfe0ff] bg-[#eef4ff]"
              : "border-[#e6edf9] bg-white/96 group-hover:border-[#cfe0ff] group-hover:bg-[#f7faff]"
          }`}
        >
          <span className="h-7 w-[2px] rounded-full bg-[#c4d1e6]" />
        </span>
      </button>
    </div>
  );
}

function HorizontalResizeHandle({
  active,
  ariaLabel,
  className,
  left,
  onPointerDown,
  top
}: {
  active: boolean;
  ariaLabel: string;
  className: string;
  left: number;
  onPointerDown: (event: ReactPointerEvent<HTMLButtonElement>) => void;
  top: string;
}) {
  return (
    <div className={`pointer-events-none absolute right-0 z-30 ${className}`} style={{ left: `${left}px`, top }}>
      <button
        type="button"
        aria-label={ariaLabel}
        onPointerDown={onPointerDown}
        className="pointer-events-auto absolute inset-x-0 top-1/2 h-4 -translate-y-1/2 cursor-row-resize touch-none group"
      >
        <span
          className={`absolute inset-x-0 top-1/2 h-px -translate-y-1/2 transition-colors ${
            active ? "bg-[#2f6df6]" : "bg-[#dfe7f5] group-hover:bg-[#7fa5ff]"
          }`}
        />
        <span
          className={`absolute left-1/2 top-1/2 flex h-[10px] w-14 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border shadow-[0_10px_26px_rgba(28,44,77,0.08)] transition-all ${
            active
              ? "border-[#cfe0ff] bg-[#eef4ff]"
              : "border-[#e6edf9] bg-white/96 group-hover:border-[#cfe0ff] group-hover:bg-[#f7faff]"
          }`}
        >
          <span className="h-[2px] w-7 rounded-full bg-[#c4d1e6]" />
        </span>
      </button>
    </div>
  );
}

export function MailGate({
  currentPanel,
  dkimRegenerated,
  showDkimRotationNotice,
  setupSection,
  setupOverview,
  managedMembers,
  memberMailboxView,
  aiAssistOverview,
  autoSendOverview,
  billingOverview,
  billingProvider,
  usesTossPayments,
  polarBillingOverview,
  billingCancellationOverview,
  billingHistoryPage,
  billingHistoryState,
  billingTab,
  planSummary,
  canManageBilling,
  domainSetupAssistanceAvailable,
  mailConfigured,
  userEmail,
  userNotificationEmail,
  userRecoveryEmail,
  userRecoveryEmailVerified,
  userDisplayName,
  mailbox,
  companyName,
  domain,
  data,
  initialMessageTranslation,
  currentFolder,
  currentView,
  composeMode,
  sourceDraftMessageId,
  detailView,
  detailOpen,
  currentSearchField,
  currentSearchQuery,
  layoutPreferences,
  composeBccDefault,
  composeCcDefault,
  composeToDefault,
  composeSubjectDefault,
  composeBodyDefault,
  composeBodyHtmlDefault,
  composeSignatureHtml,
  composeSignatureText,
  signatureSettings,
  notice
}: MailGateProps) {
  const router = useRouter();
  const { locale, t, tr } = useI18n();
  const ui = (source: string) => translateStaticUiText(locale, source);
  const savedLayoutRef = useRef(normalizeMailLayoutPreferences(layoutPreferences));
  const filterMenuButtonRef = useRef<HTMLButtonElement | null>(null);
  const pageSizeMenuButtonRef = useRef<HTMLButtonElement | null>(null);
  const sortMenuButtonRef = useRef<HTMLButtonElement | null>(null);
  const viewMenuButtonRef = useRef<HTMLButtonElement | null>(null);
  const isSetupPanel = currentPanel === "setup";
  const isAutoSendPanel = currentPanel === "auto-send";
  const isMailboxPanel = currentPanel === "mail";
  const isFileDrawerView = isMailboxPanel && currentView === "files";
  const isStandaloneContentPanel = isSetupPanel || isAutoSendPanel || isFileDrawerView;
  const [mailData, setMailData] = useState(data);
  const incomingMailDataScope = data
    ? [
        data.currentFolder,
        data.currentPage,
        data.pageSize,
        data.messageFilter,
        data.searchField,
        data.searchQuery,
        data.sortOrder,
        data.totalMessages,
        data.lastSyncAt ?? "",
        data.selectedMessage?.id ?? "",
        data.selectedMessage?.bodyHtmlDisplay?.length ?? 0,
        data.selectedMessage?.bodyText.length ?? 0,
        data.selectedMessage?.attachments.length ?? 0,
        data.messages.map((message) => `${message.id}:${Number(message.isRead)}:${Number(message.isStarred)}`).join(",")
      ].join("|")
    : "no-mail-data";
  const [receivedMailDataScope, setReceivedMailDataScope] = useState(incomingMailDataScope);
  const [mailPageFillBuffer, setMailPageFillBuffer] = useState<{
    messages: MailMessageSummary[];
    scope: string;
  } | null>(null);
  const mailboxFetchStateRef = useRef({
    inFlight: false,
    queuedSilentRefresh: false
  });
  const mailboxMutationVersionRef = useRef(0);
  const pendingMailboxMovesRef = useRef(new Map<string, PendingMailboxMoveProjection>());
  const undoRequestedMoveKeysRef = useRef(new Set<string>());
  const mailboxUndoInFlightRef = useRef(false);
  const mailboxDataFetchRef = useRef<(options?: MailboxDataFetchOptions) => Promise<boolean>>(async () => false);
  const mailboxFolderStatusStateRef = useRef({
    inFlight: false,
    queuedForce: false
  });
  const mailboxFolderStatusFetchRef = useRef<
    (options?: { force?: boolean; refreshCurrent?: boolean }) => Promise<boolean>
  >(async () => false);
  const fullMailboxSyncStateRef = useRef({ inFlight: false });
  const [localNotice, setLocalNotice] = useState<MailNotice | null>(null);
  const [mailboxUndoAction, setMailboxUndoAction] = useState<MailboxUndoAction | null>(null);
  const [mailboxUndoPending, setMailboxUndoPending] = useState(false);
  const [dismissedServerNoticeKey, setDismissedServerNoticeKey] = useState("");
  const [pendingMoveKey, setPendingMoveKey] = useState<string | null>(null);
  const [dismissedDetailScopeKey, setDismissedDetailScopeKey] = useState<string | null>(null);
  const currentMailboxFolder = mailData?.currentFolder ?? currentFolder;
  const resolvedMessageFilter = mailData?.messageFilter ?? "all";
  const resolvedCurrentPage = mailData?.currentPage ?? 1;
  const interactionScopeKey = `${currentMailboxFolder}:${resolvedMessageFilter}:${resolvedCurrentPage}:${currentView}:${detailView}:${detailOpen ? "1" : "0"}`;
  const [moveMenuState, setMoveMenuState] = useState<{
    open: boolean;
    scope: string;
  }>({
    open: false,
    scope: interactionScopeKey
  });
  const [toolbarMenuState, setToolbarMenuState] = useState<{
    kind: ToolbarMenuKind | null;
    scope: string;
  }>({
    kind: null,
    scope: interactionScopeKey
  });
  const [senderMenuState, setSenderMenuState] = useState<{
    messageId: number | null;
    scope: string;
  }>({
    messageId: null,
    scope: interactionScopeKey
  });
  const [senderMenuAnchor, setSenderMenuAnchor] = useState<HTMLElement | null>(null);
  const [detailSenderMenuAnchor, setDetailSenderMenuAnchor] = useState<HTMLElement | null>(null);
  const [detailMenuState, setDetailMenuState] = useState<{
    open: boolean;
    scope: string;
  }>({
    open: false,
    scope: ""
  });
  const [detailMenuAnchor, setDetailMenuAnchor] = useState<HTMLElement | null>(null);
  const [sidebarFolderMenuState, setSidebarFolderMenuState] = useState<{
    folderId: number | null;
    scope: string;
  }>({
    folderId: null,
    scope: ""
  });
  const [sidebarFolderMenuPosition, setSidebarFolderMenuPosition] = useState<{
    top: number;
    left: number;
  } | null>(null);
  const [rawSourceModalState, setRawSourceModalState] = useState<{
    open: boolean;
    scope: string;
  }>({
    open: false,
    scope: ""
  });
  const [rawSourceLoadState, setRawSourceLoadState] = useState<{
    error: string | null;
    loading: boolean;
    messageId: number | null;
    value: string;
  }>({
    error: null,
    loading: false,
    messageId: null,
    value: ""
  });
  const [messageTranslationState, setMessageTranslationState] = useState<MessageTranslationState>({
    data: initialMessageTranslation,
    error: null,
    loading: false,
    messageId: initialMessageTranslation?.messageId ?? null,
    targetLocale: initialMessageTranslation?.targetLocale ?? null
  });
  const [originalMessageScope, setOriginalMessageScope] = useState("");
  const [translationRetryVersion, setTranslationRetryVersion] = useState(0);
  const [attachmentPreviewState, setAttachmentPreviewState] = useState<AttachmentPreviewState | null>(null);
  const [settingsMenuState, setSettingsMenuState] = useState<{
    open: boolean;
    scope: string;
  }>({
    open: false,
    scope: ""
  });
  const [selectionState, setSelectionState] = useState<{
    ids: number[];
    scope: string;
  }>({
    ids: [],
    scope: interactionScopeKey
  });
  const [senderSpamPrompt, setSenderSpamPrompt] = useState<SenderSpamPromptState | null>(null);
  const [continueMovePrompt, setContinueMovePrompt] = useState<ContinueMovePromptState | null>(null);
  const [continueMoveExisting, setContinueMoveExisting] = useState(false);
  const [continueMoveCompletionOpen, setContinueMoveCompletionOpen] = useState(false);
  const [continueMoveReplacementPrompt, setContinueMoveReplacementPrompt] =
    useState<ContinueMoveReplacementPromptState | null>(null);
  const [senderMoveRules, setSenderMoveRules] = useState<MailboxSenderMoveRule[]>([]);
  const [senderSpamPromptTargetFolder, setSenderSpamPromptTargetFolder] = useState("inbox");
  const [senderSpamPromptNewFolderName, setSenderSpamPromptNewFolderName] = useState("");
  const [searchInput, setSearchInput] = useState(currentSearchQuery);
  const [recentSearches, setRecentSearches] = useState<string[]>([]);
  const recentSearchStorageKey = `official-mail:recent-searches:${userEmail.trim().toLowerCase()}`;
  const deferredSearchInput = useDeferredValue(searchInput.trim());
  const [searchSuggestions, setSearchSuggestions] = useState<MailSearchSuggestion[]>([]);
  const [searchLoading, setSearchLoading] = useState(false);
  const [searchOpen, setSearchOpen] = useState(false);
  const [searchHighlightedIndex, setSearchHighlightedIndex] = useState(-1);
  useEffect(() => {
    const timer = window.setTimeout(() => {
      try {
        const stored = JSON.parse(window.localStorage.getItem(recentSearchStorageKey) ?? "[]") as unknown;
        setRecentSearches(
          Array.isArray(stored)
            ? stored.filter((value): value is string => typeof value === "string" && value.trim().length > 0).slice(0, 5)
            : []
        );
      } catch {
        setRecentSearches([]);
      }
    }, 0);
    return () => window.clearTimeout(timer);
  }, [recentSearchStorageKey]);
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
  const [mobileSearchVisible, setMobileSearchVisible] = useState(false);
  const [viewportState, setViewportState] = useState({
    isLgViewport: false,
    isXlViewport: false
  });
  const [pendingStarMessageIds, setPendingStarMessageIds] = useState<number[]>([]);
  const [pendingPanelNavigation, setPendingPanelNavigation] = useState<{
    href: string;
    intent: "auto-send" | "mailbox" | "setup";
  } | null>(null);
  const [isPanelNavigationPending, startPanelNavigationTransition] = useTransition();
  const [pendingMessageNavigationId, setPendingMessageNavigationId] = useState<number | null>(null);
  const [isMessageNavigationPending, startMessageNavigationTransition] = useTransition();
  const [layoutState, setLayoutState] = useState(() => normalizeMailLayoutPreferences(layoutPreferences));
  const layoutStateRef = useRef(layoutState);
  const [dragState, setDragState] = useState<LayoutDragState | null>(null);
  const allFolders = mailData?.folders ?? [];
  const hideSpamFolder = Boolean(mailData?.spamSettings.hideSpamFolder);
  const folders = allFolders.filter(
    (folder) => !hideSpamFolder || folder.systemName !== "spam" || currentMailboxFolder === "spam"
  );
  const [folderOrderState, setFolderOrderState] = useState<number[]>(() => folders.map((folder) => folder.id));
  const folderOrderRef = useRef(folderOrderState);
  const folderDragStartOrderRef = useRef(folderOrderState);
  const [draggingFolderId, setDraggingFolderId] = useState<number | null>(null);
  const [dragOverFolderId, setDragOverFolderId] = useState<number | null>(null);
  const [folderDropPosition, setFolderDropPosition] = useState<SidebarFolderDropPosition | null>(null);
  const [draggingMessageIds, setDraggingMessageIds] = useState<number[] | null>(null);
  const [dragOverMessageFolder, setDragOverMessageFolder] = useState<string | null>(null);
  const [sidebarFolderActionState, setSidebarFolderActionState] = useState<{
    action: SidebarFolderAction | null;
    folderId: number | null;
  }>({
    action: null,
    folderId: null
  });
  const [sidebarFolderEditorState, setSidebarFolderEditorState] = useState<SidebarFolderInlineEditorState | null>(null);
  const [sidebarFolderEditorPending, setSidebarFolderEditorPending] = useState(false);
  const [personalFoldersOpen, setPersonalFoldersOpen] = useState(true);
  const [moveTargetFolder, setMoveTargetFolder] = useState("");
  const [moveNewFolderName, setMoveNewFolderName] = useState("");
  const [moveMenuAnchor, setMoveMenuAnchor] = useState<HTMLButtonElement | null>(null);

  useEffect(() => {
    layoutStateRef.current = layoutState;
  }, [layoutState]);

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

    const controller = new AbortController();
    void fetch("/api/mailbox/move-rules", {
      cache: "no-store",
      signal: controller.signal
    })
      .then(async (response) => {
        if (!response.ok) return null;
        return (await response.json()) as MailboxMoveRulesResponse;
      })
      .then((payload) => {
        if (payload?.ok && payload.settings) {
          setSenderMoveRules(payload.settings.rules);
        }
      })
      .catch(() => undefined);

    return () => controller.abort();
  }, [mailConfigured, userEmail]);

  const serverNoticeKey = notice ? `${notice.tone}:${notice.message}` : "";
  const serverNotice = notice && dismissedServerNoticeKey !== serverNoticeKey ? notice : null;
  const runtimeNotice = localNotice ?? serverNotice;

  useEffect(() => {
    if (!serverNoticeKey || typeof window === "undefined") {
      return;
    }

    const url = new URL(window.location.href);
    let changed = false;

    for (const key of ["success", "error", "details"]) {
      if (url.searchParams.has(key)) {
        url.searchParams.delete(key);
        changed = true;
      }
    }

    if (!changed) {
      return;
    }

    const nextUrl = `${url.pathname}${url.search}${url.hash}`;
    window.history.replaceState(window.history.state, "", nextUrl);
  }, [serverNoticeKey]);

  useEffect(() => {
    if (!runtimeNotice || mailboxUndoPending) {
      return;
    }

    const timer = window.setTimeout(() => {
      if (localNotice) {
        setLocalNotice((current) =>
          current?.message === localNotice.message && current?.tone === localNotice.tone ? null : current
        );
        setMailboxUndoAction(null);
        return;
      }

      if (serverNoticeKey) {
        setDismissedServerNoticeKey(serverNoticeKey);
      }
    }, 4200);

    return () => window.clearTimeout(timer);
  }, [localNotice, mailboxUndoPending, runtimeNotice, serverNoticeKey]);

  useEffect(() => {
    savedLayoutRef.current = normalizeMailLayoutPreferences(layoutPreferences);
  }, [layoutPreferences]);

  useLayoutEffect(() => {
    if (typeof window === "undefined") {
      return;
    }

    const lgMedia = window.matchMedia("(min-width: 1024px)");
    const xlMedia = window.matchMedia("(min-width: 1280px)");
    const updateViewportState = () => {
      const isLgViewport = lgMedia.matches;
      setViewportState({
        isLgViewport,
        isXlViewport: xlMedia.matches
      });

      if (isLgViewport) {
        setMobileSidebarOpen(false);
        setMobileSearchVisible(false);
      }
    };

    updateViewportState();
    lgMedia.addEventListener("change", updateViewportState);
    xlMedia.addEventListener("change", updateViewportState);

    return () => {
      lgMedia.removeEventListener("change", updateViewportState);
      xlMedia.removeEventListener("change", updateViewportState);
    };
  }, []);

  useEffect(() => {
    const frame = window.requestAnimationFrame(() => {
      setMobileSidebarOpen(false);
      setMobileSearchVisible(false);
    });

    return () => window.cancelAnimationFrame(frame);
  }, [interactionScopeKey, currentPanel, setupSection, currentView]);

  const moveMenuOpen = moveMenuState.scope === interactionScopeKey && moveMenuState.open;
  const toolbarMenuKind = toolbarMenuState.scope === interactionScopeKey ? toolbarMenuState.kind : null;
  const filterMenuOpen = toolbarMenuKind === "filter";
  const pageSizeMenuOpen = toolbarMenuKind === "pageSize";
  const sortMenuOpen = toolbarMenuKind === "sort";
  const viewMenuOpen = toolbarMenuKind === "view";
  const senderMenuMessageId = senderMenuState.scope === interactionScopeKey ? senderMenuState.messageId : null;
  const sidebarFolderMenuId =
    sidebarFolderMenuState.scope === interactionScopeKey ? sidebarFolderMenuState.folderId : null;
  const allMessages = mailData?.messages ?? [];
  const messageMap = new Map(allMessages.map((message) => [message.id, message]));
  const selectedIdsFromState = (selectionState.scope === interactionScopeKey ? selectionState.ids : []).filter((id) =>
    messageMap.has(id)
  );
  const selectedMessage = detailOpen ? (mailData?.selectedMessage ?? null) : null;
  const hasPendingLargeAttachmentLinks = selectedMessage?.direction === "inbound" &&
    selectedMessage.hasUnresolvedLargeAttachmentLinks;
  const folderMap = new Map(folders.map((folder) => [folder.systemName, folder]));
  const receivedFolderMessages = folders
    .filter(
      (folder) =>
        folder.systemName !== "sent" &&
        folder.systemName !== "drafts" &&
        folder.systemName !== "spam" &&
        folder.systemName !== "trash"
    )
    .reduce((total, folder) => total + folder.count, 0);
  const allFolderMessages = receivedFolderMessages;
  const currentViewMode = layoutState.viewMode;
  const currentSortOrder = layoutState.sortOrder;
  const isListOnlyMode = currentViewMode === "list-only";
  const isSplitHorizontalMode = currentViewMode === "split-horizontal";
  const isSplitVerticalMode = currentViewMode === "split-vertical";

  useEffect(() => {
    let cancelled = false;
    const currentData = mailData;

    if (
      !isMailboxPanel ||
      !currentData ||
      currentData.currentPage >= currentData.totalPages ||
      currentData.messages.length < currentData.pageSize
    ) {
      return;
    }

    const scope = interactionScopeKey;
    const params = buildFolderSearchParams(currentData.currentFolder, {
      page: currentData.currentPage + 1,
      pageSize: currentData.pageSize,
      searchField: currentData.searchField,
      searchQuery: currentData.searchQuery,
      sortOrder: currentSortOrder,
      tab: currentData.messageFilter
    });

    void fetch(`/api/mailbox?${params.toString()}`, {
      cache: "no-store",
      method: "GET"
    })
      .then(async (response) => {
        if (!response.ok) return null;
        return (await response.json()) as { data?: MailAppOverview | null };
      })
      .then((payload) => {
        if (!cancelled && payload?.data) {
          setMailPageFillBuffer({
            messages: payload.data.messages,
            scope
          });
        }
      })
      .catch(() => undefined);

    return () => {
      cancelled = true;
    };
  }, [currentSortOrder, interactionScopeKey, isMailboxPanel, mailData]);
  const effectiveFolderOrder = (() => {
    const nextIds = folders.map((folder) => folder.id);

    if (folderOrderState.length === 0) {
      return nextIds;
    }

    const preserved = folderOrderState.filter((id) => nextIds.includes(id));
    const missing = nextIds.filter((id) => !preserved.includes(id));
    return [...preserved, ...missing];
  })();
  const orderedFolderMap = new Map(folders.map((folder) => [folder.id, folder]));
  const orderedSidebarFolders = effectiveFolderOrder
    .map((id) => orderedFolderMap.get(id) ?? null)
    .filter((folder): folder is NonNullable<typeof folder> => Boolean(folder));

  for (const folder of folders) {
    if (!orderedSidebarFolders.some((item) => item.id === folder.id)) {
      orderedSidebarFolders.push(folder);
    }
  }

  useEffect(() => {
    folderOrderRef.current = effectiveFolderOrder;
  }, [effectiveFolderOrder]);

  const folderOrderAnimationKey = effectiveFolderOrder.join(":");

  useLayoutEffect(() => {
    const nextTopPositions = new Map<number, number>();

    folderRowRefs.current.forEach((node, id) => {
      nextTopPositions.set(id, node.getBoundingClientRect().top);
    });

    const previousTopPositions = folderRowTopPositionsRef.current;

    if (previousTopPositions.size > 0) {
      nextTopPositions.forEach((nextTop, id) => {
        if (draggingFolderId === id) {
          return;
        }

        const previousTop = previousTopPositions.get(id);
        const row = folderRowRefs.current.get(id);

        if (previousTop === undefined || !row) {
          return;
        }

        const deltaY = previousTop - nextTop;

        if (Math.abs(deltaY) < 1) {
          return;
        }

        folderRowAnimationsRef.current.get(id)?.cancel();

        const animation = row.animate([{ transform: `translateY(${deltaY}px)` }, { transform: "translateY(0px)" }], {
          duration: 240,
          easing: "cubic-bezier(0.22, 1, 0.36, 1)"
        });

        const clearAnimation = () => {
          if (folderRowAnimationsRef.current.get(id) === animation) {
            folderRowAnimationsRef.current.delete(id);
          }
        };

        animation.addEventListener("finish", clearAnimation, { once: true });
        animation.addEventListener("cancel", clearAnimation, { once: true });
        folderRowAnimationsRef.current.set(id, animation);
      });
    }

    folderRowTopPositionsRef.current = nextTopPositions;
  }, [folderOrderAnimationKey, draggingFolderId]);

  const messageFilterCounts = mailData?.messageFilterCounts ?? {
    all: allMessages.length,
    unread: allMessages.filter((message) => !message.isRead).length,
    important: allMessages.filter((message) => message.isStarred).length,
    attachments: allMessages.filter(looksLikeAttachment).length
  };
  const totalMessages = mailData?.totalMessages ?? allMessages.length;
  const totalPages = mailData?.totalPages ?? 1;
  const resolvedPageSize = mailData?.pageSize ?? DEFAULT_MAILBOX_PAGE_SIZE;
  const compactMailbox = mailbox || userEmail;
  const resolvedSearchField = mailData?.searchField ?? currentSearchField;
  const resolvedSearchQuery = (isMailboxPanel ? mailData?.searchQuery ?? currentSearchQuery : currentSearchQuery).trim();
  const currentMailboxQueryKey = [
    currentMailboxFolder,
    resolvedMessageFilter,
    resolvedCurrentPage,
    resolvedPageSize,
    resolvedSearchField,
    resolvedSearchQuery,
    currentSortOrder
  ].join(":");
  const currentMailboxQueryKeyRef = useRef(currentMailboxQueryKey);

  useEffect(() => {
    currentMailboxQueryKeyRef.current = currentMailboxQueryKey;
  }, [currentMailboxQueryKey]);
  const activeSearchBadgeVisible =
    resolvedSearchQuery.length > 0 && resolvedSearchField !== "all" && searchInput.trim() === resolvedSearchQuery;
  const mobileSearchExpanded =
    !viewportState.isLgViewport &&
    (mobileSearchVisible || searchInput.trim().length > 0 || resolvedSearchQuery.length > 0);

  const sidebarFolderItems: SidebarFolderNavItem[] = orderedSidebarFolders.map((folder) => ({
    id: folder.id,
    kind: folder.kind,
    systemName: folder.systemName,
    parentSystemName: folder.parentSystemName ?? null,
    label: folder.kind === "custom" ? folder.name : folderLabelForSidebar(folder.systemName, t),
    count: folder.count,
    depth: 0,
    icon:
      folder.kind === "custom"
        ? "folder"
        : folder.systemName === "sent"
          ? "sent"
          : folder.systemName === "drafts"
            ? "drafts"
            : folder.systemName === "trash"
              ? "trash"
              : folder.systemName === "spam"
                ? "spam"
                : "inbox",
    href: folderHref(folder.systemName, {
      pageSize: resolvedPageSize,
      sortOrder: currentSortOrder,
      tab: "all"
    })
  }));
  const sidebarFolderBySystemName = new Map(sidebarFolderItems.map((folder) => [folder.systemName, folder]));
  const resolveSidebarFolderDepth = (folder: (typeof sidebarFolderItems)[number]) => {
    let depth = 0;
    let parentSystemName = folder.parentSystemName;
    const visited = new Set([folder.systemName]);

    while (parentSystemName && depth < 8 && !visited.has(parentSystemName)) {
      const parent = sidebarFolderBySystemName.get(parentSystemName);

      if (!parent || parent.kind !== "custom") {
        break;
      }

      visited.add(parentSystemName);
      depth += 1;
      parentSystemName = parent.parentSystemName;
    }

    return depth;
  };
  const sidebarFolders: SidebarFolderNavItem[] = sidebarFolderItems.map((folder) => ({
    ...folder,
    depth: resolveSidebarFolderDepth(folder)
  }));
  const primaryFolders = sidebarFolders.filter((folder) => isPrimaryMailboxFolder(folder.systemName));
  const personalFolderItems = sidebarFolders.filter((folder) => isPersonalMailboxFolder(folder.systemName, folder.kind));
  const personalFolderSystemNames = new Set(personalFolderItems.map((folder) => folder.systemName));
  const personalFolders: SidebarFolderNavItem[] = [];
  const appendedPersonalFolderNames = new Set<string>();
  const appendPersonalFolderTree = (folder: SidebarFolderNavItem) => {
    if (appendedPersonalFolderNames.has(folder.systemName)) {
      return;
    }

    appendedPersonalFolderNames.add(folder.systemName);
    personalFolders.push(folder);

    for (const child of personalFolderItems) {
      if (child.parentSystemName === folder.systemName) {
        appendPersonalFolderTree(child);
      }
    }
  };

  for (const folder of personalFolderItems) {
    if (!folder.parentSystemName || !personalFolderSystemNames.has(folder.parentSystemName)) {
      appendPersonalFolderTree(folder);
    }
  }

  for (const folder of personalFolderItems) {
    appendPersonalFolderTree(folder);
  }
  const secondarySystemFolders = sidebarFolders.filter((folder) => isSecondarySystemMailboxFolder(folder.systemName));
  const sidebarFolderMenuItem =
    sidebarFolderMenuId === null ? null : (sidebarFolders.find((folder) => folder.id === sidebarFolderMenuId) ?? null);
  const allFolderHref = mailConfigured
    ? folderHref("all", {
        pageSize: resolvedPageSize,
        sortOrder: currentSortOrder,
        tab: "all"
      })
    : setupHref("domain");
  const allFolderNavigationIntent = mailConfigured ? "mailbox" : "setup";
  const senderSpamRestoreFolderOptions = [
    ...sidebarFolders.filter((folder) => folder.systemName === "inbox"),
    ...personalFolders
  ];
  const settingsMenuItems: SettingsMenuItem[] = [
    { id: "account", label: t("settings.account.label") },
    { id: "contacts", label: t("settings.contacts.label") },
    { id: "classification", label: t("settings.classification.label") },
    { id: "spam", label: t("settings.spam.label") },
    { id: "storage", label: t("settings.storage.label") },
    { id: "assistant", label: t("settings.assistant.label") },
    { id: "integrations", label: t("settings.integrations.label") },
    ...(canManageBilling
      ? ([
          { id: "domain", label: t("settings.domain.label") },
          { id: "members", label: t("settings.members.label") },
          { id: "billing", label: t("settings.billing.label") }
        ] satisfies SettingsMenuItem[])
      : []),
    { id: "language", label: t("settings.language.label") },
    { id: "logout", label: t("common.logout") }
  ];
  const settingsMenuRef = useRef<HTMLDivElement | null>(null);
  const searchMenuRef = useRef<HTMLDivElement | null>(null);
  const searchInputRef = useRef<HTMLInputElement | null>(null);
  const detailPanelEnabled = isMailboxPanel && currentView === "list";

  const filteredMessages = allMessages;
  const senderMenuMessage = filteredMessages.find((message) => message.id === senderMenuMessageId);
  const senderProfileTarget = senderMenuMessage
    ? senderMenuMessage.direction === "outbound"
      ? primaryRecipient(senderMenuMessage.toAddresses)
      : {
          address: senderMenuMessage.fromAddress,
          label: senderMenuMessage.senderContactName || senderLabel(senderMenuMessage.fromName, senderMenuMessage.fromAddress)
        }
    : null;
  const visibleRecentSearches = recentSearches
    .filter((value) => value.toLocaleLowerCase().includes(searchInput.trim().toLocaleLowerCase()))
    .slice(0, 5);
  const searchDropdownVisible =
    searchOpen && mailConfigured && (searchInput.trim().length > 0 || visibleRecentSearches.length > 0);

  const previewMessage = detailOpen ? createPreviewMessage(selectedMessage, null) : null;
  const readMessageTranslationState = useEffectEvent(() => messageTranslationState);

  useEffect(() => {
    const messageId = previewMessage?.id ?? null;

    if (!messageId || previewMessage?.direction !== "inbound") {
      return;
    }

    const currentTranslation = readMessageTranslationState();
    if (
      currentTranslation.messageId === messageId &&
      currentTranslation.targetLocale === locale &&
      currentTranslation.data &&
      !currentTranslation.error
    ) {
      return;
    }

    const controller = new AbortController();
    const loadingTimer = window.setTimeout(() => {
      setMessageTranslationState({
        data: null,
        error: null,
        loading: true,
        messageId,
        targetLocale: locale
      });
    }, 250);

    void fetch("/api/mailbox/message-translation", {
      body: JSON.stringify({
        messageId,
        targetLocale: locale
      }),
      cache: "no-store",
      credentials: "same-origin",
      headers: {
        "Content-Type": "application/json"
      },
      method: "POST",
      signal: controller.signal
    })
      .then(async (response) => {
        const payload = (await response.json().catch(() => null)) as {
          ok?: boolean;
          translation?: MessageTranslationData;
        } | null;

        if (!response.ok || !payload?.ok || !payload.translation) {
          throw new Error("mail-translation-failed");
        }

        return payload.translation;
      })
      .then((translation) => {
        if (controller.signal.aborted) {
          return;
        }

        setMessageTranslationState({
          data: translation,
          error: null,
          loading: false,
          messageId,
          targetLocale: locale
        });
      })
      .catch((error) => {
        if (controller.signal.aborted) {
          return;
        }

        setMessageTranslationState({
          data: null,
          error: error instanceof Error ? error.message : "mail-translation-failed",
          loading: false,
          messageId,
          targetLocale: locale
        });
      });

    return () => {
      window.clearTimeout(loadingTimer);
      controller.abort();
    };
  }, [locale, previewMessage?.direction, previewMessage?.id, translationRetryVersion]);

  const previewAttachments = (previewMessage?.attachments ?? []).filter((attachment) => !attachment.isInline);
  const previewableAttachments = previewAttachments.filter((attachment) => attachment.isPreviewable);
  const imagePreviewableAttachments = previewableAttachments.filter((attachment) =>
    isImageAttachmentPreview(attachment)
  );
  const totalAttachmentSize = previewAttachments.reduce((total, attachment) => total + attachment.sizeBytes, 0);
  const isLgViewport = viewportState.isLgViewport;
  const isXlViewport = viewportState.isXlViewport;
  const detailPanelScopeKey =
    detailPanelEnabled && detailOpen && previewMessage
      ? `${currentMailboxFolder}:${resolvedMessageFilter}:${resolvedCurrentPage}:${currentView}:${previewMessage.id}:${detailView}`
      : "";
  const splitPanelSupported =
    detailPanelEnabled &&
    !isListOnlyMode &&
    ((isSplitHorizontalMode && isXlViewport) || (isSplitVerticalMode && isLgViewport));
  const splitDetailPreviewDismissed = detailPanelScopeKey !== "" && dismissedDetailScopeKey === detailPanelScopeKey;
  const showSplitDetailPanel = splitPanelSupported;
  const showStandaloneMessageView =
    detailPanelEnabled && detailOpen && Boolean(previewMessage) && (isListOnlyMode || !splitPanelSupported);
  const attachmentPreviewItems =
    attachmentPreviewState?.collection === "images" ? imagePreviewableAttachments : previewableAttachments;
  const attachmentPreviewCurrentIndex = attachmentPreviewState
    ? Math.min(attachmentPreviewState.currentIndex, Math.max(attachmentPreviewItems.length - 1, 0))
    : 0;
  const attachmentPreviewModalOpen = Boolean(
    attachmentPreviewState &&
    attachmentPreviewState.scope === detailPanelScopeKey &&
    attachmentPreviewItems[attachmentPreviewCurrentIndex]
  );
  const activeAttachmentPreview =
    attachmentPreviewModalOpen && attachmentPreviewState
      ? (attachmentPreviewItems[attachmentPreviewCurrentIndex] ?? null)
      : null;
  const activeAttachmentPreviewUrl =
    activeAttachmentPreview && attachmentPreviewState
      ? `/api/mailbox/attachment?message=${attachmentPreviewState.messageId}&index=${activeAttachmentPreview.index}&mode=inline`
      : "";
  const activeAttachmentPreviewIsImage = activeAttachmentPreview
    ? isImageAttachmentPreview(activeAttachmentPreview)
    : false;
  const activeAttachmentPreviewFile = activeAttachmentPreview
    ? splitAttachmentFilename(activeAttachmentPreview.filename)
    : null;
  const attachmentPreviewCountLabel =
    attachmentPreviewState && attachmentPreviewItems.length > 0
      ? `${attachmentPreviewCurrentIndex + 1}/${attachmentPreviewItems.length}`
      : "";
  const selectedIds =
    selectedIdsFromState.length > 0
      ? selectedIdsFromState
      : !isListOnlyMode && previewMessage
        ? [previewMessage.id]
        : [];
  const activePreviewMessageId = previewMessage?.id ?? null;
  const activeMessageStarPending =
    activePreviewMessageId !== null && pendingStarMessageIds.includes(activePreviewMessageId);
  const showPanelNavigationOverlay = pendingPanelNavigation !== null && isPanelNavigationPending;
  const showMessageNavigationOverlay = pendingMessageNavigationId !== null && isMessageNavigationPending;
  const showContentNavigationOverlay = showMessageNavigationOverlay || showPanelNavigationOverlay;
  const showMobileFullContentNavigationOverlay = !isLgViewport && showContentNavigationOverlay;
  const contentNavigationDescription = showMessageNavigationOverlay
    ? "메일 내용을 불러오고 있습니다."
    : pendingPanelNavigation?.intent === "setup"
      ? "설정 화면을 불러오고 있습니다."
      : pendingPanelNavigation?.intent === "auto-send"
        ? "자동발송 예약 화면을 불러오고 있습니다."
        : "메일함을 불러오고 있습니다.";
  const showPrimaryContentNavigationOverlay =
    !showMobileFullContentNavigationOverlay &&
    (showPanelNavigationOverlay || (showMessageNavigationOverlay && !showSplitDetailPanel));
  const showDetailContentNavigationOverlay =
    !showMobileFullContentNavigationOverlay && showContentNavigationOverlay && showSplitDetailPanel;
  const showEmbeddedContentNavigationOverlay = !showMobileFullContentNavigationOverlay && showContentNavigationOverlay;
  const renderContentNavigationOverlay = (
    className = "absolute inset-0 z-[120] flex items-center justify-center bg-[rgba(252,253,255,0.96)] transition-opacity duration-200"
  ) =>
    showContentNavigationOverlay ? (
      <div className={className}>
        <div className="flex flex-col items-center gap-4 text-center">
          <span className="inline-flex h-14 w-14 items-center justify-center">
            <span className="h-6 w-6 animate-spin rounded-full border-[2.5px] border-transparent border-r-[#2f6df6] border-t-[#2f6df6]" />
          </span>
          <div className="space-y-1">
            <p className="text-[18px] font-semibold tracking-[-0.03em] text-[#1a2943]">{t("mail.loading")}</p>
            <p className="text-[13px] text-[#72839f]">{contentNavigationDescription}</p>
          </div>
        </div>
      </div>
    ) : null;
  const backToListHref = folderHref(currentMailboxFolder, {
    page: resolvedCurrentPage,
    pageSize: resolvedPageSize,
    searchField: resolvedSearchField,
    searchQuery: resolvedSearchQuery,
    sortOrder: currentSortOrder,
    tab: resolvedMessageFilter
  });
  const currentFolderLabel =
    currentMailboxFolder === "all"
      ? t("mail.all")
      : folderLabelForHeader(currentMailboxFolder, folderMap.get(currentMailboxFolder)?.name ?? t("mail.inbox"), t);
  const currentSidebarFolderItem =
    currentMailboxFolder === "all"
      ? null
      : (sidebarFolders.find((folder) => folder.systemName === currentMailboxFolder) ?? null);
  const currentFolderClearPending =
    currentMailboxFolder === "trash" &&
    currentSidebarFolderItem !== null &&
    sidebarFolderActionState.folderId === currentSidebarFolderItem.id &&
    sidebarFolderActionState.action === "clear";
  const renderFolderSummaryTrailingAction = () => {
    if (currentMailboxFolder === "trash" && currentSidebarFolderItem) {
      return (
        <button
          type="button"
          onClick={() => void runSidebarFolderAction(currentSidebarFolderItem, "clear")}
          disabled={currentFolderClearPending}
          className={`ml-2 inline-flex items-center gap-1.5 transition-colors ${
            currentFolderClearPending
              ? "cursor-wait text-[#c1c9d7]"
              : "cursor-pointer text-[#8a94a8] hover:text-[#2f6df6]"
          }`}
        >
          {currentFolderClearPending ? (
            <span className="h-3 w-3 animate-spin rounded-full border-2 border-[#c1c9d7] border-t-[#2f6df6]" />
          ) : null}
          {currentFolderClearPending ? tr("비우는 중") : tr("휴지통 비우기")}
        </button>
      );
    }

    return (
      <Link
        href={folderHref(currentMailboxFolder, {
          page: 1,
          pageSize: resolvedPageSize,
          searchField: resolvedSearchField,
          searchQuery: resolvedSearchQuery,
          sortOrder: currentSortOrder,
          tab: resolvedMessageFilter === "unread" ? "all" : "unread"
        })}
        prefetch={false}
        scroll={false}
        className={`ml-2 transition-colors hover:text-[#2f6df6] ${
          resolvedMessageFilter === "unread" ? "font-semibold text-[#2f6df6]" : "text-[#8a94a8]"
        }`}
      >
        {tr("안읽음")} {formatCount(messageFilterCounts.unread)}
      </Link>
    );
  };

  const selectedMessages = filteredMessages.filter((message) => selectedIds.includes(message.id));
  const activeMessageId = selectedMessage?.id ?? (selectedMessages.length === 1 ? selectedMessages[0].id : null);
  const toolbarMessage: ComposeToolbarMessage | null =
    selectedMessages.length === 1
      ? {
          id: selectedMessages[0].id,
          bodyText: selectedMessages[0].snippet,
          fromAddress: selectedMessages[0].fromAddress,
          fromName: selectedMessages[0].fromName,
          receivedAt: selectedMessages[0].receivedAt,
          snippet: selectedMessages[0].snippet,
          subject: selectedMessages[0].subject,
          toAddresses: selectedMessages[0].toAddresses
        }
      : null;

  const checkedMessageIds = selectedIds;
  const checkedMessageIdsValue = checkedMessageIds.join(",");
  const hasCheckedMessages = checkedMessageIds.length > 0;
  const standaloneMessageIdsValue = showStandaloneMessageView && previewMessage ? String(previewMessage.id) : "";
  const draggingMessageIdSet = new Set(draggingMessageIds ?? []);
  const selectedMessagesAllRead = hasCheckedMessages && selectedMessages.every((message) => message.isRead);
  const bulkReadToggleLabel = tr(selectedMessagesAllRead ? "안읽음" : "읽음");
  const bulkReadToggleValue = selectedMessagesAllRead ? "unread" : "read";
  const allVisibleSelected =
    filteredMessages.length > 0 && filteredMessages.every((message) => selectedIds.includes(message.id));
  const activeToolbarMessage: ComposeToolbarMessage | null =
    toolbarMessage ??
    (previewMessage
      ? {
          id: previewMessage.id,
          bodyHtml: previewMessage.bodyHtml,
          bodyText: previewMessage.bodyText,
          fromAddress: previewMessage.fromAddress,
          fromName: previewMessage.fromName,
          receivedAt: previewMessage.receivedAt,
          snippet: previewMessage.snippet,
          subject: previewMessage.subject,
          toAddresses: previewMessage.toAddresses
        }
      : null);
  const ownAddresses = [compactMailbox, userEmail];
  const replyHref = buildComposeHref({
    action: "reply",
    currentFolder: currentMailboxFolder,
    ownAddresses,
    message: activeToolbarMessage
  });
  const replyAllHref = buildComposeHref({
    action: "replyAll",
    currentFolder: currentMailboxFolder,
    ownAddresses,
    message: activeToolbarMessage
  });
  const forwardHref = buildComposeHref({
    action: "forward",
    currentFolder: currentMailboxFolder,
    ownAddresses,
    message: activeToolbarMessage
  });

  const syncError = mailData?.syncError ?? "";
  const reconnectRequired =
    Boolean(syncError) &&
    (syncError === "mailbox-auth-missing" ||
      syncError === "mailbox-auth-invalid" ||
      syncError.toLowerCase().includes("auth") ||
      syncError.toLowerCase().includes("login") ||
      syncError.toLowerCase().includes("credential"));

  const composeHref = mailConfigured ? "/mail?view=compose" : setupHref("domain");
  const fileDrawerHref = mailConfigured ? "/mail?view=files" : setupHref("domain");
  const sessionDomain = setupOverview?.domain || domain || compactMailbox.split("@")[1] || "";
  const settingsMenuScopeKey = `${currentPanel}:${setupSection}:${currentMailboxFolder}:${currentView}:${resolvedCurrentPage}:${resolvedMessageFilter}`;
  const settingsMenuOpen = settingsMenuState.open && settingsMenuState.scope === settingsMenuScopeKey;
  const composeFormKey = [
    composeBccDefault,
    composeCcDefault,
    composeToDefault,
    composeSubjectDefault,
    composeBodyDefault
  ].join("::");
  const verticalSplitDividerTop = `clamp(${MAIL_LAYOUT_LIMITS.listHeight.min}px, ${layoutState.listHeight}px, calc(100% - ${MAIL_LAYOUT_LIMITS.detailMinHeight}px))`;
  const layoutGridStyle = {
    "--mail-sidebar-width": `${layoutState.sidebarWidth}px`,
    "--mail-list-width": `${layoutState.listWidth}px`,
    "--mail-list-height": `${layoutState.listHeight}px`,
    "--mail-list-height-clamped": verticalSplitDividerTop
  } as CSSProperties;
  const paginationItems = buildPaginationItems(resolvedCurrentPage, totalPages);
  const currentFolderTotalCount =
    currentMailboxFolder === "all" ? allFolderMessages : (folderMap.get(currentMailboxFolder)?.count ?? totalMessages);
  const moveFolderOptionGroups = [
    {
      key: "primary",
      items: primaryFolders
    },
    {
      key: "personal",
      items: personalFolders
    }
  ].filter((group) => group.items.length > 0);
  const rawSourceText =
    previewMessage && rawSourceLoadState.messageId === previewMessage.id ? rawSourceLoadState.value : "";
  const rawSourceLoading =
    previewMessage !== null && rawSourceLoadState.messageId === previewMessage.id && rawSourceLoadState.loading;

  function openRawSourceModal() {
    if (!previewMessage?.rawSourceAvailable) {
      return;
    }

    setDetailMenuState({ open: false, scope: detailPanelScopeKey });
    setRawSourceModalState({
      open: true,
      scope: detailPanelScopeKey
    });

    if (
      rawSourceLoadState.messageId === previewMessage.id &&
      (rawSourceLoadState.loading || rawSourceLoadState.value)
    ) {
      return;
    }

    const messageId = previewMessage.id;
    setRawSourceLoadState({
      error: null,
      loading: true,
      messageId,
      value: ""
    });

    void fetch(`/api/mailbox/message-source?message=${messageId}`, {
      cache: "no-store",
      credentials: "same-origin"
    })
      .then(async (response) => {
        if (!response.ok) {
          throw new Error("source-load-failed");
        }

        return response.text();
      })
      .then((value) => {
        setRawSourceLoadState({
          error: null,
          loading: false,
          messageId,
          value
        });
      })
      .catch(() => {
        setRawSourceLoadState({
          error: "메일 원본을 불러오지 못했습니다. 잠시 후 다시 시도해주세요.",
          loading: false,
          messageId,
          value: ""
        });
      });
  }
  const mailboxQuotaUsedBytes = mailData?.mailboxQuotaUsedBytes ?? null;
  const mailboxQuotaBytes = mailData?.mailboxQuotaBytes ?? null;
  const mailboxQuotaIsUnlimited = mailData?.mailboxQuotaIsUnlimited ?? false;
  const usagePercent =
    mailData?.mailboxQuotaUsagePercent !== null && mailData?.mailboxQuotaUsagePercent !== undefined
      ? Math.max(0, Math.min(100, mailData.mailboxQuotaUsagePercent))
      : mailboxQuotaUsedBytes !== null && mailboxQuotaBytes && mailboxQuotaBytes > 0
        ? Math.max(0, Math.min(100, Math.round((mailboxQuotaUsedBytes / mailboxQuotaBytes) * 100)))
        : null;
  const usageSummary =
    mailboxQuotaUsedBytes === null
      ? "집계 중"
      : mailboxQuotaIsUnlimited
        ? `${formatStorage(mailboxQuotaUsedBytes)} / 무제한`
        : mailboxQuotaBytes === null
          ? formatStorage(mailboxQuotaUsedBytes)
          : `${formatStorage(mailboxQuotaUsedBytes)} / ${formatStorage(mailboxQuotaBytes)}`;
  const usageProgressValue = usagePercent ?? 0;
  const usageProgressRatio = usageProgressValue / 100;
  const usageProgressStartHue = Math.round(144 - usageProgressRatio * 136);
  const usageProgressEndHue = Math.max(0, usageProgressStartHue - 22);
  const usageProgressFillStyle = {
    backgroundImage: `linear-gradient(90deg, hsl(${usageProgressStartHue} 72% 58%) 0%, hsl(${usageProgressEndHue} 88% 56%) 100%)`,
    boxShadow: `0 10px 24px hsla(${usageProgressEndHue}, 85%, 50%, 0.24)`
  } satisfies CSSProperties;
  const availableMoveFolder =
    sidebarFolders.find((folder) => folder.systemName === currentMailboxFolder) ??
    primaryFolders[0] ??
    personalFolders[0] ??
    null;
  const resolvedMoveTargetFolder = moveTargetFolder || availableMoveFolder?.systemName || "inbox";
  const personalFoldersExpanded =
    personalFoldersOpen || personalFolders.some((folder) => folder.systemName === currentMailboxFolder);

  const openComposeToAddress = (address: string) => {
    setSenderMenuState({ messageId: null, scope: interactionScopeKey });
    router.push(buildDirectComposeHref(currentMailboxFolder, address));
  };

  const copyToClipboard = async (value: string, message: string) => {
    try {
      if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
        await navigator.clipboard.writeText(value);
      } else if (typeof document !== "undefined") {
        const helper = document.createElement("textarea");
        helper.value = value;
        helper.setAttribute("readonly", "true");
        helper.style.position = "fixed";
        helper.style.opacity = "0";
        document.body.appendChild(helper);
        helper.select();
        document.execCommand("copy");
        helper.remove();
      } else {
        throw new Error("clipboard-unavailable");
      }

      setLocalNotice({ tone: "success", message });
    } catch {
      setLocalNotice({
        tone: "error",
        message: "클립보드에 복사하지 못했습니다."
      });
    }
  };

  const markMessagesReadOptimistically = (messageIds: number[], nextReadState: boolean) => {
    setMailData((currentData) => buildOptimisticReadStateMailData(currentData, messageIds, nextReadState));
  };

  const toggleMessageStarByClient = async (messageId: number) => {
    if (pendingStarMessageIds.includes(messageId)) {
      return false;
    }

    const currentStarred = resolveMessageStarState(mailData, messageId);

    if (currentStarred === null) {
      return false;
    }

    const nextStarred = !currentStarred;
    const previousData = mailData;
    const shouldReconcileImportantFilter = previousData?.messageFilter === "important" && !nextStarred;

    setMailData((currentData) => buildOptimisticStarToggledMailData(currentData, messageId, nextStarred));
    setPendingStarMessageIds((current) => [...current, messageId]);

    try {
      const response = await fetch("/api/mailbox/star", {
        body: JSON.stringify({
          isStarred: nextStarred,
          messageId
        }),
        headers: {
          "Content-Type": "application/json"
        },
        method: "POST"
      });

      if (response.status === 401) {
        window.location.assign("/login");
        return false;
      }

      const payload = (await response.json().catch(() => null)) as {
        isStarred?: boolean;
        notice?: MailNotice | null;
        ok?: boolean;
      } | null;

      if (!response.ok || !payload?.ok || typeof payload.isStarred !== "boolean") {
        throw new Error(payload?.notice?.message || "star-failed");
      }

      if (payload.isStarred !== nextStarred) {
        setMailData((currentData) =>
          buildOptimisticStarToggledMailData(currentData, messageId, payload.isStarred as boolean)
        );
      }

      if (shouldReconcileImportantFilter) {
        router.replace(
          folderHref(currentMailboxFolder, {
            page: resolvedCurrentPage,
            pageSize: resolvedPageSize,
            searchField: resolvedSearchField,
            searchQuery: resolvedSearchQuery,
            sortOrder: currentSortOrder,
            tab: resolvedMessageFilter
          }),
          { scroll: false }
        );
        void fetchMailboxData({
          messageId: null,
          preserveSelection: false,
          silent: true
        });
      }

      return true;
    } catch (error) {
      setMailData(previousData);
      setLocalNotice({
        tone: "error",
        message: error instanceof Error && error.message ? error.message : "중요 표시를 바꾸는 중 문제가 발생했습니다."
      });
      return false;
    } finally {
      setPendingStarMessageIds((current) => current.filter((id) => id !== messageId));
    }
  };

  const fetchMailboxData = async (options: MailboxDataFetchOptions = {}) => {
    if (!mailConfigured) {
      return false;
    }

    if (mailboxFetchStateRef.current.inFlight) {
      if (options.silent && !options.refresh) {
        mailboxFetchStateRef.current.queuedSilentRefresh = true;
      }

      return false;
    }

    mailboxFetchStateRef.current.inFlight = true;
    const requestMutationVersion = mailboxMutationVersionRef.current;

    try {
      const targetFolder = options.folder ?? currentMailboxFolder;
      const targetPage = options.page ?? resolvedCurrentPage;
      const targetPageSize = normalizeMailboxPageSize(options.pageSize ?? resolvedPageSize);
      const targetSearchField = options.searchField ?? resolvedSearchField;
      const targetSearchQuery = options.searchQuery ?? resolvedSearchQuery;
      const targetSortOrder = options.sortOrder ?? layoutStateRef.current.sortOrder;
      const targetTab = options.tab ?? resolvedMessageFilter;
      const targetMessageId = options.messageId === undefined ? (selectedMessage?.id ?? null) : options.messageId;
      const params = buildFolderSearchParams(targetFolder, {
        page: targetPage,
        pageSize: targetPageSize,
        searchField: targetSearchField,
        searchQuery: targetSearchQuery,
        sortOrder: targetSortOrder,
        tab: targetTab
      });
      params.set("locale", locale);

      if (targetMessageId) {
        params.set("message", String(targetMessageId));
      }

      if (options.refresh) {
        params.set("refresh", "1");
      } else if (options.staleRefresh) {
        params.set("refresh", "stale");
      }

      const response = await fetch(`/api/mailbox?${params.toString()}`, {
        method: "GET",
        cache: "no-store"
      });

      if (response.status === 401) {
        window.location.assign("/login");
        return false;
      }

      const payload = (await response.json()) as {
        cachedTranslation?: MessageTranslationData | null;
        data?: MailAppOverview | null;
        notice?: MailNotice | null;
      };

      if (requestMutationVersion !== mailboxMutationVersionRef.current || mailboxUndoInFlightRef.current) {
        return false;
      }

      if (payload.data) {
        const selectedTranslation = payload.cachedTranslation ?? null;
        const selectedMessageId = payload.data.selectedMessage?.id ?? null;
        setMessageTranslationState((current) => {
          if (selectedTranslation) {
            return {
              data: selectedTranslation,
              error: null,
              loading: false,
              messageId: selectedTranslation.messageId,
              targetLocale: selectedTranslation.targetLocale
            };
          }

          if (
            selectedMessageId !== null &&
            current.messageId === selectedMessageId &&
            current.targetLocale === locale &&
            (current.loading || current.data !== null || current.error !== null)
          ) {
            return current;
          }

          return {
            data: null,
            error: null,
            loading: false,
            messageId: null,
            targetLocale: null
          };
        });
        setMailData((currentData) => {
          if (requestMutationVersion !== mailboxMutationVersionRef.current) {
            return currentData;
          }

          if (mailboxUndoInFlightRef.current) {
            return currentData;
          }

          const nextData = applyPendingMailboxMoveProjections(payload.data ?? null);

          if (!nextData || pendingMailboxMovesRef.current.size === 0 || !currentData) {
            return nextData;
          }

          return {
            ...nextData,
            folders: currentData.folders
          };
        });

        if (options.syncUrl && typeof window !== "undefined") {
          const nextUrl = folderHref(payload.data.currentFolder, {
            page: payload.data.currentPage,
            pageSize: payload.data.pageSize,
            searchField: payload.data.searchField,
            searchQuery: payload.data.searchQuery,
            sortOrder: targetSortOrder,
            tab: payload.data.messageFilter
          });
          window.history.replaceState(window.history.state, "", nextUrl);
        }

        if (!options.preserveSelection) {
          setSelectionState({ ids: [], scope: interactionScopeKey });
          setMoveMenuState({ open: false, scope: interactionScopeKey });
        }
      }

      if (!options.silent) {
        setLocalNotice(
          payload.notice ??
            (options.refresh
              ? {
                  tone: "success",
                  message: "메일함을 다시 동기화했습니다."
                }
              : null)
        );
      }

      return true;
    } catch {
      if (!options.silent) {
        setLocalNotice({
          tone: "error",
          message: "메일함 새로고침 중 오류가 발생했습니다."
        });
      }

      return false;
    } finally {
      mailboxFetchStateRef.current.inFlight = false;

      if (mailboxFetchStateRef.current.queuedSilentRefresh) {
        mailboxFetchStateRef.current.queuedSilentRefresh = false;
        void fetchMailboxData({ preserveSelection: true, silent: true });
      }
    }
  };

  const reconcileMailboxPageAfterMutation = (
    options: Omit<MailboxDataFetchOptions, "preserveSelection" | "silent" | "syncUrl">
  ) =>
    retryMailboxDataFetch({
      isFetchInFlight: () => mailboxFetchStateRef.current.inFlight,
      fetchData: () =>
        fetchMailboxData({
          ...options,
          preserveSelection: false,
          silent: true,
          syncUrl: true
        })
    });

  const refreshMailboxFolderStatus = async (options: { force?: boolean; refreshCurrent?: boolean } = {}) => {
    if (!isMailboxPanel || !mailConfigured || reconnectRequired) {
      return false;
    }

    if (mailboxFolderStatusStateRef.current.inFlight) {
      if (options.force) {
        mailboxFolderStatusStateRef.current.queuedForce = true;
      }
      return false;
    }

    mailboxFolderStatusStateRef.current.inFlight = true;
    const requestMutationVersion = mailboxMutationVersionRef.current;

    try {
      const params = new URLSearchParams({ folder: currentMailboxFolder });

      if (options.force) {
        params.set("force", "1");
      }

      const response = await fetch(`/api/mailbox/folder-status?${params.toString()}`, {
        cache: "no-store",
        method: "GET"
      });

      if (response.status === 401) {
        window.location.assign("/login");
        return false;
      }

      const payload = (await response.json()) as MailboxFolderStatusResponse;

      if (!response.ok || !payload.ok || !payload.status) {
        return false;
      }

      if (
        requestMutationVersion !== mailboxMutationVersionRef.current ||
        mailboxUndoInFlightRef.current ||
        pendingMailboxMovesRef.current.size > 0
      ) {
        return false;
      }

      const folderStatusMap = new Map(payload.status.folders.map((folder) => [folder.systemName, folder]));

      setMailData((currentData) =>
        requestMutationVersion === mailboxMutationVersionRef.current && currentData
          ? {
              ...currentData,
              folders: currentData.folders.map((folder) => {
                const status = folderStatusMap.get(folder.systemName);

                return status
                  ? {
                      ...folder,
                      count: status.count,
                      unreadCount: status.unreadCount
                    }
                  : folder;
              })
            }
          : currentData
      );

      if (payload.status.currentFolderChanged && options.refreshCurrent !== false) {
        await mailboxDataFetchRef.current({
          preserveSelection: true,
          silent: true
        });
      }

      return true;
    } catch {
      return false;
    } finally {
      mailboxFolderStatusStateRef.current.inFlight = false;

      if (mailboxFolderStatusStateRef.current.queuedForce) {
        mailboxFolderStatusStateRef.current.queuedForce = false;
        void mailboxFolderStatusFetchRef.current({ force: true });
      }
    }
  };

  const syncMailbox = () => {
    if (!mailConfigured || fullMailboxSyncStateRef.current.inFlight) {
      return;
    }

    fullMailboxSyncStateRef.current.inFlight = true;

    void fetch("/api/mailbox/sync-all", {
      cache: "no-store",
      method: "POST"
    })
      .then(async (response) => {
        if (response.status === 401) {
          window.location.assign("/login");
          return;
        }

        if (!response.ok) {
          throw new Error(`mailbox-full-sync-failed:${response.status}`);
        }

        await mailboxFolderStatusFetchRef.current({
          force: true,
          refreshCurrent: false
        });
        await mailboxDataFetchRef.current({
          preserveSelection: true,
          silent: true
        });
      })
      .catch(() => {
        setLocalNotice({
          tone: "error",
          message: "메일함 동기화를 완료하지 못했습니다."
        });
      })
      .finally(() => {
        fullMailboxSyncStateRef.current.inFlight = false;
      });
  };

  const applySearch = (field: MailSearchField, query: string) => {
    const normalizedQuery = query.trim();

    if (normalizedQuery) {
      const nextRecentSearches = [
        normalizedQuery,
        ...recentSearches.filter((item) => item.toLocaleLowerCase() !== normalizedQuery.toLocaleLowerCase())
      ].slice(0, 5);
      setRecentSearches(nextRecentSearches);
      try {
        window.localStorage.setItem(recentSearchStorageKey, JSON.stringify(nextRecentSearches));
      } catch {
        // Search remains available when local storage is blocked.
      }
    }

    setSearchInput(normalizedQuery);
    setSearchOpen(false);
    setSearchSuggestions([]);
    setSearchHighlightedIndex(-1);
    setDismissedDetailScopeKey(null);
    router.push(
      folderHref("all", {
        page: 1,
        pageSize: resolvedPageSize,
        searchField: normalizedQuery ? field : "all",
        searchQuery: normalizedQuery,
        sortOrder: currentSortOrder,
        tab: "all"
      }),
      { scroll: false }
    );
  };

  const clearSearch = () => {
    setSearchInput("");
    setSearchOpen(false);
    setSearchSuggestions([]);
    setSearchHighlightedIndex(-1);
    searchInputRef.current?.blur();

    if (!isMailboxPanel || !resolvedSearchQuery) {
      return;
    }

    setDismissedDetailScopeKey(null);
    router.push(
      folderHref(currentMailboxFolder, {
        page: 1,
        pageSize: resolvedPageSize,
        sortOrder: currentSortOrder,
        tab: resolvedMessageFilter
      }),
      { scroll: false }
    );
  };

  const applySearchSuggestion = (suggestion: MailSearchSuggestion) => {
    applySearch(suggestion.field, suggestion.value);
  };

  const applyPageSize = async (nextPageSize: number) => {
    const pageSize = normalizeMailboxPageSize(nextPageSize);

    if (pageSize === resolvedPageSize) {
      return;
    }

    setToolbarMenuKind(null);
    setDismissedDetailScopeKey(null);
    setSelectionState({ ids: [], scope: interactionScopeKey });
    setMoveMenuState({ open: false, scope: interactionScopeKey });

    const updated = await fetchMailboxData({
      messageId: null,
      page: 1,
      pageSize,
      preserveSelection: false,
      searchField: resolvedSearchField,
      searchQuery: resolvedSearchQuery,
      silent: true,
      sortOrder: currentSortOrder,
      syncUrl: true,
      tab: resolvedMessageFilter
    });

    if (!updated) {
      setLocalNotice({
        tone: "error",
        message: "표시 개수를 변경하는 중 오류가 발생했습니다."
      });
    }
  };

  const visibleSearchSuggestions = searchInput.trim() ? searchSuggestions.slice(0, 2) : [];

  useEffect(() => {
    if (!searchDropdownVisible) {
      return;
    }

    const handlePointerDown = (event: MouseEvent) => {
      const target = event.target;

      if (!(target instanceof HTMLElement)) {
        return;
      }

      if (!target.closest("[data-mail-search-root='true']")) {
        setSearchOpen(false);
        setSearchHighlightedIndex(-1);
      }
    };

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setSearchOpen(false);
        setSearchHighlightedIndex(-1);
      }
    };

    document.addEventListener("mousedown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);

    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [searchDropdownVisible]);

  useEffect(() => {
    if (!mailConfigured || !searchOpen || !deferredSearchInput) {
      return;
    }

    const controller = new AbortController();
    const params = new URLSearchParams({
      folder: "all",
      q: deferredSearchInput
    });
    const timer = window.setTimeout(async () => {
      setSearchLoading(true);

      try {
        const response = await fetch(`/api/mailbox/search?${params.toString()}`, {
          cache: "no-store",
          method: "GET",
          signal: controller.signal
        });

        if (!response.ok) {
          throw new Error(`mail-search-failed:${response.status}`);
        }

        const payload = (await response.json()) as {
          suggestions?: MailSearchSuggestion[];
        };
        const nextSuggestions = payload.suggestions ?? [];

        setSearchSuggestions(nextSuggestions);
        setSearchHighlightedIndex(-1);
      } catch (error) {
        if (
          error instanceof DOMException &&
          (error.name === "AbortError" || error.message === "The operation was aborted.")
        ) {
          return;
        }

        setSearchSuggestions([]);
        setSearchHighlightedIndex(-1);
      } finally {
        setSearchLoading(false);
      }
    }, 120);

    return () => {
      controller.abort();
      window.clearTimeout(timer);
    };
  }, [currentMailboxFolder, deferredSearchInput, isMailboxPanel, mailConfigured, searchOpen]);

  useEffect(() => {
    if (!moveMenuOpen) {
      return;
    }

    const handlePointerDown = (event: MouseEvent) => {
      const target = event.target;

      if (!(target instanceof HTMLElement)) {
        return;
      }

      if (!target.closest("[data-move-menu-root='true']")) {
        setMoveMenuState({ open: false, scope: interactionScopeKey });
      }
    };

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        if (continueMovePrompt && !pendingMoveKey) {
          setContinueMovePrompt(null);
        } else {
          setMoveMenuState({ open: false, scope: interactionScopeKey });
        }
      }
    };

    document.addEventListener("mousedown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);

    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [continueMovePrompt, interactionScopeKey, moveMenuOpen, pendingMoveKey]);

  useEffect(() => {
    if (!toolbarMenuKind) {
      return;
    }

    const handlePointerDown = (event: MouseEvent) => {
      const target = event.target;

      if (!(target instanceof HTMLElement)) {
        return;
      }

      if (!target.closest("[data-toolbar-menu-root='true']")) {
        setToolbarMenuState({ kind: null, scope: interactionScopeKey });
      }
    };

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setToolbarMenuState({ kind: null, scope: interactionScopeKey });
      }
    };

    document.addEventListener("mousedown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);

    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [interactionScopeKey, toolbarMenuKind]);

  useEffect(() => {
    if (sidebarFolderMenuId === null) {
      return;
    }

    const updatePosition = () => {
      const row = folderRowRefs.current.get(sidebarFolderMenuId);

      if (!row) {
        setSidebarFolderMenuPosition(null);
        return;
      }

      const rect = row.getBoundingClientRect();
      setSidebarFolderMenuPosition({
        top: window.scrollY + rect.top + rect.height / 2,
        left: window.scrollX + rect.right + 10
      });
    };

    const handlePointerDown = (event: MouseEvent) => {
      const target = event.target;

      if (!(target instanceof HTMLElement)) {
        return;
      }

      if (!target.closest("[data-sidebar-folder-menu-root='true']")) {
        setSidebarFolderMenuState({
          folderId: null,
          scope: interactionScopeKey
        });
      }
    };

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setSidebarFolderMenuState({
          folderId: null,
          scope: interactionScopeKey
        });
      }
    };

    const animationFrameId = window.requestAnimationFrame(updatePosition);

    document.addEventListener("mousedown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);
    document.addEventListener("scroll", updatePosition, true);
    window.addEventListener("resize", updatePosition);

    return () => {
      window.cancelAnimationFrame(animationFrameId);
      document.removeEventListener("mousedown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
      document.removeEventListener("scroll", updatePosition, true);
      window.removeEventListener("resize", updatePosition);
    };
  }, [interactionScopeKey, sidebarFolderMenuId]);

  useEffect(() => {
    if (!settingsMenuOpen) {
      return;
    }

    const handlePointerDown = (event: MouseEvent) => {
      const target = event.target;

      if (!(target instanceof HTMLElement)) {
        return;
      }

      if (!target.closest("[data-settings-menu-root='true']")) {
        setSettingsMenuState({ open: false, scope: settingsMenuScopeKey });
      }
    };

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setSettingsMenuState({ open: false, scope: settingsMenuScopeKey });
      }
    };

    document.addEventListener("mousedown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);

    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [settingsMenuOpen, settingsMenuScopeKey]);

  useEffect(() => {
    mailboxDataFetchRef.current = fetchMailboxData;
    mailboxFolderStatusFetchRef.current = refreshMailboxFolderStatus;
  });

  useEffect(() => {
    if (!isMailboxPanel || selectedMessage?.direction !== "outbound" || selectedMessage.transportStatus !== "queued") {
      return;
    }

    // Only poll while this exact message is queued; delivery itself happens in
    // the worker and must never depend on the compose request staying open.
    const intervalId = window.setInterval(() => {
      if (document.visibilityState === "visible") {
        void mailboxDataFetchRef.current({
          messageId: selectedMessage.id,
          preserveSelection: true,
          silent: true,
        });
      }
    }, 5_000);

    return () => window.clearInterval(intervalId);
  }, [isMailboxPanel, selectedMessage?.direction, selectedMessage?.id, selectedMessage?.transportStatus]);

  useEffect(() => {
    if (!isMailboxPanel || !selectedMessage?.id || !hasPendingLargeAttachmentLinks) return;

    const receivedAt = Date.parse(selectedMessage.receivedAt);
    if (!Number.isFinite(receivedAt) || Date.now() - receivedAt > 10 * 60_000) return;

    // An inbound copy can arrive before the outbound worker publishes its
    // large-attachment link. Refresh only recent affected messages, with a cap.
    const messageId = selectedMessage.id;
    let attempts = 0;
    const intervalId = window.setInterval(() => {
      if (document.visibilityState !== "visible") return;
      attempts += 1;
      void mailboxDataFetchRef.current({
        messageId,
        preserveSelection: true,
        silent: true,
      });
      if (attempts >= 10) window.clearInterval(intervalId);
    }, 3_000);

    return () => window.clearInterval(intervalId);
  }, [hasPendingLargeAttachmentLinks, isMailboxPanel, selectedMessage?.id, selectedMessage?.receivedAt]);

  useEffect(() => {
    if (!isMailboxPanel || !mailConfigured || reconnectRequired) {
      return;
    }

    const refreshWhenVisible = (force: boolean) => {
      if (document.visibilityState !== "visible") {
        return;
      }

      void mailboxFolderStatusFetchRef.current({ force });
    };
    const intervalId = window.setInterval(() => refreshWhenVisible(false), 60_000);
    const handleVisibilityChange = () => {
      if (document.visibilityState === "visible") {
        refreshWhenVisible(true);
      }
    };

    // Route changes already carry the latest database snapshot. Respect the
    // short server cache instead of forcing a new IMAP status pass per click.
    refreshWhenVisible(false);
    document.addEventListener("visibilitychange", handleVisibilityChange);

    return () => {
      window.clearInterval(intervalId);
      document.removeEventListener("visibilitychange", handleVisibilityChange);
    };
  }, [currentMailboxFolder, isMailboxPanel, mailConfigured, reconnectRequired]);

  useEffect(() => {
    if ((!isMailboxPanel && !isAutoSendPanel) || !mailConfigured || reconnectRequired) {
      return;
    }

    const eventMode = isMailboxPanel ? "all" : "auto-send";
    let source: EventSource | null = null;

    const handleMailboxUpdated = (event: Event) => {
      if (!isMailboxPanel) {
        return;
      }
      if (!(event instanceof MessageEvent)) {
        return;
      }

      let payload: MailboxLiveUpdatePayload | null = null;

      try {
        payload = JSON.parse(event.data) as MailboxLiveUpdatePayload;
      } catch {
        return;
      }

      void mailboxDataFetchRef.current({ preserveSelection: true, silent: true }).then((updated) => {
        if (
          updated &&
          payload &&
          payload.reason === "exists" &&
          payload.delta > 0 &&
          (currentMailboxFolder === "all" || currentMailboxFolder === "inbox")
        ) {
          setLocalNotice({
            tone: "success",
            message: payload.delta === 1 ? "새 메일 1건이 도착했습니다." : `새 메일 ${payload.delta}건이 도착했습니다.`
          });
        }
      });
    };

    const handleAutoSendCompleted = (event: Event) => {
      if (!(event instanceof MessageEvent)) {
        return;
      }

      try {
        const payload = JSON.parse(event.data) as MailboxAutoSendCompletedPayload;
        const deliveryCount = Math.max(1, Number(payload.deliveryCount) || 1);
        setLocalNotice({
          tone: "success",
          message:
            deliveryCount === 1
              ? `자동발송 메일을 ${payload.recipientEmail}에게 보냈어요.`
              : `자동발송 메일 ${deliveryCount}건을 보냈어요.`
        });
      } catch {
        return;
      }
    };

    const handleMailboxError = (event: Event) => {
      if (!isMailboxPanel) {
        return;
      }
      if (!(event instanceof MessageEvent)) {
        return;
      }

      try {
        const payload = JSON.parse(event.data) as MailboxLiveErrorPayload;

        if (payload.message === "mailbox-auth-missing" || payload.message === "mailbox-auth-invalid") {
          setLocalNotice({
            tone: "error",
            message: "실시간 메일 동기화를 시작할 수 없습니다. 메일함 인증 정보를 다시 확인해주세요."
          });
        }
      } catch {
        // Ignore malformed realtime error events.
      }
    };

    const disconnect = () => {
      if (!source) {
        return;
      }

      source.removeEventListener("mailbox-updated", handleMailboxUpdated);
      source.removeEventListener("auto-send-completed", handleAutoSendCompleted);
      source.removeEventListener("mailbox-error", handleMailboxError);
      source.close();
      source = null;
    };
    const connect = () => {
      if (source || document.visibilityState !== "visible") {
        return;
      }

      source = new EventSource(
        `/api/mailbox/live?folder=${encodeURIComponent(currentMailboxFolder)}&events=${eventMode}`
      );
      source.addEventListener("mailbox-updated", handleMailboxUpdated);
      source.addEventListener("auto-send-completed", handleAutoSendCompleted);
      source.addEventListener("mailbox-error", handleMailboxError);
    };
    const handleVisibilityChange = () => {
      if (document.visibilityState === "visible") {
        connect();
      } else {
        disconnect();
      }
    };

    connect();
    document.addEventListener("visibilitychange", handleVisibilityChange);

    return () => {
      document.removeEventListener("visibilitychange", handleVisibilityChange);
      disconnect();
    };
  }, [
    currentMailboxFolder,
    isAutoSendPanel,
    isMailboxPanel,
    mailConfigured,
    reconnectRequired
  ]);

  const saveLayoutPreferences = async (nextLayout: MailLayoutPreferences) => {
    try {
      const response = await fetch("/api/mail-layout", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(nextLayout)
      });

      if (!response.ok) {
        throw new Error(`mail-layout-save-failed:${response.status}`);
      }

      const payload = (await response.json()) as {
        preferences?: MailLayoutPreferences;
      };

      savedLayoutRef.current = normalizeMailLayoutPreferences(payload.preferences ?? nextLayout);
    } catch (error) {
      console.error("Failed to save mail layout preferences", error);
    }
  };

  const applyLayoutPreferences = (nextLayout: MailLayoutPreferences) => {
    const normalized = normalizeMailLayoutPreferences(nextLayout);
    layoutStateRef.current = normalized;
    setLayoutState(normalized);
    void saveLayoutPreferences(normalized);
    return normalized;
  };

  const setToolbarMenuKind = (kind: ToolbarMenuKind | null) => {
    setToolbarMenuState({ kind, scope: interactionScopeKey });
  };

  const navigateWithinMailbox = (
    href: string,
    options?: {
      intent?: "auto-send" | "mailbox" | "setup";
      replace?: boolean;
    }
  ) => {
    if (typeof window !== "undefined") {
      const currentUrl = new URL(window.location.href);
      const nextUrl = new URL(href, window.location.origin);

      if (
        currentUrl.pathname === nextUrl.pathname &&
        currentUrl.search === nextUrl.search &&
        currentUrl.hash === nextUrl.hash
      ) {
        return;
      }
    }

    setDismissedDetailScopeKey(null);
    setToolbarMenuKind(null);
    setMoveMenuState({ open: false, scope: interactionScopeKey });
    setSenderMenuState({ messageId: null, scope: interactionScopeKey });
    setSenderMenuAnchor(null);
    setDetailSenderMenuAnchor(null);
    setSidebarFolderMenuState({ folderId: null, scope: interactionScopeKey });
    setSettingsMenuState((current) => ({ ...current, open: false }));
    setSearchOpen(false);
    setSearchHighlightedIndex(-1);
    setSearchInput(
      typeof window === "undefined"
        ? ""
        : new URL(href, window.location.origin).searchParams.get("q") ?? ""
    );
    setMobileSidebarOpen(false);
    setMobileSearchVisible(false);
    setPendingPanelNavigation({
      href,
      intent: options?.intent ?? "mailbox"
    });

    startPanelNavigationTransition(() => {
      if (options?.replace) {
        router.replace(href, { scroll: false });
        return;
      }

      router.push(href, { scroll: false });
    });
  };

  const handleMailboxNavigationLinkClick = (
    event: ReactMouseEvent<HTMLAnchorElement>,
    href: string,
    options?: {
      intent?: "auto-send" | "mailbox" | "setup";
      replace?: boolean;
    }
  ) => {
    if (shouldBypassClientNavigation(event)) {
      return;
    }

    event.preventDefault();
    navigateWithinMailbox(href, options);
  };

  const applyViewMode = (viewMode: MailListViewMode) => {
    if (layoutStateRef.current.viewMode === viewMode) {
      setToolbarMenuKind(null);
      return;
    }

    applyLayoutPreferences({
      ...layoutStateRef.current,
      viewMode
    });
    setDismissedDetailScopeKey(null);
    setToolbarMenuKind(null);

    if (viewMode === "list-only") {
      setSelectionState({ ids: [], scope: interactionScopeKey });
    } else if (activePreviewMessageId) {
      setSelectionState({
        ids: [activePreviewMessageId],
        scope: interactionScopeKey
      });
    }
  };

  const applySortOrder = async (sortOrder: MailListSortOrder) => {
    if (layoutStateRef.current.sortOrder === sortOrder) {
      return;
    }

    applyLayoutPreferences({
      ...layoutStateRef.current,
      sortOrder
    });
    setToolbarMenuKind(null);
    setDismissedDetailScopeKey(null);
    setSelectionState({ ids: [], scope: interactionScopeKey });
    setMoveMenuState({ open: false, scope: interactionScopeKey });

    const updated = await fetchMailboxData({
      messageId: null,
      page: 1,
      pageSize: resolvedPageSize,
      preserveSelection: false,
      searchField: resolvedSearchField,
      searchQuery: resolvedSearchQuery,
      silent: true,
      sortOrder,
      syncUrl: true,
      tab: resolvedMessageFilter
    });

    if (!updated) {
      const fallbackLayout = normalizeMailLayoutPreferences(savedLayoutRef.current);
      layoutStateRef.current = fallbackLayout;
      setLayoutState(fallbackLayout);
      setLocalNotice({
        tone: "error",
        message: "정렬을 변경하는 중 오류가 발생했습니다."
      });
    }
  };

  const saveFolderOrder = async (nextOrder: number[]) => {
    try {
      const response = await fetch("/api/mail-layout", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ folderOrder: nextOrder })
      });

      if (!response.ok) {
        throw new Error(`mail-folder-order-save-failed:${response.status}`);
      }
    } catch (error) {
      console.error("Failed to save mailbox folder order", error);
      setLocalNotice({
        tone: "error",
        message: "메일함 순서를 저장하는 중 오류가 발생했습니다."
      });
    }
  };

  const folderRowRefs = useRef(new Map<number, HTMLDivElement>());
  const folderRowTopPositionsRef = useRef(new Map<number, number>());
  const folderRowAnimationsRef = useRef(new Map<number, Animation>());
  const sidebarFolderEditorInputRef = useRef<HTMLInputElement | null>(null);
  const folderDragPreviewRef = useRef<HTMLDivElement | null>(null);
  const messageRowRefs = useRef(new Map<number, HTMLDivElement>());
  const messageDragPreviewRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    const animations = folderRowAnimationsRef.current;

    return () => {
      animations.forEach((animation) => animation.cancel());
      animations.clear();
    };
  }, []);

  const clearFolderDragPreview = () => {
    folderDragPreviewRef.current?.remove();
    folderDragPreviewRef.current = null;
  };

  const clearMessageDragPreview = () => {
    messageDragPreviewRef.current?.remove();
    messageDragPreviewRef.current = null;
  };

  const finishMessageDrag = () => {
    clearMessageDragPreview();
    setDraggingMessageIds(null);
    setDragOverMessageFolder(null);
  };

  const canDropMessagesToFolder = (
    targetFolderSystemName: string,
    messageIdsInput: number[] | null = draggingMessageIds
  ) => {
    if (!messageIdsInput || messageIdsInput.length === 0 || targetFolderSystemName === "all") {
      return false;
    }

    const messages = messageIdsInput
      .map((messageId) => messageMap.get(messageId) ?? null)
      .filter((message): message is MailMessageSummary => Boolean(message));

    if (messages.length === 0) {
      return false;
    }

    if (targetFolderSystemName === "sent" && messages.some((message) => message.direction !== "outbound")) {
      return false;
    }

    return messages.some(
      (message) => message.folderSystemName !== targetFolderSystemName || targetFolderSystemName === "trash"
    );
  };

  const buildOptimisticMovedMailData = (
    currentData: MailAppOverview | null,
    messageIdsInput: number[],
    targetFolderSystemName: string,
    moveFolderCounts?: MailSenderClassificationInput["moveFolderCounts"],
    forceFolderCounts = false
  ) => {
    if (!currentData) {
      return currentData;
    }

    const messageIdsSet = new Set(messageIdsInput);
    const movingMessages = currentData.messages.filter((message) => messageIdsSet.has(message.id));
    const movedAwayMessages = movingMessages.filter(
      (message) => message.folderSystemName !== targetFolderSystemName || targetFolderSystemName === "trash"
    );

    if (movedAwayMessages.length === 0 && !(forceFolderCounts && moveFolderCounts?.length)) {
      return currentData;
    }

    const movedAwayIds = new Set(movedAwayMessages.map((message) => message.id));
    const targetFolder = currentData.folders.find((folder) => folder.systemName === targetFolderSystemName);
    const remainsVisible = (message: MailAppOverview["messages"][number]) =>
      !(message.folderSystemName === "trash" && targetFolderSystemName === "trash") &&
      (currentData.currentFolder === targetFolderSystemName ||
        (currentData.currentFolder === "all" && message.direction === "inbound" &&
          !["sent", "drafts", "spam", "trash"].includes(targetFolderSystemName)));
    const removedMessages = movedAwayMessages.filter((message) => !remainsVisible(message));
    const removedIds = new Set(removedMessages.map((message) => message.id));
    const nextMessages = currentData.messages.filter((message) => !removedIds.has(message.id)).map((message) =>
      movedAwayIds.has(message.id)
        ? { ...message, folderSystemName: targetFolderSystemName, folderName: targetFolder?.name ?? message.folderName }
        : message
    );
    if (mailPageFillBuffer?.scope === interactionScopeKey) {
      const existingIds = new Set(nextMessages.map((message) => message.id));
      for (const message of mailPageFillBuffer.messages) {
        if (
          nextMessages.length >= currentData.pageSize ||
          existingIds.has(message.id) ||
          messageIdsSet.has(message.id)
        ) {
          continue;
        }
        nextMessages.push(message);
        existingIds.add(message.id);
      }
    }
    const nextFolders = currentData.folders.map((folder) => {
      let nextCount = folder.count;
      let nextUnreadCount = folder.unreadCount;

      for (const message of movedAwayMessages) {
        if (folder.systemName === message.folderSystemName) {
          nextCount -= 1;

          if (!message.isRead) {
            nextUnreadCount -= 1;
          }
        }

        if (folder.systemName === targetFolderSystemName) {
          if (targetFolderSystemName === "trash" && message.folderSystemName === "trash") {
            continue;
          }

          nextCount += 1;

          if (!message.isRead) {
            nextUnreadCount += 1;
          }
        }
      }

      if (moveFolderCounts?.length) {
        nextCount = folder.count;
        nextUnreadCount = folder.unreadCount;
        for (const source of moveFolderCounts) {
          if (source.folderSystemName === targetFolderSystemName) continue;
          if (folder.systemName === source.folderSystemName) {
            nextCount -= source.messageCount;
            nextUnreadCount -= source.unreadCount;
          }
          if (folder.systemName === targetFolderSystemName) {
            nextCount += source.messageCount;
            nextUnreadCount += source.unreadCount;
          }
        }
      }

      return {
        ...folder,
        count: Math.max(0, nextCount),
        unreadCount: Math.max(0, nextUnreadCount)
      };
    });
    const nextFilterCounts = {
      all: Math.max(0, currentData.messageFilterCounts.all - removedMessages.length),
      unread: Math.max(
        0,
        currentData.messageFilterCounts.unread - removedMessages.filter((message) => !message.isRead).length
      ),
      important: Math.max(
        0,
        currentData.messageFilterCounts.important - removedMessages.filter((message) => message.isStarred).length
      ),
      attachments: Math.max(
        0,
        currentData.messageFilterCounts.attachments -
          removedMessages.filter((message) => looksLikeAttachment(message)).length
      )
    };
    const currentFolderCounts = !currentData.searchQuery && currentData.currentFolder !== targetFolderSystemName
      ? moveFolderCounts?.find((source) => source.folderSystemName === currentData.currentFolder)
      : undefined;
    if (currentFolderCounts) {
      nextFilterCounts.all = Math.max(0, currentData.messageFilterCounts.all - currentFolderCounts.messageCount);
      nextFilterCounts.unread = Math.max(0, currentData.messageFilterCounts.unread - currentFolderCounts.unreadCount);
    }
    const removedCount = currentFolderCounts && currentData.messageFilter === "all"
      ? currentFolderCounts.messageCount
      : currentFolderCounts && currentData.messageFilter === "unread"
        ? currentFolderCounts.unreadCount
        : removedMessages.length;
    const nextTotalMessages = Math.max(0, currentData.totalMessages - removedCount);
    const nextTotalPages =
      currentData.pageSize > 0
        ? Math.max(1, Math.ceil(nextTotalMessages / currentData.pageSize))
        : currentData.totalPages;
    const selectedMessageMoved =
      currentData.selectedMessage !== null && removedIds.has(currentData.selectedMessage.id);

    return {
      ...currentData,
      currentPage: Math.min(currentData.currentPage, nextTotalPages),
      folders: nextFolders,
      messageFilterCounts: nextFilterCounts,
      messages: nextMessages,
      selectedMessage: selectedMessageMoved ? null : currentData.selectedMessage && movedAwayIds.has(currentData.selectedMessage.id)
        ? { ...currentData.selectedMessage, folderSystemName: targetFolderSystemName, folderName: targetFolder?.name ?? currentData.selectedMessage.folderName }
        : currentData.selectedMessage,
      selectedMessageLogs: selectedMessageMoved ? [] : currentData.selectedMessageLogs,
      totalMessages: nextTotalMessages,
      totalPages: nextTotalPages
    };
  };

  const buildOptimisticClearedFolderData = (
    currentData: MailAppOverview | null,
    folderSystemName: string
  ) => {
    if (!currentData) {
      return currentData;
    }

    const clearingCurrentFolder = currentData.currentFolder === folderSystemName;

    return {
      ...currentData,
      currentPage: clearingCurrentFolder ? 1 : currentData.currentPage,
      folders: currentData.folders.map((folder) =>
        folder.systemName === folderSystemName
          ? { ...folder, count: 0, unreadCount: 0 }
          : folder
      ),
      messageFilterCounts: clearingCurrentFolder
        ? { all: 0, unread: 0, important: 0, attachments: 0 }
        : currentData.messageFilterCounts,
      messages: clearingCurrentFolder ? [] : currentData.messages,
      selectedMessage: clearingCurrentFolder ? null : currentData.selectedMessage,
      selectedMessageLogs: clearingCurrentFolder ? [] : currentData.selectedMessageLogs,
      totalMessages: clearingCurrentFolder ? 0 : currentData.totalMessages,
      totalPages: clearingCurrentFolder ? 1 : currentData.totalPages
    };
  };

  const applyPendingMailboxMoveProjections = (currentData: MailAppOverview | null) => {
    let nextData = currentData;

    for (const projection of pendingMailboxMovesRef.current.values()) {
      nextData = buildOptimisticMovedMailData(nextData, projection.messageIds, projection.targetFolder, projection.moveFolderCounts);
    }

    return nextData;
  };

  if (receivedMailDataScope !== incomingMailDataScope) {
    setReceivedMailDataScope(incomingMailDataScope);
    setMailData(data);
    setMailPageFillBuffer(null);
    setSearchInput(currentSearchQuery);
    setMessageTranslationState({
      data: initialMessageTranslation,
      error: null,
      loading: false,
      messageId: initialMessageTranslation?.messageId ?? null,
      targetLocale: initialMessageTranslation?.targetLocale ?? null
    });
    setOriginalMessageScope("");
  }

  const addPendingMailboxMoveProjection = (projection: PendingMailboxMoveProjection) => {
    pendingMailboxMovesRef.current.set(projection.key, projection);
    mailboxMutationVersionRef.current += 1;
  };

  const removePendingMailboxMoveProjection = (moveKey: string) => {
    if (pendingMailboxMovesRef.current.delete(moveKey)) {
      mailboxMutationVersionRef.current += 1;
    }
  };

  const monitorMailboxMoveJob = async (
    jobId: number,
    sourceFolder: string,
    sourceQueryKey: string,
    optimisticData: MailAppOverview | null,
    moveKey: string
  ) => {
    for (let attempt = 0; attempt < 300; attempt += 1) {
      await new Promise<void>((resolve) => window.setTimeout(resolve, 300));

      try {
        const response = await fetch(`/api/mailbox/move-jobs/${jobId}`, {
          cache: "no-store",
          method: "GET"
        });
        if (response.status === 401) {
          removePendingMailboxMoveProjection(moveKey);
          window.location.assign("/login");
          return;
        }
        if (!response.ok) continue;

        const payload = (await response.json()) as MailboxMoveJobResponse;
        if (!payload.ok || !payload.job) continue;
        if (payload.job.status === "pending" || payload.job.status === "processing") {
          continue;
        }

        removePendingMailboxMoveProjection(moveKey);

        if (undoRequestedMoveKeysRef.current.has(moveKey)) {
          undoRequestedMoveKeysRef.current.delete(moveKey);
          return;
        }

        if (currentMailboxQueryKeyRef.current === sourceQueryKey) {
          await reconcileMailboxPageAfterMutation({
            folder: optimisticData?.currentFolder ?? sourceFolder,
            messageId: null,
            page: optimisticData?.currentPage ?? 1,
            pageSize: optimisticData?.pageSize ?? resolvedPageSize,
            searchField: optimisticData?.searchField ?? resolvedSearchField,
            searchQuery: optimisticData?.searchQuery ?? resolvedSearchQuery,
            sortOrder: currentSortOrder,
            tab: optimisticData?.messageFilter ?? resolvedMessageFilter
          });
        } else {
          void mailboxFolderStatusFetchRef.current({ force: true });
        }

        if (payload.job.status === "failed") {
          setLocalNotice({
            message: "메일 이동을 완료하지 못해 현재 서버 상태로 목록을 복구했습니다. 다시 시도해주세요.",
            tone: "error"
          });
        }
        return;
      } catch {
        // A transient status request failure is retried until the deadline.
      }
    }

    removePendingMailboxMoveProjection(moveKey);
    if (undoRequestedMoveKeysRef.current.has(moveKey)) {
      undoRequestedMoveKeysRef.current.delete(moveKey);
      return;
    }
    if (currentMailboxQueryKeyRef.current === sourceQueryKey) {
      await mailboxDataFetchRef.current({
        preserveSelection: false,
        silent: true
      });
    }
    setLocalNotice({
      message: "메일 이동 상태 확인이 지연되고 있습니다. 목록을 새로고침해주세요.",
      tone: "error"
    });
  };

  const saveSenderClassificationByClient = async (input: MailSenderClassificationInput) => {
    const previousData = mailData;
    const sourceQueryKey = currentMailboxQueryKey;
    const moveKey = `classification:${input.senderAddress}:${mailboxMutationVersionRef.current + 1}`;
    const messageIds = input.moveExistingMessages
      ? [...(previousData?.messages ?? []), ...(mailPageFillBuffer?.scope === interactionScopeKey ? mailPageFillBuffer.messages : [])].filter((message) => message.direction === "inbound" &&
          (message.folderSystemName === "inbox" || message.folderSystemName === input.previousTargetFolder) &&
          message.fromAddress.trim().toLowerCase() === input.senderAddress.trim().toLowerCase()
        ).map((message) => message.id)
      : [];
    const moveFolderCounts = input.moveExistingMessages ? input.moveFolderCounts?.filter((source) =>
      source.folderSystemName === "inbox" || source.folderSystemName === input.previousTargetFolder
    ) : undefined;
    const nextData = buildOptimisticMovedMailData(previousData, messageIds, input.targetFolder, moveFolderCounts, true);
    addPendingMailboxMoveProjection({ key: moveKey, messageIds, targetFolder: input.targetFolder, moveFolderCounts });
    const mutationVersion = mailboxMutationVersionRef.current;
    setMailData(nextData);
    setLocalNotice(null);

    try {
      const response = await fetch("/api/mailbox/move-rules", {
        method: "POST", keepalive: true, headers: { "Content-Type": "application/json" }, body: JSON.stringify(input)
      });
      const payload = await response.json() as {
        ok?: boolean; message?: string; rule?: MailboxSenderMoveRule;
        queued?: boolean; jobId?: number; result?: { movedMessageIds?: number[] };
      };
      if (!response.ok || !payload.ok || !payload.rule) throw new Error(payload.message || "자동분류를 저장하지 못했습니다.");
      const savedRule = payload.rule;
      setSenderMoveRules((rules) => [...rules.filter((rule) => rule.id !== savedRule.id && rule.senderAddress !== savedRule.senderAddress), savedRule]);
      if (payload.queued && payload.jobId) {
        addPendingMailboxMoveProjection({ key: moveKey, messageIds: payload.result?.movedMessageIds ?? messageIds, targetFolder: input.targetFolder, moveFolderCounts });
        void monitorMailboxMoveJob(payload.jobId, previousData?.currentFolder ?? currentMailboxFolder, sourceQueryKey, nextData, moveKey);
      } else {
        removePendingMailboxMoveProjection(moveKey);
      }
      // Only wait for the rule and durable job to commit, never for IMAP.
      setSenderMenuState({ messageId: null, scope: interactionScopeKey });
      setDetailSenderMenuAnchor(null);
      if (previousData?.selectedMessage && !nextData?.selectedMessage && currentMailboxQueryKeyRef.current === sourceQueryKey) {
        window.history.replaceState(window.history.state, "", folderHref(currentMailboxFolder, {
          page: nextData?.currentPage ?? 1, pageSize: resolvedPageSize,
          searchField: resolvedSearchField, searchQuery: resolvedSearchQuery,
          sortOrder: currentSortOrder, tab: resolvedMessageFilter
        }));
      }
      setLocalNotice({ message: "자동분류를 저장했습니다.", tone: "success" });
      if (input.moveExistingMessages) setContinueMoveCompletionOpen(true);
      void mailboxDataFetchRef.current({ silent: true, preserveSelection: true });
      return savedRule;
    } catch (error) {
      removePendingMailboxMoveProjection(moveKey);
      if (currentMailboxQueryKeyRef.current === sourceQueryKey && mailboxMutationVersionRef.current === mutationVersion + 1) {
        setMailData(previousData);
      }
      setLocalNotice({ message: error instanceof Error ? error.message : "자동분류를 저장하지 못했습니다.", tone: "error" });
      void mailboxDataFetchRef.current({ silent: true, preserveSelection: true });
      throw error;
    }
  };

  const waitForMailboxMoveJobs = async (jobIds: number[]) => {
    const pendingJobIds = new Set(jobIds);

    for (let attempt = 0; attempt < 300 && pendingJobIds.size > 0; attempt += 1) {
      await new Promise<void>((resolve) => window.setTimeout(resolve, 300));

      for (const jobId of [...pendingJobIds]) {
        const response = await fetch(`/api/mailbox/move-jobs/${jobId}`, {
          cache: "no-store",
          method: "GET"
        });
        if (response.status === 401) {
          window.location.assign("/login");
          throw new Error("unauthorized");
        }
        if (!response.ok) continue;

        const payload = (await response.json()) as MailboxMoveJobResponse;
        if (!payload.ok || !payload.job) continue;
        if (payload.job.status === "failed") throw new Error("undo-job-failed");
        if (payload.job.status === "completed") pendingJobIds.delete(jobId);
      }
    }

    if (pendingJobIds.size > 0) throw new Error("undo-job-timeout");
  };

  const undoMailboxAction = async () => {
    const action = mailboxUndoAction;
    if (!action || mailboxUndoPending) return;

    const sameMailboxView = currentMailboxQueryKeyRef.current === action.sourceQueryKey;
    undoRequestedMoveKeysRef.current.add(action.moveKey);
    mailboxUndoInFlightRef.current = true;
    setMailboxUndoPending(true);
    removePendingMailboxMoveProjection(action.moveKey);

    if (sameMailboxView) {
      setMailData(action.previousData);
      setSelectionState({
        ids: action.previousSelectionIds,
        scope: interactionScopeKey
      });

      if (action.activeMessageId !== null) {
        window.history.replaceState(
          window.history.state,
          "",
          messageHref(action.sourceFolder, action.activeMessageId, action.detailView, {
            page: action.previousData?.currentPage ?? 1,
            pageSize: action.previousData?.pageSize ?? resolvedPageSize,
            searchField: action.previousData?.searchField ?? resolvedSearchField,
            searchQuery: action.previousData?.searchQuery ?? resolvedSearchQuery,
            sortOrder: currentSortOrder,
            tab: action.previousData?.messageFilter ?? resolvedMessageFilter
          })
        );
      }
    }

    try {
      let undoPayload: MailboxMoveUndoResponse | null = null;

      for (let attempt = 0; attempt < 100; attempt += 1) {
        const response = await fetch(`/api/mailbox/move-jobs/${action.jobId}/undo`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ restoreFolder: action.restoreFolder })
        });
        if (response.status === 401) {
          window.location.assign("/login");
          return;
        }

        undoPayload = (await response.json()) as MailboxMoveUndoResponse;
        if (!response.ok || !undoPayload.ok) {
          throw new Error(undoPayload.message ?? undoPayload.error ?? "undo-failed");
        }
        if (!undoPayload.retry) break;
        await new Promise<void>((resolve) => window.setTimeout(resolve, 300));
      }

      if (!undoPayload || undoPayload.retry) throw new Error("undo-timeout");
      if (undoPayload.jobIds?.length) {
        await waitForMailboxMoveJobs(undoPayload.jobIds);
      }

      mailboxUndoInFlightRef.current = false;
      setMailboxUndoAction(null);

      if (sameMailboxView) {
        await mailboxDataFetchRef.current({
          folder: action.sourceFolder,
          messageId: action.activeMessageId,
          page: action.previousData?.currentPage ?? 1,
          pageSize: action.previousData?.pageSize ?? resolvedPageSize,
          preserveSelection: true,
          searchField: action.previousData?.searchField ?? resolvedSearchField,
          searchQuery: action.previousData?.searchQuery ?? resolvedSearchQuery,
          silent: true,
          sortOrder: currentSortOrder,
          tab: action.previousData?.messageFilter ?? resolvedMessageFilter
        });
      } else {
        void mailboxFolderStatusFetchRef.current({ force: true });
      }

      setLocalNotice({
        message: "메일 작업을 실행 취소했습니다.",
        tone: "success"
      });
    } catch (error) {
      mailboxUndoInFlightRef.current = false;
      setMailboxUndoAction(null);
      if (sameMailboxView) {
        await mailboxDataFetchRef.current({
          preserveSelection: false,
          silent: true
        });
      } else {
        void mailboxFolderStatusFetchRef.current({ force: true });
      }
      setLocalNotice({
        message: error instanceof Error && error.message ? error.message : "메일 작업을 실행 취소하지 못했습니다.",
        tone: "error"
      });
    } finally {
      setMailboxUndoPending(false);
    }
  };

  const moveMessagesByClient = async (
    messageIdsInput: number[],
    targetFolderSystemName: string,
    options?: {
      continueMove?: boolean;
      expectedExistingTargetFolder?: string;
      formId?: string;
      moveExistingMessages?: boolean;
      newFolderName?: string;
      releaseSpam?: boolean;
      replaceExistingRule?: boolean;
      senderAddress?: string;
      showCompletionDialog?: boolean;
      targetLabel?: string;
    }
  ) => {
    const messageIds = [...new Set(messageIdsInput)];
    const moveKey = `${targetFolderSystemName}:${messageIds.sort((a, b) => a - b).join(",")}`;

    if (pendingMoveKey) {
      return false;
    }

    if (!canDropMessagesToFolder(targetFolderSystemName, messageIds)) {
      return false;
    }

    const previousData = mailData;
    const previousSelectionIds = [...selectedIds];
    const normalizedSenderAddress = options?.senderAddress?.trim().toLowerCase() ?? "";
    const optimisticMessageIds =
      options?.continueMove && options.moveExistingMessages && normalizedSenderAddress && previousData
        ? [
            ...new Set([
              ...messageIds,
              ...previousData.messages
                .filter(
                  (message) =>
                    message.direction === "inbound" &&
                    message.fromAddress.trim().toLowerCase() === normalizedSenderAddress
                )
                .map((message) => message.id)
            ])
          ]
        : messageIds;
    const activeMessageId = previousData?.selectedMessage?.id ?? null;
    const activeMessageMoved = activeMessageId !== null && optimisticMessageIds.includes(activeMessageId);
    const selectedOriginalMessages = previousData?.messages.filter((message) => messageIds.includes(message.id)) ?? [];
    const containsPermanentDeletion =
      targetFolderSystemName === "trash" &&
      selectedOriginalMessages.some((message) => message.folderSystemName === "trash");
    const restoreFolder =
      currentMailboxFolder === "all"
        ? (selectedOriginalMessages[0]?.folderSystemName ?? "inbox")
        : currentMailboxFolder;
    const nextData = buildOptimisticMovedMailData(previousData, optimisticMessageIds, targetFolderSystemName);

    addPendingMailboxMoveProjection({
      key: moveKey,
      messageIds: optimisticMessageIds,
      targetFolder: targetFolderSystemName
    });

    if (nextData !== previousData) {
      setMailData(nextData);
    }

    setSelectionState({ ids: [], scope: interactionScopeKey });
    setMoveMenuState({ open: false, scope: interactionScopeKey });
    setSenderMenuState({ messageId: null, scope: interactionScopeKey });
    setPendingMoveKey(moveKey);
    setLocalNotice(null);
    setMailboxUndoAction(null);

    if (activeMessageMoved) {
      const nextFolderHref = folderHref(currentMailboxFolder, {
        page: resolvedCurrentPage,
        pageSize: resolvedPageSize,
        searchField: resolvedSearchField,
        searchQuery: resolvedSearchQuery,
        sortOrder: currentSortOrder,
        tab: resolvedMessageFilter
      });

      // A Next navigation remounts this client tree and discards the pending
      // move projection before the worker finishes. Only update the address bar
      // so stale folder counts cannot flash back into the optimistic mailbox.
      window.history.replaceState(window.history.state, "", nextFolderHref);
    }

    try {
      const response = await fetch("/api/mailbox/move", {
        method: "POST",
        keepalive: true,
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          continueMove: Boolean(options?.continueMove),
          expectedExistingTargetFolder: options?.expectedExistingTargetFolder || undefined,
          messageIds,
          moveExistingMessages: Boolean(options?.moveExistingMessages),
          newFolderName: options?.newFolderName?.trim() || undefined,
          releaseSpam: Boolean(options?.releaseSpam),
          replaceExistingRule: Boolean(options?.replaceExistingRule),
          sourceFolder: currentMailboxFolder,
          targetFolder: targetFolderSystemName
        })
      });

      if (response.status === 401) {
        removePendingMailboxMoveProjection(moveKey);
        window.location.assign("/login");
        return false;
      }

      const payload = (await response.json()) as MailboxMoveResponse;

      if (!response.ok || !payload.ok) {
        if (payload.error === "continue-move-rule-exists" && options?.continueMove && normalizedSenderAddress) {
          if (options.showCompletionDialog) {
            setContinueMoveCompletionOpen(false);
          }
          setContinueMovePrompt(null);
          removePendingMailboxMoveProjection(moveKey);
          setMailData(previousData);
          setSelectionState({
            ids: previousSelectionIds,
            scope: interactionScopeKey
          });

          if (activeMessageMoved && activeMessageId !== null) {
            router.replace(
              messageHref(currentMailboxFolder, activeMessageId, detailView, {
                page: resolvedCurrentPage,
                pageSize: resolvedPageSize,
                searchField: resolvedSearchField,
                searchQuery: resolvedSearchQuery,
                sortOrder: currentSortOrder,
                tab: resolvedMessageFilter
              }),
              { scroll: false }
            );
          }

          const existingTargetFolder = payload.existingTargetFolder ?? "";
          setLocalNotice(null);
          setContinueMoveReplacementPrompt({
            existingTargetLabel:
              folders.find((folder) => folder.systemName === existingTargetFolder)?.name ??
              (existingTargetFolder || tr("기존 메일함")),
            existingTargetFolder,
            formId: options.formId ?? "continue-move-replacement",
            includeExisting: Boolean(options.moveExistingMessages),
            messageIds,
            newFolderName: options.newFolderName?.trim() ?? "",
            senderAddress: normalizedSenderAddress,
            targetFolder: targetFolderSystemName,
            targetLabel: options.targetLabel ?? tr("선택한 메일함")
          });
          return false;
        }

        throw new Error(payload.error ?? "move-failed");
      }

      if (payload.queued && payload.jobId) {
        if (payload.result?.movedMessageIds.length) {
          addPendingMailboxMoveProjection({ key: moveKey, messageIds: payload.result.movedMessageIds, targetFolder: targetFolderSystemName });
        }
        if (options?.continueMove) {
          void fetch("/api/mailbox/move-rules", { cache: "no-store" })
            .then((response) => response.json() as Promise<MailboxMoveRulesResponse>)
            .then((rulesPayload) => { if (rulesPayload.ok && rulesPayload.settings) setSenderMoveRules(rulesPayload.settings.rules); })
            .catch(() => undefined);
        }
        if (options?.showCompletionDialog) setContinueMoveCompletionOpen(true);
        setLocalNotice(
          payload.notice ?? {
            tone: "success",
            message: "메일 이동 요청을 처리하고 있습니다."
          }
        );
        if (!options?.continueMove && !containsPermanentDeletion) {
          setMailboxUndoAction({
            activeMessageId,
            detailView,
            jobId: payload.jobId,
            moveKey,
            previousData,
            previousSelectionIds,
            restoreFolder,
            sourceFolder: currentMailboxFolder,
            sourceQueryKey: currentMailboxQueryKey
          });
        }
        void monitorMailboxMoveJob(payload.jobId, currentMailboxFolder, currentMailboxQueryKey, nextData, moveKey);
        void mailboxDataFetchRef.current({ silent: true, preserveSelection: false });
        return true;
      }

      if (!options?.showCompletionDialog) {
        setLocalNotice(
          payload.notice ?? {
            tone: "success",
            message: options?.continueMove
              ? "앞으로 같은 발신자에게 오는 메일 이동을 설정했습니다."
              : "메일을 이동했습니다."
          }
        );
      }

      if (options?.continueMove) {
        void fetch("/api/mailbox/move-rules", { cache: "no-store" })
          .then(async (rulesResponse) => {
            if (!rulesResponse.ok) return null;
            return (await rulesResponse.json()) as MailboxMoveRulesResponse;
          })
          .then((rulesPayload) => {
            if (rulesPayload?.ok && rulesPayload.settings) {
              setSenderMoveRules(rulesPayload.settings.rules);
            }
          })
          .catch(() => undefined);
      }

      removePendingMailboxMoveProjection(moveKey);
      const reconciled = await reconcileMailboxPageAfterMutation({
        folder: nextData?.currentFolder ?? currentMailboxFolder,
        messageId: null,
        page: nextData?.currentPage ?? resolvedCurrentPage,
        pageSize: nextData?.pageSize ?? resolvedPageSize,
        searchField: nextData?.searchField ?? resolvedSearchField,
        searchQuery: nextData?.searchQuery ?? resolvedSearchQuery,
        sortOrder: currentSortOrder,
        tab: nextData?.messageFilter ?? resolvedMessageFilter
      });

      if (!reconciled) {
        setLocalNotice({
          tone: "error",
          message: "메일은 처리했지만 목록을 갱신하지 못했습니다. 새로고침해주세요."
        });
      }
      if (options?.showCompletionDialog) {
        setContinueMoveCompletionOpen(true);
      }
      return true;
    } catch (error) {
      if (options?.showCompletionDialog) {
        setContinueMoveCompletionOpen(false);
      }
      removePendingMailboxMoveProjection(moveKey);
      setMailboxUndoAction(null);
      setMailData(previousData);
      setSelectionState({
        ids: previousSelectionIds,
        scope: interactionScopeKey
      });
      if (activeMessageMoved && activeMessageId !== null) {
        router.replace(
          messageHref(currentMailboxFolder, activeMessageId, detailView, {
            page: resolvedCurrentPage,
            pageSize: resolvedPageSize,
            searchField: resolvedSearchField,
            searchQuery: resolvedSearchQuery,
            sortOrder: currentSortOrder,
            tab: resolvedMessageFilter
          }),
          { scroll: false }
        );
      }
      setLocalNotice({
        tone: "error",
        message:
          error instanceof Error && error.message === "move-sent-only"
            ? "보낸 메일만 보낸메일함으로 이동할 수 있습니다."
            : error instanceof Error && error.message === "move-conflict"
              ? "메일함 상태가 최신이 아니어서 이동에 실패했습니다. 메일함을 다시 동기화한 뒤 다시 시도해주세요."
              : "메일 이동 중 오류가 발생했습니다."
      });
      if (error instanceof Error && error.message === "move-conflict") {
        await reconcileMailboxPageAfterMutation({
          folder: currentMailboxFolder,
          messageId: null,
          page: resolvedCurrentPage,
          pageSize: resolvedPageSize,
          searchField: resolvedSearchField,
          searchQuery: resolvedSearchQuery,
          sortOrder: currentSortOrder,
          tab: resolvedMessageFilter
        });
      }
      return false;
    } finally {
      setPendingMoveKey((current) => (current === moveKey ? null : current));
    }
  };

  const beginMessageDrag = (messageId: number) => (event: ReactDragEvent<HTMLDivElement>) => {
    if (shouldPreventMailRowOpen(event.target)) {
      event.preventDefault();
      return;
    }

    const nextDraggedIds = selectedIds.includes(messageId) && selectedIds.length > 0 ? [...selectedIds] : [messageId];

    if (!(selectedIds.includes(messageId) && selectedIds.length === nextDraggedIds.length)) {
      setSelectionState({ ids: nextDraggedIds, scope: interactionScopeKey });
    }

    event.dataTransfer.effectAllowed = "move";
    event.dataTransfer.setData("text/plain", nextDraggedIds.join(","));
    clearMessageDragPreview();

    const row = messageRowRefs.current.get(messageId);

    if (row) {
      const dragPreview = row.cloneNode(true);

      if (dragPreview instanceof HTMLDivElement) {
        const { width, height } = row.getBoundingClientRect();
        dragPreview.style.position = "fixed";
        dragPreview.style.top = "-9999px";
        dragPreview.style.left = "-9999px";
        dragPreview.style.width = `${Math.ceil(width)}px`;
        dragPreview.style.height = `${Math.ceil(height)}px`;
        dragPreview.style.pointerEvents = "none";
        dragPreview.style.margin = "0";
        dragPreview.style.zIndex = "9999";
        dragPreview.style.overflow = "visible";
        dragPreview.style.borderRadius = "0";
        dragPreview.style.opacity = "0.98";
        dragPreview.style.background = "#edf4ff";
        dragPreview.style.boxShadow = "0 16px 34px rgba(25, 38, 72, 0.14)";

        const countBadge = document.createElement("div");
        countBadge.style.position = "absolute";
        countBadge.style.right = "12px";
        countBadge.style.bottom = "-14px";
        countBadge.style.display = "flex";
        countBadge.style.alignItems = "flex-end";
        countBadge.style.gap = "6px";

        const countPill = document.createElement("span");
        countPill.textContent = String(nextDraggedIds.length);
        countPill.style.display = "inline-flex";
        countPill.style.alignItems = "center";
        countPill.style.justifyContent = "center";
        countPill.style.minWidth = "38px";
        countPill.style.height = "28px";
        countPill.style.padding = "0 10px";
        countPill.style.borderRadius = "9px";
        countPill.style.background = "#ffffff";
        countPill.style.color = "#1f2c45";
        countPill.style.font = "600 14px var(--font-pretendard), sans-serif";
        countPill.style.boxShadow = "0 10px 22px rgba(25, 38, 72, 0.16)";

        const pointerBubble = document.createElement("span");
        pointerBubble.textContent = "↘";
        pointerBubble.style.display = "inline-flex";
        pointerBubble.style.alignItems = "center";
        pointerBubble.style.justifyContent = "center";
        pointerBubble.style.width = "18px";
        pointerBubble.style.height = "18px";
        pointerBubble.style.borderRadius = "999px";
        pointerBubble.style.background = "#19b25f";
        pointerBubble.style.color = "#ffffff";
        pointerBubble.style.font = "700 11px system-ui, sans-serif";
        pointerBubble.style.boxShadow = "0 8px 18px rgba(25, 178, 95, 0.28)";

        countBadge.appendChild(countPill);
        countBadge.appendChild(pointerBubble);
        dragPreview.appendChild(countBadge);
        document.body.appendChild(dragPreview);
        messageDragPreviewRef.current = dragPreview;
        event.dataTransfer.setDragImage(dragPreview, 28, Math.max(16, Math.floor(height / 2)));
      }
    }

    setDraggingMessageIds(nextDraggedIds);
    setDragOverMessageFolder(null);
  };

  const beginFolderDrag = (folderId: number) => (event: ReactDragEvent<HTMLDivElement>) => {
    event.stopPropagation();
    event.dataTransfer.effectAllowed = "move";
    event.dataTransfer.setData("text/plain", String(folderId));
    clearFolderDragPreview();

    const folderRow = folderRowRefs.current.get(folderId);

    if (folderRow) {
      const dragPreview = folderRow.cloneNode(true);

      if (dragPreview instanceof HTMLDivElement) {
        const { width, height } = folderRow.getBoundingClientRect();
        dragPreview.style.position = "fixed";
        dragPreview.style.top = "-9999px";
        dragPreview.style.left = "-9999px";
        dragPreview.style.width = `${Math.ceil(width)}px`;
        dragPreview.style.height = `${Math.ceil(height)}px`;
        dragPreview.style.pointerEvents = "none";
        dragPreview.style.margin = "0";
        dragPreview.style.zIndex = "9999";
        dragPreview.style.opacity = "0.98";
        dragPreview.style.transform = "rotate(-1.25deg) scale(1.02)";
        dragPreview.style.borderRadius = "14px";
        dragPreview.style.background = "#ffffff";
        dragPreview.style.boxShadow = "0 22px 48px rgba(25, 38, 72, 0.18)";
        document.body.appendChild(dragPreview);
        folderDragPreviewRef.current = dragPreview;
        event.dataTransfer.setDragImage(dragPreview, 28, Math.max(18, Math.floor(height / 2)));
      }
    }

    folderDragStartOrderRef.current = [...folderOrderRef.current];
    setDraggingFolderId(folderId);
    setDragOverFolderId(folderId);
  };

  const handleFolderDragOver = (targetFolderId: number) => (event: ReactDragEvent<HTMLDivElement>) => {
    if (draggingFolderId === null) {
      return;
    }

    event.preventDefault();
    event.dataTransfer.dropEffect = "move";

    if (draggingFolderId === targetFolderId) {
      setDragOverFolderId(targetFolderId);
      setFolderDropPosition(null);
      return;
    }

    const bounds = event.currentTarget.getBoundingClientRect();
    const relativeY = (event.clientY - bounds.top) / Math.max(1, bounds.height);
    const nextPosition: SidebarFolderDropPosition =
      relativeY < 0.24 ? "before" : relativeY > 0.76 ? "after" : "inside";
    setDragOverFolderId(targetFolderId);
    setFolderDropPosition(nextPosition);
  };

  const finishFolderDrag = () => {
    clearFolderDragPreview();
    setDraggingFolderId(null);
    setDragOverFolderId(null);
    setFolderDropPosition(null);
  };

  const dropSidebarFolder = async (
    sourceFolderId: number,
    targetFolderId: number,
    position: SidebarFolderDropPosition,
  ) => {
    const sourceFolder = personalFolders.find((folder) => folder.id === sourceFolderId);
    const targetFolder = personalFolders.find((folder) => folder.id === targetFolderId);

    if (!sourceFolder || !targetFolder || sourceFolder.id === targetFolder.id) {
      finishFolderDrag();
      return;
    }

    const nextParentSystemName = position === "inside"
      ? targetFolder.systemName
      : targetFolder.parentSystemName;
    const previousOrder = [...folderDragStartOrderRef.current];
    const nextOrder = [...folderOrderRef.current];
    const sourceIndex = nextOrder.indexOf(sourceFolderId);
    const targetIndex = nextOrder.indexOf(targetFolderId);

    if (sourceIndex !== -1 && targetIndex !== -1) {
      nextOrder.splice(sourceIndex, 1);
      const adjustedTargetIndex = nextOrder.indexOf(targetFolderId);
      nextOrder.splice(position === "before" ? adjustedTargetIndex : adjustedTargetIndex + 1, 0, sourceFolderId);
      folderOrderRef.current = nextOrder;
      setFolderOrderState(nextOrder);
    }

    finishFolderDrag();

    try {
      const response = await fetch("/api/mailbox/folder-actions", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          action: "move",
          folder: sourceFolder.systemName,
          parentFolder: nextParentSystemName
        })
      });
      const payload = (await response.json()) as {
        error?: string;
        notice?: MailNotice | null;
        ok?: boolean;
      };

      if (!response.ok || !payload.ok) {
        throw new Error(payload.notice?.message ?? payload.error ?? "folder-move-failed");
      }

      if (!sameNumberArray(previousOrder, nextOrder)) {
        await saveFolderOrder(nextOrder);
      }

      setLocalNotice(payload.notice ?? { tone: "success", message: "메일함 위치를 변경했습니다." });
      await fetchMailboxData({ preserveSelection: false, silent: true });
    } catch (error) {
      folderOrderRef.current = previousOrder;
      setFolderOrderState(previousOrder);
      setLocalNotice({
        tone: "error",
        message: error instanceof Error && error.message ? error.message : "메일함 위치를 변경하지 못했습니다."
      });
    }
  };

  const runSidebarFolderAction = async (item: SidebarFolderNavItem, action: SidebarFolderAction) => {
    if (sidebarFolderActionState.folderId === item.id && sidebarFolderActionState.action === action) {
      return;
    }

    const clearingTrash = action === "clear" && item.systemName === "trash";
    if (
      clearingTrash &&
      !window.confirm("휴지통의 모든 메일을 영구 삭제합니다. 삭제한 메일은 복구할 수 없습니다. 계속하시겠습니까?")
    ) {
      return;
    }

    const previousData = mailData;
    const trashClearMoveKey = clearingTrash ? `clear:trash:${item.id}` : null;
    const optimisticTrashData = clearingTrash
      ? buildOptimisticClearedFolderData(previousData, "trash")
      : previousData;

    if (clearingTrash && trashClearMoveKey) {
      addPendingMailboxMoveProjection({
        key: trashClearMoveKey,
        messageIds:
          previousData?.messages
            .filter((message) => message.folderSystemName === "trash")
            .map((message) => message.id) ?? [],
        targetFolder: "trash"
      });
      setMailData(optimisticTrashData);
      setMailPageFillBuffer(null);
      setSelectionState({ ids: [], scope: interactionScopeKey });
      setSenderMenuState({ messageId: null, scope: interactionScopeKey });
      setMoveMenuState({ open: false, scope: interactionScopeKey });
    }

    setSidebarFolderMenuState({ folderId: null, scope: interactionScopeKey });
    setSidebarFolderActionState({ folderId: item.id, action });

    try {
      const response = await fetch("/api/mailbox/folder-actions", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          action,
          folder: item.systemName,
          label: item.label
        })
      });

      if (response.status === 401) {
        window.location.assign("/login");
        return;
      }

      const payload = (await response.json()) as {
        error?: string;
        jobId?: number | null;
        notice?: MailNotice | null;
        ok?: boolean;
        queued?: boolean;
      };

      if (!response.ok || !payload.ok) {
        throw new Error(payload.notice?.message ?? payload.error ?? "folder-action-failed");
      }

      setLocalNotice(
        payload.notice ?? {
          tone: "success",
          message: "폴더 작업을 완료했습니다."
        }
      );
      setSelectionState({ ids: [], scope: interactionScopeKey });
      setSenderMenuState({ messageId: null, scope: interactionScopeKey });
      setMoveMenuState({ open: false, scope: interactionScopeKey });

      if (clearingTrash && trashClearMoveKey) {
        if (payload.queued && payload.jobId) {
          const sourceQueryKey =
            currentMailboxFolder === "trash" ? currentMailboxQueryKey : `${trashClearMoveKey}:background`;
          void monitorMailboxMoveJob(
            payload.jobId,
            "trash",
            sourceQueryKey,
            optimisticTrashData,
            trashClearMoveKey
          );
        } else {
          removePendingMailboxMoveProjection(trashClearMoveKey);
        }
        return;
      }

      if (action === "delete" && currentMailboxFolder === item.systemName) {
        router.push(
          folderHref("inbox", {
            page: 1,
            pageSize: resolvedPageSize,
            searchField: resolvedSearchField,
            searchQuery: resolvedSearchQuery,
            sortOrder: currentSortOrder,
            tab: "all"
          }),
          { scroll: false }
        );
        return;
      }

      await fetchMailboxData({ preserveSelection: false, silent: true });
    } catch (error) {
      if (clearingTrash && trashClearMoveKey) {
        removePendingMailboxMoveProjection(trashClearMoveKey);
        setMailData(previousData);
      }
      setLocalNotice({
        tone: "error",
        message: error instanceof Error && error.message ? error.message : "폴더 작업 중 오류가 발생했습니다."
      });
    } finally {
      setSidebarFolderActionState({ folderId: null, action: null });
    }
  };

  const renderSidebarFolderItem = (item: SidebarFolderNavItem, options?: { draggable?: boolean }) => {
    const active = showMailboxSidebarActiveState && currentMailboxFolder === item.systemName;
    const dragging = draggingFolderId === item.id;
    const draggable = Boolean(options?.draggable);
    const messageDropAllowed = draggingFolderId === null && canDropMessagesToFolder(item.systemName);
    const messageDropActive = messageDropAllowed && dragOverMessageFolder === item.systemName;
    const menuOpen = sidebarFolderMenuId === item.id;
    const rowTone = active
      ? "bg-[#eaf2ff] text-[#2f6df6] shadow-[inset_0_0_0_1px_rgba(47,109,246,0.04)]"
      : dragging
        ? "bg-white text-[#31415d]"
        : "text-[#33445f] hover:bg-[#f4f6fa] hover:text-[#2f6df6]";
    const itemNavigationHref = mailConfigured ? item.href : setupHref("domain");
    const itemNavigationIntent = mailConfigured ? "mailbox" : "setup";

    return (
      <div
        key={item.id}
        draggable={draggable}
        onDragStart={draggable ? beginFolderDrag(item.id) : undefined}
        onDragEnd={draggable ? finishFolderDrag : undefined}
        ref={(node) => {
          if (node) {
            folderRowRefs.current.set(item.id, node);
          } else {
            folderRowRefs.current.delete(item.id);
          }
        }}
        onDragOver={(event) => {
          if (draggingFolderId !== null && draggable) {
            handleFolderDragOver(item.id)(event);
            return;
          }

          if (!messageDropAllowed) {
            return;
          }

          event.preventDefault();
          event.dataTransfer.dropEffect = "move";

          if (dragOverMessageFolder !== item.systemName) {
            setDragOverMessageFolder(item.systemName);
          }
        }}
        onDragLeave={(event) => {
          if (!messageDropAllowed || dragOverMessageFolder !== item.systemName) {
            return;
          }

          const currentTarget = event.currentTarget;
          const { clientX, clientY } = event;
          const nextTarget = event.relatedTarget;

          if (nextTarget instanceof Node && event.currentTarget.contains(nextTarget)) {
            return;
          }

          window.requestAnimationFrame(() => {
            const hoveredElement = document.elementFromPoint(clientX, clientY);

            if (hoveredElement instanceof Node && currentTarget.contains(hoveredElement)) {
              return;
            }

            setDragOverMessageFolder((current) => (current === item.systemName ? null : current));
          });
        }}
        onDrop={(event) => {
          if (draggingFolderId !== null && draggable) {
            event.preventDefault();
            event.stopPropagation();
            const bounds = event.currentTarget.getBoundingClientRect();
            const relativeY = (event.clientY - bounds.top) / Math.max(1, bounds.height);
            const position: SidebarFolderDropPosition =
              relativeY < 0.24 ? "before" : relativeY > 0.76 ? "after" : "inside";
            void dropSidebarFolder(draggingFolderId, item.id, position);
            return;
          }

          if (!messageDropAllowed || draggingMessageIds === null) {
            return;
          }

          event.preventDefault();
          event.stopPropagation();
          const draggedIds = [...draggingMessageIds];
          finishMessageDrag();
          void moveMessagesByClient(draggedIds, item.systemName);
        }}
        className={`group relative mb-0 flex items-center gap-1 rounded-[10px] transition-all duration-150 will-change-transform ${rowTone} ${
          draggable ? "cursor-grab active:cursor-grabbing" : ""
        } ${
          draggable && dragOverFolderId === item.id && draggingFolderId !== item.id && folderDropPosition === "inside"
            ? "bg-[#edf4ff] shadow-[inset_0_0_0_2px_rgba(47,109,246,0.58)]"
            : ""
        } ${
          messageDropActive
            ? "bg-[#f7fbff] shadow-[inset_0_0_0_2px_rgba(47,109,246,0.72),0_10px_24px_rgba(47,109,246,0.12)]"
            : ""
        } ${dragging ? "scale-[1.01] bg-white opacity-80 shadow-[0_18px_38px_rgba(25,38,72,0.12)]" : ""}`}
        style={draggable && item.depth > 0 ? { marginLeft: `${Math.min(item.depth, 6) * 16}px` } : undefined}
      >
        {draggable && dragOverFolderId === item.id && draggingFolderId !== item.id && (folderDropPosition === "before" || folderDropPosition === "after") ? (
          <span
            aria-hidden="true"
            className={`pointer-events-none absolute inset-x-0 h-[2px] bg-[rgba(47,109,246,0.8)] ${folderDropPosition === "before" ? "top-0" : "bottom-0"}`}
          />
        ) : null}
        {draggable ? (
          <span
            aria-hidden="true"
            className="inline-flex h-9 w-7 shrink-0 cursor-grab items-center justify-center text-[#9ca9bc] transition-colors group-hover:text-[#61738f] group-active:cursor-grabbing"
          >
            <GripIcon className="h-[15px] w-[15px]" />
          </span>
        ) : null}

        <div
          className={`relative flex min-w-0 flex-1 items-center rounded-[10px] px-2 py-2 text-[14px] transition-all duration-200 ${
            draggable ? "cursor-grab active:cursor-grabbing" : "cursor-pointer"
          }`}
          data-sidebar-folder-menu-root="true"
          onClick={(event) => {
            if (shouldBypassClientNavigation(event)) {
              return;
            }

            navigateWithinMailbox(itemNavigationHref, {
              intent: itemNavigationIntent
            });
          }}
        >
          <Link
            href={itemNavigationHref}
            draggable={false}
            prefetch={false}
            scroll={false}
            onClick={(event) =>
              handleMailboxNavigationLinkClick(event, itemNavigationHref, {
                intent: itemNavigationIntent
              })
            }
            className={`flex min-w-0 flex-1 items-center ${SIDEBAR_MENU_TEXT_GAP_CLASS} ${
              draggable ? "cursor-grab active:cursor-grabbing" : ""
            }`}
          >
            {iconForNav(item.icon, SIDEBAR_MENU_ICON_CLASS)}
            <span className="flex min-w-0 items-center gap-1.5">
              <span className={`truncate ${active ? "font-semibold" : "font-medium"}`}>{item.label}</span>
              <span className={`shrink-0 text-[12px] ${active ? "font-semibold text-[#2f6df6]" : "text-[#70829f]"}`}>
                {compactCount(item.count)}
              </span>
            </span>
          </Link>

          <div className="ml-1 flex shrink-0 items-center">
            <button
              type="button"
              aria-expanded={menuOpen}
              aria-haspopup="menu"
              onClick={(event) => {
                event.stopPropagation();
                setSidebarFolderMenuState((current) =>
                  current.scope === interactionScopeKey && current.folderId === item.id
                    ? { folderId: null, scope: interactionScopeKey }
                    : { folderId: item.id, scope: interactionScopeKey }
                );
              }}
              className={`inline-flex h-5 w-5 cursor-pointer items-center justify-center rounded-[7px] text-[#8a96ab] transition-all duration-150 ${
                menuOpen
                  ? "bg-white text-[#556885]"
                  : "pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100 hover:bg-white hover:text-[#2f6df6]"
              }`}
            >
              <MoreIcon className="h-[14px] w-[14px]" />
            </button>
          </div>
        </div>
      </div>
    );
  };

  const renderSidebarFolderMenuPortal = () => {
    if (!sidebarFolderMenuItem || !sidebarFolderMenuPosition || typeof document === "undefined") {
      return null;
    }

    const actionPending = sidebarFolderActionState.folderId === sidebarFolderMenuItem.id;
    const sidebarFolderMenuItemClass = (disabled = false) => popoverMenuItemClass({ disabled });

    return createPortal(
      <div
        data-sidebar-folder-menu-root="true"
        className={`absolute z-[120] w-max min-w-[188px] -translate-y-1/2 ${popoverMenuSurfaceClass()}`}
        style={{
          top: `${sidebarFolderMenuPosition.top}px`,
          left: `${sidebarFolderMenuPosition.left}px`
        }}
      >
        <button
          type="button"
          onClick={() => void runSidebarFolderAction(sidebarFolderMenuItem, "mark-read")}
          disabled={actionPending}
          className={sidebarFolderMenuItemClass(actionPending)}
        >
          모두 읽음 표시
        </button>
        <button
          type="button"
          onClick={() => void runSidebarFolderAction(sidebarFolderMenuItem, "clear")}
          disabled={actionPending}
          className={sidebarFolderMenuItemClass(actionPending)}
        >
          비우기
        </button>
        <button
          type="button"
          onClick={() => void runSidebarFolderAction(sidebarFolderMenuItem, "backup")}
          disabled={actionPending}
          className={sidebarFolderMenuItemClass(actionPending)}
        >
          백업하기
        </button>
        {sidebarFolderMenuItem.kind === "custom" ? (
          <>
            <div className="my-2 border-t border-[#edf2fb]" />
            <button
              type="button"
              onClick={() => openSidebarFolderRenameEditor(sidebarFolderMenuItem)}
              disabled={actionPending || sidebarFolderEditorPending}
              className={sidebarFolderMenuItemClass(actionPending || sidebarFolderEditorPending)}
            >
              이름 바꾸기
            </button>
            <button
              type="button"
              onClick={() =>
                openSidebarFolderCreateEditor({
                  insertAfterSystemName: sidebarFolderMenuItem.systemName,
                  parentSystemName: sidebarFolderMenuItem.systemName
                })
              }
              disabled={actionPending || sidebarFolderEditorPending}
              className={sidebarFolderMenuItemClass(actionPending || sidebarFolderEditorPending)}
            >
              하위 메일함 추가
            </button>
            <button
              type="button"
              onClick={() => void runSidebarFolderAction(sidebarFolderMenuItem, "delete")}
              disabled={actionPending}
              className={sidebarFolderMenuItemClass(actionPending)}
            >
              메일함 삭제
            </button>
          </>
        ) : null}
      </div>,
      document.body
    );
  };

  const openSidebarFolderCreateEditor = (options?: {
    insertAfterSystemName?: string | null;
    insertAtTop?: boolean;
    parentSystemName?: string | null;
  }) => {
    setSidebarFolderMenuState({ folderId: null, scope: interactionScopeKey });
    setPersonalFoldersOpen(true);
    setSidebarFolderEditorState({
      errorMessage: undefined,
      insertAfterSystemName: options?.insertAfterSystemName ?? null,
      insertAtTop: options?.insertAtTop ?? false,
      mode: "create",
      parentSystemName: options?.parentSystemName ?? null,
      value: "새 메일함"
    });
  };

  const openSidebarFolderRenameEditor = (item: SidebarFolderNavItem) => {
    setSidebarFolderMenuState({ folderId: null, scope: interactionScopeKey });
    setPersonalFoldersOpen(true);
    setSidebarFolderEditorState({
      errorMessage: undefined,
      folderId: item.id,
      mode: "rename",
      systemName: item.systemName,
      value: item.label
    });
  };

  const closeSidebarFolderEditor = () => {
    if (sidebarFolderEditorPending) {
      return;
    }

    setSidebarFolderEditorState(null);
  };

  const submitSidebarFolderEditor = async (editorStateSnapshot: SidebarFolderInlineEditorState | null) => {
    if (!editorStateSnapshot || sidebarFolderEditorPending) {
      return;
    }

    const nextName = editorStateSnapshot.value.trim();

    if (!nextName) {
      setSidebarFolderEditorState((current) =>
        current
          ? {
              ...current,
              errorMessage: "메일함 이름을 입력해주세요."
            }
          : current
      );
      window.requestAnimationFrame(() => {
        sidebarFolderEditorInputRef.current?.focus();
        sidebarFolderEditorInputRef.current?.select();
      });
      return;
    }

    setSidebarFolderEditorPending(true);

    try {
      const response = await fetch("/api/mailbox/folder-actions", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(
          editorStateSnapshot.mode === "create"
            ? {
                action: "create",
                insertAfterFolder: editorStateSnapshot.insertAfterSystemName,
                insertAtTop: editorStateSnapshot.insertAtTop,
                name: nextName,
                parentFolder: editorStateSnapshot.parentSystemName
              }
            : {
                action: "rename",
                folder: editorStateSnapshot.systemName,
                label: nextName,
                name: nextName
              }
        )
      });

      if (response.status === 401) {
        window.location.assign("/login");
        return;
      }

      const payload = (await response.json()) as {
        error?: string;
        notice?: MailNotice | null;
        ok?: boolean;
      };

      if (!response.ok || !payload.ok) {
        const failureMessage =
          payload.notice?.message ??
          (payload.error === "folder-name-exists"
            ? "같은 이름의 메일함이 이미 있습니다. 다른 이름을 입력해주세요."
            : payload.error === "folder-name-required"
              ? "메일함 이름을 입력해주세요."
              : editorStateSnapshot.mode === "create"
                ? "메일함 생성 중 오류가 발생했습니다."
                : "메일함 이름 변경 중 오류가 발생했습니다.");

        setSidebarFolderEditorState((current) =>
          current
            ? {
                ...current,
                errorMessage: failureMessage
              }
            : current
        );
        setLocalNotice(payload.notice ?? { tone: "error", message: failureMessage });
        window.requestAnimationFrame(() => {
          sidebarFolderEditorInputRef.current?.focus();
          sidebarFolderEditorInputRef.current?.select();
        });
        return;
      }

      setLocalNotice(
        payload.notice ?? {
          message: editorStateSnapshot.mode === "create" ? "메일함을 만들었습니다." : "메일함 이름을 변경했습니다.",
          tone: "success"
        }
      );
      setSidebarFolderEditorState(null);
      await fetchMailboxData({ preserveSelection: false, silent: true });
    } catch {
      setSidebarFolderEditorState((current) =>
        current
          ? {
              ...current,
              errorMessage:
                editorStateSnapshot.mode === "create"
                  ? "메일함 생성 중 오류가 발생했습니다."
                  : "메일함 이름 변경 중 오류가 발생했습니다."
            }
          : current
      );
      setLocalNotice({
        tone: "error",
        message:
          editorStateSnapshot.mode === "create"
            ? "메일함 생성 중 오류가 발생했습니다."
            : "메일함 이름 변경 중 오류가 발생했습니다."
      });
      window.requestAnimationFrame(() => {
        sidebarFolderEditorInputRef.current?.focus();
        sidebarFolderEditorInputRef.current?.select();
      });
    } finally {
      setSidebarFolderEditorPending(false);
    }
  };

  const handleSidebarFolderEditorSubmit = (event: ReactFormEvent<HTMLFormElement>) => {
    event.preventDefault();

    if (sidebarFolderEditorState) {
      void submitSidebarFolderEditor(sidebarFolderEditorState);
    }
  };

  const renderSidebarFolderInlineEditor = () => {
    if (!sidebarFolderEditorState) {
      return null;
    }

    return (
      <div
        className="group mb-0 flex items-center gap-1 rounded-[10px]"
        style={
          sidebarFolderEditorState.mode === "create" && sidebarFolderEditorState.parentSystemName
            ? {
                marginLeft: `${Math.min(
                  (sidebarFolders.find(
                    (folder) => folder.systemName === sidebarFolderEditorState.parentSystemName,
                  )?.depth ?? 0) + 1,
                  6,
                ) * 16}px`,
              }
            : undefined
        }
      >
        <div className="h-9 w-7 shrink-0" />
        <form
          onSubmit={handleSidebarFolderEditorSubmit}
          className="relative flex min-w-0 flex-1 flex-col rounded-[10px] bg-[#eaf2ff] px-2 py-2 text-[14px] text-[#2f6df6] shadow-[inset_0_0_0_1px_rgba(47,109,246,0.14)]"
        >
          <div className="flex min-w-0 items-center gap-[5px]">
            <FolderIcon className={SIDEBAR_MENU_ICON_CLASS} />
            <input
              ref={sidebarFolderEditorInputRef}
              type="text"
              value={sidebarFolderEditorState.value}
              disabled={sidebarFolderEditorPending}
              onChange={(event) =>
                setSidebarFolderEditorState((current) =>
                  current
                    ? {
                        ...current,
                        errorMessage: undefined,
                        value: event.target.value
                      }
                    : current
                )
              }
              onBlur={(event) => event.currentTarget.form?.requestSubmit()}
              onKeyDown={(event) => {
                if (event.key === "Escape") {
                  event.preventDefault();
                  closeSidebarFolderEditor();
                }
              }}
              className={`h-[28px] min-w-0 flex-1 rounded-[6px] bg-white px-2 text-[14px] font-medium outline-none placeholder:text-[#83a1e4] ${
                sidebarFolderEditorState.errorMessage
                  ? "border border-[#f08b8b] text-[#b42318]"
                  : "border border-[#5f93ff] text-[#2f6df6]"
              }`}
            />
          </div>
          {sidebarFolderEditorState.errorMessage ? (
            <p className="mt-1.5 pl-6 text-[12px] leading-5 text-[#c23333]">{sidebarFolderEditorState.errorMessage}</p>
          ) : null}
        </form>
      </div>
    );
  };

  useEffect(() => {
    if (!sidebarFolderEditorState) {
      return;
    }

    const animationFrameId = window.requestAnimationFrame(() => {
      sidebarFolderEditorInputRef.current?.focus();
      sidebarFolderEditorInputRef.current?.select();
    });

    return () => {
      window.cancelAnimationFrame(animationFrameId);
    };
  }, [sidebarFolderEditorState]);

  useEffect(
    () => () => {
      clearFolderDragPreview();
      clearMessageDragPreview();
    },
    []
  );

  const beginResize = (panel: LayoutResizePanel) => (event: ReactPointerEvent<HTMLButtonElement>) => {
    const layoutRoot =
      panel === "list-height"
        ? event.currentTarget.closest("[data-mail-vertical-split-root='true']")
        : event.currentTarget.closest("[data-mail-layout-root='true']");

    if (!(layoutRoot instanceof HTMLDivElement)) {
      return;
    }

    const rect = layoutRoot.getBoundingClientRect();

    event.preventDefault();
    setDragState({
      containerHeight: rect.height,
      containerWidth: rect.width,
      isXlLayout: window.matchMedia("(min-width: 1280px)").matches && !isStandaloneContentPanel,
      panel,
      startListHeight: layoutState.listHeight,
      startListWidth: layoutState.listWidth,
      startSidebarWidth: layoutState.sidebarWidth,
      startX: event.clientX,
      startY: event.clientY
    });
  };

  const toggleSelection = (messageId: number) => {
    setMoveMenuState({ open: false, scope: interactionScopeKey });
    setSelectionState((prev) => {
      const baseIds = prev.scope === interactionScopeKey ? prev.ids : [];
      return {
        ids: baseIds.includes(messageId) ? baseIds.filter((id) => id !== messageId) : [...baseIds, messageId],
        scope: interactionScopeKey
      };
    });
  };

  const toggleSelectAll = () => {
    setMoveMenuState({ open: false, scope: interactionScopeKey });

    if (allVisibleSelected) {
      setSelectionState({ ids: [], scope: interactionScopeKey });
      return;
    }

    setSelectionState({
      ids: filteredMessages.map((message) => message.id),
      scope: interactionScopeKey
    });
  };

  const activeTranslationState =
    previewMessage &&
    messageTranslationState.messageId === previewMessage.id &&
    messageTranslationState.targetLocale === locale
      ? messageTranslationState
      : null;
  const translatedMessage = activeTranslationState?.data?.status === "translated" ? activeTranslationState.data : null;
  const translationScope = previewMessage ? `${previewMessage.id}:${locale}` : "";
  const showOriginalMessage = originalMessageScope === translationScope;
  const showingTranslatedMessage = Boolean(translatedMessage && !showOriginalMessage);
  const displayedBodyText = showingTranslatedMessage
    ? (translatedMessage?.bodyText ?? "")
    : (previewMessage?.bodyText ?? "");
  const previewLines = displayedBodyText
    .split(/\r?\n/)
    .map((line) => line.trimEnd())
    .filter((line, index, array) => !(line === "" && array[index - 1] === ""))
    .slice(0, 14);
  const previewHtml = showingTranslatedMessage
    ? (translatedMessage?.bodyHtml?.trim() ?? "")
    : (previewMessage?.bodyHtmlDisplay?.trim() ?? "");
  const detailMenuOpen =
    detailMenuState.open && detailMenuState.scope === detailPanelScopeKey && detailPanelScopeKey !== "";
  const rawSourceModalOpen =
    rawSourceModalState.open && rawSourceModalState.scope === detailPanelScopeKey && detailPanelScopeKey !== "";
  const showMailboxSidebarActiveState = isMailboxPanel && currentView === "list";

  useEffect(() => {
    if (!detailMenuOpen) {
      return;
    }

    const handlePointerDown = (event: MouseEvent) => {
      const target = event.target;

      if (!(target instanceof HTMLElement)) {
        return;
      }

      if (!target.closest("[data-detail-menu-root='true']")) {
        setDetailMenuState({ open: false, scope: detailPanelScopeKey });
      }
    };

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setDetailMenuState({ open: false, scope: detailPanelScopeKey });
      }
    };

    document.addEventListener("mousedown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);

    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [detailMenuOpen, detailPanelScopeKey]);

  useEffect(() => {
    if (!attachmentPreviewModalOpen || !attachmentPreviewState) {
      return;
    }

    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setAttachmentPreviewState(null);
        return;
      }

      if (event.key === "ArrowLeft") {
        setAttachmentPreviewState((current) => {
          if (!current) return current;
          const currentIndex = Math.min(current.currentIndex, attachmentPreviewItems.length - 1);
          return currentIndex > 0 ? { ...current, currentIndex: currentIndex - 1 } : current;
        });
        return;
      }

      if (event.key === "ArrowRight") {
        setAttachmentPreviewState((current) => {
          if (!current) return current;
          const currentIndex = Math.min(current.currentIndex, attachmentPreviewItems.length - 1);
          return currentIndex < attachmentPreviewItems.length - 1
            ? { ...current, currentIndex: currentIndex + 1 }
            : current;
        });
      }
    };

    document.addEventListener("keydown", handleKeyDown);

    return () => {
      document.body.style.overflow = previousOverflow;
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [attachmentPreviewItems.length, attachmentPreviewModalOpen, attachmentPreviewState]);

  const openMessage = (message: MailMessageSummary) => {
    const messageId = message.id;

    if (message.direction === "draft" || currentMailboxFolder === "drafts") {
      setPendingMessageNavigationId(null);
      startMessageNavigationTransition(() => {
        router.push(`/mail?folder=drafts&view=compose&composeMode=draft&message=${messageId}`, { scroll: false });
      });
      return;
    }

    if (detailOpen && activeMessageId === messageId) {
      return;
    }

    setDismissedDetailScopeKey(null);
    setToolbarMenuKind(null);
    setMoveMenuState({ open: false, scope: interactionScopeKey });
    setSelectionState({
      ids: layoutStateRef.current.viewMode === "list-only" ? [] : [messageId],
      scope: interactionScopeKey
    });
    setPendingMessageNavigationId(messageId);
    startMessageNavigationTransition(() => {
      router.push(
        messageHref(currentMailboxFolder, messageId, detailView, {
          page: resolvedCurrentPage,
          pageSize: resolvedPageSize,
          searchField: resolvedSearchField,
          searchQuery: resolvedSearchQuery,
          sortOrder: layoutStateRef.current.sortOrder,
          tab: resolvedMessageFilter
        }),
        { scroll: false }
      );
    });
  };

  const openMessagePopup = (messageId: number) => {
    if (typeof window === "undefined") {
      return;
    }

    const popupUrl = messageBodyHref(messageId);

    const popupWidth = Math.min(1280, Math.max(960, Math.floor(window.innerWidth * 0.82)));
    const popupHeight = Math.min(920, Math.max(720, Math.floor(window.innerHeight * 0.88)));
    const popupLeft = Math.max(0, Math.floor(window.screenX + (window.outerWidth - popupWidth) / 2));
    const popupTop = Math.max(0, Math.floor(window.screenY + (window.outerHeight - popupHeight) / 2));
    const popupFeatures = [
      `width=${popupWidth}`,
      `height=${popupHeight}`,
      `left=${popupLeft}`,
      `top=${popupTop}`,
      "popup=yes",
      "resizable=yes",
      "scrollbars=yes"
    ].join(",");
    const popup = window.open(popupUrl, `official-mail-message-${messageId}`, popupFeatures);

    if (popup) {
      popup.focus();
    } else {
      window.location.assign(popupUrl);
    }
  };

  const openAttachmentPreview = (messageId: number, attachment: MailMessageAttachment) => {
    if (!detailPanelScopeKey) {
      return;
    }

    const collection = isImageAttachmentPreview(attachment) ? "images" : "previewable";
    const items = collection === "images" ? imagePreviewableAttachments : previewableAttachments;
    const currentIndex = items.findIndex(
      (item) =>
        item.index === attachment.index &&
        item.filename === attachment.filename &&
        item.contentType === attachment.contentType
    );

    if (currentIndex < 0) {
      return;
    }

    setAttachmentPreviewState({
      collection,
      currentIndex,
      messageId,
      scope: detailPanelScopeKey
    });
  };

  const downloadAttachment = (messageId: number, attachment: MailMessageAttachment) => {
    if (typeof window === "undefined" || attachment.isPendingPublication) {
      return;
    }

    const downloadUrl = attachment.isLargeAttachment && attachment.externalUrl
      ? attachment.externalUrl
      : `/api/mailbox/attachment?message=${messageId}&index=${attachment.index}&mode=download`;
    const anchor = document.createElement("a");
    anchor.href = downloadUrl;
    anchor.rel = "noopener";
    if (attachment.isLargeAttachment) {
      // A 410 navigation opens the expiry alert without replacing the mailbox.
      anchor.target = "_blank";
    }
    anchor.style.display = "none";
    document.body.appendChild(anchor);
    anchor.click();
    window.setTimeout(() => anchor.remove(), 0);
  };

  const downloadAllAttachments = (messageId: number, attachments: MailMessageAttachment[]) => {
    if (typeof window === "undefined") {
      return;
    }

    attachments.filter((attachment) => !attachment.isPendingPublication).forEach((attachment, index) => {
      window.setTimeout(() => {
        downloadAttachment(messageId, attachment);
      }, index * 140);
    });
  };

  useEffect(() => {
    if (!dragState) {
      return;
    }

    const handlePointerMove = (event: PointerEvent) => {
      if (dragState.panel === "list-height") {
        const delta = event.clientY - dragState.startY;

        setLayoutState((prev) => {
          const listHeight = clampListHeight({
            containerHeight: dragState.containerHeight,
            value: dragState.startListHeight + delta
          });

          if (prev.listHeight === listHeight) {
            return prev;
          }

          return { ...prev, listHeight };
        });
        return;
      }

      const delta = event.clientX - dragState.startX;

      setLayoutState((prev) => {
        if (dragState.panel === "sidebar") {
          const sidebarWidth = clampSidebarWidth({
            containerWidth: dragState.containerWidth,
            isSetupPanel: isStandaloneContentPanel,
            isXlLayout: dragState.isXlLayout,
            value: dragState.startSidebarWidth + delta
          });
          const listWidth =
            !isStandaloneContentPanel && dragState.isXlLayout
              ? clampListWidth({
                  containerWidth: dragState.containerWidth,
                  sidebarWidth,
                  value: dragState.startListWidth
                })
              : prev.listWidth;

          if (prev.sidebarWidth === sidebarWidth && prev.listWidth === listWidth) {
            return prev;
          }

          return { ...prev, sidebarWidth, listWidth };
        }

        const listWidth = clampListWidth({
          containerWidth: dragState.containerWidth,
          sidebarWidth: prev.sidebarWidth,
          value: dragState.startListWidth + delta
        });

        if (prev.listWidth === listWidth) {
          return prev;
        }

        return { ...prev, listWidth };
      });
    };

    const finishDrag = () => {
      const nextLayout = normalizeMailLayoutPreferences(layoutStateRef.current);
      const savedLayout = savedLayoutRef.current;

      if (
        nextLayout.sidebarWidth !== savedLayout.sidebarWidth ||
        nextLayout.listWidth !== savedLayout.listWidth ||
        nextLayout.listHeight !== savedLayout.listHeight
      ) {
        void saveLayoutPreferences(nextLayout);
      }

      setDragState(null);
    };

    const previousCursor = document.body.style.cursor;
    const previousUserSelect = document.body.style.userSelect;

    // eslint-disable-next-line react-hooks/immutability
    document.body.style.cursor = dragState.panel === "list-height" ? "row-resize" : "col-resize";
    // eslint-disable-next-line react-hooks/immutability
    document.body.style.userSelect = "none";
    window.addEventListener("pointermove", handlePointerMove);
    window.addEventListener("pointerup", finishDrag);
    window.addEventListener("pointercancel", finishDrag);

    return () => {
      document.body.style.cursor = previousCursor;
      document.body.style.userSelect = previousUserSelect;
      window.removeEventListener("pointermove", handlePointerMove);
      window.removeEventListener("pointerup", finishDrag);
      window.removeEventListener("pointercancel", finishDrag);
    };
  }, [dragState, isStandaloneContentPanel]);

  const renderMoveMenuPopover = (messageIdsValue: string) => {
    const inputId = `move-folder-name-${messageIdsValue.replace(/[^a-z0-9]+/gi, "-") || "default"}`;
    const formId = `move-mail-form-${messageIdsValue.replace(/[^a-z0-9]+/gi, "-") || "default"}`;
    const messageIds = messageIdsValue
      .split(",")
      .map((value) => Number(value))
      .filter((value) => Number.isInteger(value) && value > 0);
    const openContinueMovePrompt = () => {
      const isNewCustomFolder = resolvedMoveTargetFolder === "__new__" && Boolean(moveNewFolderName.trim());
      const isExistingCustomFolder = resolvedMoveTargetFolder.startsWith("custom__");
      const isInboxFolder = resolvedMoveTargetFolder === "inbox";

      if (!isNewCustomFolder && !isExistingCustomFolder && !isInboxFolder) {
        setLocalNotice({
          tone: "error",
          message: "계속 이동은 받은메일함 또는 내 메일함을 선택했을 때 설정할 수 있습니다."
        });
        return;
      }

      const availableMessages = [...filteredMessages, ...(previewMessage ? [previewMessage] : [])];
      const promptMessages = messageIds
        .map((messageId) => availableMessages.find((message) => message.id === messageId))
        .filter((message) => message !== undefined);
      const senderAddresses = [
        ...new Set(
          promptMessages
            .filter((message) => message.direction === "inbound")
            .map((message) => message.fromAddress.trim().toLowerCase())
            .filter(Boolean)
        )
      ];

      if (
        promptMessages.length !== messageIds.length ||
        senderAddresses.length !== 1 ||
        promptMessages.some((message) => message.direction !== "inbound")
      ) {
        setLocalNotice({
          tone: "error",
          message: "계속 이동은 같은 발신자의 받은 메일만 선택해 설정할 수 있습니다."
        });
        return;
      }

      const targetLabel = isNewCustomFolder
        ? moveNewFolderName.trim()
        : (folders.find((folder) => folder.systemName === resolvedMoveTargetFolder)?.name ?? "선택한 메일함");
      const nextPrompt: ContinueMovePromptState = {
        formId,
        messageIds,
        newFolderName: isNewCustomFolder ? moveNewFolderName.trim() : "",
        senderAddress: senderAddresses[0],
        targetFolder: resolvedMoveTargetFolder,
        targetLabel
      };
      const existingRule = senderMoveRules.find(
        (rule) => rule.senderAddress.trim().toLowerCase() === senderAddresses[0]
      );

      setContinueMoveExisting(false);
      if (isInboxFolder && !existingRule) {
        setLocalNotice({
          tone: "error",
          message: "이 발신자에 등록된 자동분류가 없습니다. 일반 이동을 이용해 주세요."
        });
        return;
      }

      if (existingRule && existingRule.targetFolderSystemName !== resolvedMoveTargetFolder) {
        setMoveMenuState({ open: false, scope: interactionScopeKey });
        setContinueMoveReplacementPrompt({
          ...nextPrompt,
          existingTargetLabel: existingRule.targetFolderName,
          existingTargetFolder: existingRule.targetFolderSystemName,
          includeExisting: false
        });
        return;
      }

      setContinueMovePrompt(nextPrompt);
    };

    return (
      <>
        <ToolbarPortalMenu
          anchorElement={moveMenuAnchor}
          className="z-[9999] w-[min(280px,calc(100vw-24px))]"
          open={moveMenuOpen}
          rootKind="move"
        >
          <div
            id={formId}
            className="relative overflow-hidden rounded-[18px] border border-[#e4ebf8] bg-white p-3 shadow-[0_20px_44px_rgba(25,38,72,0.12)]"
          >
            <MoveMenuFieldset>
              <div className="space-y-2">
                {moveFolderOptionGroups.map((group, groupIndex) => (
                  <div
                    key={group.key}
                    className={groupIndex === 0 ? "space-y-1.5" : "space-y-1.5 border-t border-[#edf2f8] pt-2"}
                  >
                    {group.items.map((item) => {
                      const selected = resolvedMoveTargetFolder === item.systemName;

                      return (
                        <button
                          key={item.systemName}
                          type="button"
                          onClick={() => setMoveTargetFolder(item.systemName)}
                          className={`flex w-full cursor-pointer items-center rounded-[12px] px-3 py-2 text-left text-[12px] font-medium transition-colors ${
                            selected
                              ? "border border-[#dbe6ff] bg-[#edf4ff] font-semibold text-[#2f6df6]"
                              : "border border-transparent text-[#344767] hover:bg-[#f6f9ff] hover:text-[#2f6df6]"
                          }`}
                        >
                          <span className="flex min-w-0 items-center gap-1.5">
                            <span className="truncate">{item.label}</span>
                            <span className={`shrink-0 text-[12px] ${selected ? "text-[#2f6df6]" : "text-[#8c98ad]"}`}>
                              {compactCount(item.count)}
                            </span>
                          </span>
                        </button>
                      );
                    })}
                  </div>
                ))}
              </div>

              <div className="mt-3 rounded-[14px] border border-[#e8eef9] bg-[#fbfcff] p-3">
                <div className="flex items-center gap-2">
                  <label htmlFor={inputId} className="sr-only">
                    이동할 메일함 직접 입력
                  </label>
                  <input
                    id={inputId}
                    type="text"
                    value={moveNewFolderName}
                    onChange={(event) => setMoveNewFolderName(event.target.value)}
                    placeholder="새 메일함"
                    className="h-10 min-w-0 flex-1 rounded-[12px] border border-[#dbe5f7] bg-white px-3 text-[13px] text-[#243555] outline-none transition-colors placeholder:text-[#97a7c2] focus:border-[#bcd1ff]"
                  />
                  <button
                    type="button"
                    onClick={() => {
                      const nextFolderName = moveNewFolderName.trim();

                      if (!nextFolderName) {
                        setLocalNotice({
                          tone: "error",
                          message: "새 메일함 이름을 입력해주세요."
                        });
                        return;
                      }

                      setMoveTargetFolder("__new__");
                      setMoveNewFolderName(nextFolderName);
                    }}
                    className="om-btn-secondary h-10 shrink-0 rounded-[12px] px-3 text-[12px] text-[#344767]"
                  >
                    추가
                  </button>
                </div>
                {resolvedMoveTargetFolder === "__new__" && moveNewFolderName.trim() ? (
                  <p className="mt-2 text-[12px] text-[#5f7396]">
                    새 메일함 <span className="font-semibold text-[#2f6df6]">{moveNewFolderName.trim()}</span>
                    으로 이동합니다.
                  </p>
                ) : null}
              </div>

              <div className="mt-3 flex items-center gap-2">
                <button
                  type="button"
                  onClick={() => {
                    // This callback only runs after a user click; the render helper never invokes it.
                    // eslint-disable-next-line react-hooks/refs
                    void moveMessagesByClient(messageIds, resolvedMoveTargetFolder, {
                      newFolderName: resolvedMoveTargetFolder === "__new__" ? moveNewFolderName.trim() : ""
                    });
                  }}
                  className="inline-flex h-10 flex-1 cursor-pointer items-center justify-center gap-2 rounded-[14px] bg-[#2f6df6] px-4 text-[13px] font-semibold text-white transition-all duration-200 hover:bg-[#245de3]"
                >
                  이동
                </button>
                <MoveMenuContinueButton onClick={openContinueMovePrompt} />
              </div>
            </MoveMenuFieldset>
          </div>
        </ToolbarPortalMenu>
        <ContinueMoveDialog
          includeExisting={continueMoveExisting}
          onCancel={() => {
            if (!pendingMoveKey) setContinueMovePrompt(null);
          }}
          onConfirm={() => {
            const prompt = continueMovePrompt?.formId === formId ? continueMovePrompt : null;

            if (!prompt || pendingMoveKey) {
              return;
            }

            void moveMessagesByClient(prompt.messageIds, prompt.targetFolder, {
              continueMove: true,
              formId: prompt.formId,
              moveExistingMessages: continueMoveExisting,
              newFolderName: prompt.newFolderName,
              senderAddress: prompt.senderAddress,
              showCompletionDialog: true,
              targetLabel: prompt.targetLabel
            }).then((succeeded) => {
              if (succeeded) setContinueMovePrompt(null);
            });
          }}
          onIncludeExistingChange={setContinueMoveExisting}
          pending={pendingMoveKey !== null}
          prompt={continueMovePrompt?.formId === formId ? continueMovePrompt : null}
        />
      </>
    );
  };

  const renderMessageDetailContent = ({ standalone }: { standalone: boolean }) => {
    if (!previewMessage || (!standalone && splitDetailPreviewDismissed)) {
      return (
        <div className="flex h-full flex-col items-center justify-center px-8 text-center">
          <p className="text-[16px] font-semibold tracking-[-0.03em] text-[#1f2c45]">메일을 선택해주세요.</p>
          <p className="mt-2 text-[13px] leading-6 text-[#7f8da7]">선택한 메일의 내용이 이 영역에 표시됩니다.</p>
        </div>
      );
    }

    return (
      <>
        {standalone ? (
          <>
            <div className="relative border-b border-[#edf1f6] px-3 py-3 sm:px-4 lg:px-8">
              <Link
                href={backToListHref}
                prefetch={false}
                scroll={false}
                aria-label={`${currentFolderLabel} 목록으로 돌아가기`}
                className="absolute left-1 top-1/2 inline-flex h-7 w-7 -translate-y-1/2 cursor-pointer items-center justify-center text-[#65738a] transition-colors hover:text-[#2f6df6] lg:left-0.5"
              >
                <BackIcon className="h-[18px] w-[18px]" />
              </Link>
              <div className="flex flex-wrap items-center gap-x-2 gap-y-2 pl-6 text-[15px] leading-none sm:gap-x-3 sm:pl-6 sm:text-[16px] lg:pl-0">
                <h2 className="text-[16px] font-semibold tracking-[-0.03em] text-[#111a33] sm:text-[17px]">
                  {currentFolderLabel}
                </h2>
                <span className="font-semibold text-[#2f6df6]">{formatCount(totalMessages)}</span>
                <span className="text-[#7c879b]">/ {formatCount(currentFolderTotalCount)}</span>
                {renderFolderSummaryTrailingAction()}
              </div>
            </div>

            <div className="border-b border-[#edf1f6] px-3 py-3 sm:px-4 lg:px-8">
              <DraggableHorizontalScroll
                className="-mx-1 overflow-x-auto pb-1 text-[14px] text-[#5f6f89] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
                innerClassName="flex min-w-max items-center gap-x-5 gap-y-2 px-1"
              >
                {replyHref ? (
                  <Link href={replyHref} prefetch={false} className={mailboxBulkActionClass()}>
                    답장
                  </Link>
                ) : (
                  <span className={mailboxBulkActionClass({ enabled: false })}>답장</span>
                )}
                {replyAllHref ? (
                  <Link href={replyAllHref} prefetch={false} className={mailboxBulkActionClass()}>
                    전체답장
                  </Link>
                ) : (
                  <span className={mailboxBulkActionClass({ enabled: false })}>전체답장</span>
                )}
                {forwardHref ? (
                  <Link href={forwardHref} prefetch={false} className={mailboxBulkActionClass()}>
                    전달
                  </Link>
                ) : (
                  <span className={mailboxBulkActionClass({ enabled: false })}>전달</span>
                )}

                {currentMailboxFolder === "trash" ? (
                  <button
                    type="button"
                    disabled={pendingMoveKey !== null || !previewMessage}
                    onClick={() => {
                      if (previewMessage) {
                        void moveMessagesByClient([previewMessage.id], "inbox");
                      }
                    }}
                    className={mailboxBulkActionClass({
                      enabled: pendingMoveKey === null && Boolean(previewMessage)
                    })}
                  >
                    복구
                  </button>
                ) : null}

                <button
                  type="button"
                  disabled={pendingMoveKey !== null || !previewMessage}
                  onClick={() => {
                    if (previewMessage) {
                      void moveMessagesByClient([previewMessage.id], "trash");
                    }
                  }}
                  className={mailboxBulkActionClass({
                    enabled: pendingMoveKey === null && Boolean(previewMessage)
                  })}
                >
                  {pendingMoveKey ? (
                    <span className="inline-flex items-center gap-1.5">
                      <SpinnerIcon className="h-[13px] w-[13px] animate-spin" />
                      삭제
                    </span>
                  ) : (
                    "삭제"
                  )}
                </button>

                <button
                  type="button"
                  disabled={pendingMoveKey !== null || !previewMessage}
                  onClick={() => {
                    if (!previewMessage) return;
                    void moveMessagesByClient([previewMessage.id], currentMailboxFolder === "spam" ? "inbox" : "spam", {
                      releaseSpam: currentMailboxFolder === "spam"
                    });
                  }}
                  className={mailboxBulkActionClass({
                    enabled: pendingMoveKey === null && Boolean(previewMessage)
                  })}
                >
                  {currentMailboxFolder === "spam" ? tr("스팸 해제") : tr("스팸신고")}
                </button>

                <form
                  action={markMailboxMessagesReadAction}
                  onSubmit={() => {
                    markMessagesReadOptimistically([previewMessage.id], !previewMessage.isRead);
                  }}
                >
                  <input type="hidden" name="folder" value={currentMailboxFolder} />
                  <input type="hidden" name="detail" value={detailView} />
                  <input type="hidden" name="page" value={resolvedCurrentPage} />
                  <input type="hidden" name="pageSize" value={resolvedPageSize} />
                  <input type="hidden" name="tab" value={resolvedMessageFilter} />
                  <input type="hidden" name="field" value={resolvedSearchField} />
                  <input type="hidden" name="q" value={resolvedSearchQuery} />
                  <input type="hidden" name="messageIds" value={standaloneMessageIdsValue} />
                  <input type="hidden" name="readState" value={previewMessage.isRead ? "unread" : "read"} />
                  <button type="submit" className={mailboxBulkActionClass()}>
                    {previewMessage.isRead ? "안읽음" : "읽음"}
                  </button>
                </form>

                <div className="relative inline-flex" data-move-menu-root="true">
                  <button
                    type="button"
                    onClick={(event) => {
                      setMoveMenuAnchor(event.currentTarget);
                      setMoveTargetFolder(currentMailboxFolder === "all" ? "inbox" : currentMailboxFolder || "inbox");
                      setMoveMenuState((prev) => ({
                        open: !(prev.scope === interactionScopeKey && prev.open),
                        scope: interactionScopeKey
                      }));
                    }}
                    className={mailboxBulkActionClass({
                      gapClassName: "gap-1"
                    })}
                  >
                    이동
                    <ChevronDownIcon className="h-[11px] w-[11px]" />
                  </button>
                  {moveMenuOpen ? renderMoveMenuPopover(standaloneMessageIdsValue) : null}
                </div>

                <div className="relative inline-flex" data-detail-menu-root="true">
                  <button
                    type="button"
                    aria-expanded={detailMenuOpen}
                    aria-haspopup="menu"
                    onClick={(event) => {
                      setDetailMenuAnchor(event.currentTarget);
                      setDetailMenuState((prev) => ({
                        open: !(prev.open && prev.scope === detailPanelScopeKey),
                        scope: detailPanelScopeKey
                      }));
                    }}
                    className={mailboxBulkActionClass({
                      gapClassName: "gap-1"
                    })}
                  >
                    더보기
                    <ChevronDownIcon className="h-[11px] w-[11px]" />
                  </button>
                  {detailMenuOpen ? (
                    <ToolbarPortalMenu
                      anchorElement={detailMenuAnchor}
                      open={detailMenuOpen}
                      rootKind="detail"
                      className={`z-[9999] w-[188px] ${popoverMenuSurfaceClass()}`}
                    >
                      <button
                        type="button"
                        disabled={!previewMessage.rawSourceAvailable}
                        onClick={openRawSourceModal}
                        className={popoverMenuItemClass({
                          disabled: !previewMessage.rawSourceAvailable
                        })}
                      >
                        메일 원본 보기
                      </button>
                    </ToolbarPortalMenu>
                  ) : null}
                </div>
              </DraggableHorizontalScroll>
            </div>
          </>
        ) : null}

        <div className="min-h-0 flex-1 overflow-y-auto">
          <div className="border-b border-[#edf2fb] px-3 py-4 sm:px-4 lg:px-8">
            <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between lg:gap-6">
              <div className="min-w-0">
                <div className="flex items-center gap-[10px]">
                  <ActionTooltip label={previewMessage.isStarred ? "중요 표시 해제" : "중요 표시"}>
                    <button
                      type="button"
                      onClick={() => {
                        void toggleMessageStarByClient(previewMessage.id);
                      }}
                      disabled={activeMessageStarPending}
                      aria-label={previewMessage.isStarred ? "중요 표시 해제" : "중요 표시"}
                      className={`inline-flex h-5 w-5 shrink-0 items-center justify-center transition-colors ${
                        activeMessageStarPending ? "cursor-wait opacity-60" : "cursor-pointer"
                      } ${
                        previewMessage.isStarred
                          ? "text-[#2f6df6] hover:text-[#245de3]"
                          : "text-[#c4cddd] hover:text-[#88a4ea]"
                      }`}
                    >
                      <StarIcon filled={previewMessage.isStarred} className="h-5 w-5" />
                    </button>
                  </ActionTooltip>
                  <h3 className="truncate text-[20px] font-semibold tracking-[-0.04em] text-[#1a2943] sm:text-[22px] lg:text-[24px]">
                    {previewMessage.subject || "(제목 없음)"}
                  </h3>
                </div>

                <div className="mt-4 min-w-0">
                  <div className="flex flex-wrap items-center gap-2 text-[16px]">
                    <button
                      type="button"
                      onClick={(event) => {
                        const anchor = event.currentTarget;
                        setSenderMenuState({ messageId: null, scope: interactionScopeKey });
                        setDetailSenderMenuAnchor((current) => current ? null : anchor);
                      }}
                      className="cursor-pointer font-semibold text-[#1f2d46] transition-colors hover:text-[#2f6df6] hover:underline"
                      data-sender-menu-root="true"
                    >
                      {previewMessage.senderContactName || senderLabel(previewMessage.fromName, previewMessage.fromAddress)}
                    </button>
                    <span className="text-[#5c6d88]">&lt;{previewMessage.fromAddress}&gt;</span>
                    {!standalone ? <ChevronDownIcon className="h-4 w-4 text-[#8796b2]" /> : null}
                    {detailSenderMenuAnchor ? (
                      <MailSenderProfilePopover
                        anchorElement={detailSenderMenuAnchor}
                        currentFolder={currentMailboxFolder}
                        displayName={previewMessage.senderContactName || senderLabel(previewMessage.fromName, previewMessage.fromAddress)}
                        email={previewMessage.fromAddress}
                        onClose={() => setDetailSenderMenuAnchor(null)}
                        onCompose={openComposeToAddress}
                          onMailboxChanged={() => { void fetchMailboxData({ silent: true, preserveSelection: true }); }}
                          onSaveClassification={saveSenderClassificationByClient}
                        onNavigate={(href) => navigateWithinMailbox(href)}
                      />
                    ) : null}
                  </div>
                  <div className="mt-2 flex flex-wrap items-center gap-3 text-[15px] text-[#607390]">
                    <span>받는사람</span>
                    <span className="text-[#30425f]">{previewMessage.toAddresses}</span>
                    {!standalone ? <ChevronDownIcon className="h-4 w-4 text-[#8796b2]" /> : null}
                  </div>
                </div>
              </div>

              <div className="flex shrink-0 flex-col gap-4 lg:items-end lg:gap-5">
                <div className="text-[14px] text-[#72839f]">{formatFullDate(previewMessage.receivedAt)}</div>
                {!standalone ? (
                  <div className="flex flex-wrap items-center gap-1">
                    <form
                      action={markMailboxMessagesReadAction}
                      onSubmit={() => {
                        markMessagesReadOptimistically([previewMessage.id], !previewMessage.isRead);
                      }}
                    >
                      <input type="hidden" name="folder" value={currentMailboxFolder} />
                      <input type="hidden" name="detail" value={detailView} />
                      <input type="hidden" name="page" value={resolvedCurrentPage} />
                      <input type="hidden" name="pageSize" value={resolvedPageSize} />
                      <input type="hidden" name="tab" value={resolvedMessageFilter} />
                      <input type="hidden" name="field" value={resolvedSearchField} />
                      <input type="hidden" name="q" value={resolvedSearchQuery} />
                      <input type="hidden" name="message" value={previewMessage.id} />
                      <input type="hidden" name="messageIds" value={previewMessage.id} />
                      <input type="hidden" name="readState" value={previewMessage.isRead ? "unread" : "read"} />
                      <button
                        type="submit"
                        aria-label={previewMessage.isRead ? "안읽음으로 표시" : "읽음으로 표시"}
                        className="inline-flex h-10 cursor-pointer items-center rounded-full px-4 text-[13px] font-medium text-[#60718e] transition-all duration-200 hover:bg-[#f4f7fc] hover:text-[#2f6df6] hover:shadow-[0_10px_24px_rgba(25,38,72,0.08)]"
                      >
                        {previewMessage.isRead ? "안읽음" : "읽음"}
                      </button>
                    </form>
                    {replyHref ? (
                      <ActionTooltip label="답장">
                        <Link href={replyHref} prefetch={false} aria-label="답장" className={detailHeaderActionClass()}>
                          <ReplyIcon className="h-5 w-5" />
                        </Link>
                      </ActionTooltip>
                    ) : null}
                    {replyAllHref ? (
                      <ActionTooltip label="전체 답장">
                        <Link
                          href={replyAllHref}
                          prefetch={false}
                          aria-label="전체 답장"
                          className={detailHeaderActionClass()}
                        >
                          <ReplyAllIcon className="h-5 w-5" />
                        </Link>
                      </ActionTooltip>
                    ) : null}
                    {forwardHref ? (
                      <ActionTooltip label="전달">
                        <Link
                          href={forwardHref}
                          prefetch={false}
                          aria-label="전달"
                          className={detailHeaderActionClass()}
                        >
                          <ForwardIcon className="h-5 w-5" />
                        </Link>
                      </ActionTooltip>
                    ) : null}
                    <ActionTooltip label="상세 패널 닫기">
                      <button
                        type="button"
                        aria-label="닫기"
                        onClick={() => setDismissedDetailScopeKey(detailPanelScopeKey)}
                        className={detailHeaderActionClass()}
                      >
                        <CloseIcon className="h-5 w-5" />
                      </button>
                    </ActionTooltip>
                    <div className="relative" data-detail-menu-root="true">
                      <ActionTooltip label="추가 메뉴">
                        <button
                          type="button"
                          aria-label="추가 메뉴"
                          aria-expanded={detailMenuOpen}
                          aria-haspopup="menu"
                          onClick={(event) => {
                            setDetailMenuAnchor(event.currentTarget);
                            setDetailMenuState((prev) => ({
                              open: !(prev.open && prev.scope === detailPanelScopeKey),
                              scope: detailPanelScopeKey
                            }));
                          }}
                          className={detailHeaderActionClass()}
                        >
                          <MoreIcon className="h-5 w-5" />
                        </button>
                      </ActionTooltip>

                      {detailMenuOpen ? (
                        <ToolbarPortalMenu
                          align="right"
                          anchorElement={detailMenuAnchor}
                          open={detailMenuOpen}
                          rootKind="detail"
                          className={`z-[9999] w-[188px] ${popoverMenuSurfaceClass()}`}
                        >
                          <button
                            type="button"
                            disabled={!previewMessage.rawSourceAvailable}
                            onClick={openRawSourceModal}
                            className={popoverMenuItemClass({
                              disabled: !previewMessage.rawSourceAvailable
                            })}
                          >
                            메일 원본 보기
                          </button>
                        </ToolbarPortalMenu>
                      ) : null}
                    </div>
                  </div>
                ) : null}
              </div>
            </div>
          </div>

          <div className="px-3 py-5 sm:px-4 sm:py-6 lg:px-8 lg:py-8">
            <div className="w-full">
              {previewMessage.direction === "outbound" && previewMessage.transportStatus !== "sent" ? (
                <div
                  role="status"
                  className={`mb-6 rounded-[12px] border px-4 py-3 text-[13px] leading-5 ${
                    previewMessage.transportStatus === "failed"
                      ? "border-[#f0d0d0] bg-[#fff8f8] text-[#8f3333]"
                      : "border-[#d8e5ff] bg-[#f3f7ff] text-[#2f5cae]"
                  }`}
                >
                  <p className="font-semibold">
                    {previewMessage.transportStatus === "failed" ? "발송 실패" : "발송 대기 중"}
                  </p>
                  <p className="mt-1">
                    {previewMessage.transportStatus === "failed"
                      ? getPublicMailTransportErrorMessage(previewMessage.transportResponse ?? "", "smtp")
                      : "메일은 발송 대기열에 등록됐습니다. 실제 발송 결과를 확인하는 중입니다."}
                  </p>
                </div>
              ) : null}
              {previewMessage.direction === "inbound" &&
              activeTranslationState &&
              (activeTranslationState.loading ||
                activeTranslationState.error ||
                activeTranslationState.data?.status === "translated") ? (
                <section className="mb-6 max-w-[560px] rounded-[12px] bg-[#f1f5fb] px-4 py-3.5 text-[#23334f] sm:px-5 sm:py-4">
                  <div className="flex items-start gap-3">
                    <span className="mt-0.5 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-[8px] bg-white text-[#52657f] shadow-[0_5px_16px_rgba(35,51,79,0.08)]">
                      {activeTranslationState.loading ? (
                        <SpinnerIcon className="h-[17px] w-[17px] animate-spin" />
                      ) : (
                        <GlobeIcon className="h-[17px] w-[17px]" />
                      )}
                    </span>
                    <div className="min-w-0 flex-1">
                      <p className="text-[14px] font-semibold tracking-[-0.02em] text-[#263750] sm:text-[15px]">
                        {activeTranslationState.loading
                          ? ui("메일 언어를 확인하고 있습니다.")
                          : activeTranslationState.error
                            ? ui("메일을 번역하지 못했습니다.")
                            : showOriginalMessage
                              ? ui("원문 표시 중")
                              : `${ui("번역됨:")} ${activeTranslationState.data?.sourceLanguageLabel ?? ""} → ${activeTranslationState.data?.targetLanguageLabel ?? ""}`}
                      </p>
                      {activeTranslationState.loading ? (
                        <p className="mt-1 text-[12px] leading-5 text-[#687a95]">{ui("메일을 번역하고 있습니다.")}</p>
                      ) : activeTranslationState.error ? (
                        <button
                          type="button"
                          onClick={() => setTranslationRetryVersion((current) => current + 1)}
                          className="mt-1.5 cursor-pointer text-[12px] font-semibold text-[#2f6df6] transition-colors hover:text-[#245de3]"
                        >
                          {ui("다시 시도")}
                        </button>
                      ) : (
                        <>
                          <p className="mt-1 text-[12px] leading-5 text-[#687a95]">
                            {ui("번역 기능은 실수할 수 있으니 중요한 내용은 원문과 함께 확인하세요.")}
                          </p>
                          <button
                            type="button"
                            onClick={() =>
                              setOriginalMessageScope((current) =>
                                current === translationScope ? "" : translationScope
                              )
                            }
                            className="mt-1.5 cursor-pointer text-[12px] font-semibold text-[#2f6df6] transition-colors hover:text-[#245de3]"
                          >
                            {showOriginalMessage ? ui("번역 보기") : ui("원본 보기")}
                          </button>
                        </>
                      )}
                    </div>
                  </div>
                </section>
              ) : null}

              {previewAttachments.length > 0 ? (
                <section className="mb-8">
                  <div className="flex flex-wrap items-center justify-between gap-2 pb-2 text-[13px]">
                    <div className="flex min-w-0 items-center gap-2 text-[#64748e]">
                      <PaperclipIcon className="h-[15px] w-[15px] shrink-0 text-[#364967]" />
                      <span className="font-semibold text-[#263750]">첨부파일 {previewAttachments.length}개</span>
                      <span>{formatAttachmentSize(totalAttachmentSize)}</span>
                    </div>
                    <div className="flex shrink-0 items-center gap-3">
                      {previewAttachments.some((attachment) => !attachment.isPendingPublication) ? (
                        <button
                          type="button"
                          onClick={() => downloadAllAttachments(previewMessage.id, previewAttachments)}
                          className="cursor-pointer font-medium text-[#2f6df6] transition-colors hover:text-[#245de3]"
                        >
                          모두 저장
                        </button>
                      ) : null}
                    </div>
                  </div>

                  <div className="overflow-hidden border border-[#dfe5ee] bg-white">
                    {previewAttachments.map((attachment, index) => (
                      <div
                        key={`${attachment.index}:${attachment.filename}:${index}`}
                        className={`flex min-h-12 items-center gap-3 px-3 py-2 ${
                          index === 0 ? "" : "border-t border-[#eef2f8]"
                        }`}
                      >
                        <AttachmentFileIcon attachment={attachment} />

                        <div className="flex min-w-0 flex-1 items-center gap-1.5">
                          {attachment.isPreviewable && !attachment.isLargeAttachment ? (
                            <button
                              type="button"
                              onClick={() => openAttachmentPreview(previewMessage.id, attachment)}
                              title={attachment.filename}
                              className="min-w-0 cursor-pointer truncate text-left text-[13px] font-medium text-[#1f2d46] transition-colors hover:text-[#2f6df6]"
                            >
                              {attachment.filename}
                            </button>
                          ) : attachment.isLargeAttachment && !attachment.isPendingPublication ? (
                            <button
                              type="button"
                              onClick={() => downloadAttachment(previewMessage.id, attachment)}
                              title={attachment.filename}
                              className="min-w-0 cursor-pointer truncate text-left text-[13px] font-medium text-[#1f2d46] transition-colors hover:text-[#2f6df6]"
                            >
                              {attachment.filename}
                            </button>
                          ) : (
                            <p title={attachment.filename} className="min-w-0 truncate text-[13px] font-medium text-[#1f2d46]">
                              {attachment.filename}
                            </p>
                          )}
                          <span className="shrink-0 text-[11px] text-[#96a0b0]">
                            {formatAttachmentSize(attachment.sizeBytes)}
                          </span>
                          {attachment.isLargeAttachment ? (
                            <span className="shrink-0 rounded-full bg-[#edf3ff] px-2 py-0.5 text-[10px] font-semibold text-[#2f6df6]">
                              대용량
                            </span>
                          ) : null}
                          {attachment.isPendingPublication ? (
                            <span className="shrink-0 text-[11px] text-[#8794a8]">발송 전</span>
                          ) : null}
                          {attachment.isPreviewable && !attachment.isLargeAttachment ? (
                            <button
                              type="button"
                              aria-label={`${attachment.filename} 미리보기`}
                              onClick={() => openAttachmentPreview(previewMessage.id, attachment)}
                              className="inline-flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center text-[#7f8b9e] transition-colors hover:text-[#2f6df6]"
                            >
                              <SearchIcon className="h-[12px] w-[12px]" />
                            </button>
                          ) : null}
                        </div>

                        {attachment.isPendingPublication ? null : (
                          <button
                            type="button"
                            aria-label={`${attachment.filename} 다운로드`}
                            onClick={() => downloadAttachment(previewMessage.id, attachment)}
                            className="inline-flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center text-[#8995a8] transition-colors hover:text-[#2f6df6]"
                          >
                            <DownloadIcon className="h-[16px] w-[16px]" />
                          </button>
                        )}
                      </div>
                    ))}
                  </div>
                </section>
              ) : null}

              {previewHtml ? (
                <MailHtmlPreview
                  key={previewMessage ? `${previewMessage.id}:${previewHtml.length}:${Number(currentMailboxFolder === "spam" && mailData?.spamSettings.hideRemoteContent)}` : "mail-preview"}
                  blockRemoteContent={currentMailboxFolder === "spam" && Boolean(mailData?.spamSettings.hideRemoteContent)}
                  html={previewHtml}
                />
              ) : (
                <div className="space-y-5 text-[17px] leading-[2] text-[#2c3b56]">
                  {(previewLines ?? []).map((line, index) => (
                    <p key={`${line}-${index}`} className={line === "" ? "h-4" : ""}>
                      {line || "\u00A0"}
                    </p>
                  ))}
                </div>
              )}
            </div>
          </div>
        </div>
      </>
    );
  };

  const renderAttachmentPreviewModal = () => {
    if (
      !attachmentPreviewModalOpen ||
      !attachmentPreviewState ||
      !activeAttachmentPreview ||
      !activeAttachmentPreviewFile ||
      typeof document === "undefined"
    ) {
      return null;
    }

    const canShowPrevious = attachmentPreviewCurrentIndex > 0;
    const canShowNext = attachmentPreviewCurrentIndex < attachmentPreviewItems.length - 1;
    const moveButtonClass = (disabled: boolean) =>
      `inline-flex h-14 w-14 items-center justify-center rounded-full border border-white/12 bg-[rgba(11,16,30,0.76)] text-white shadow-[0_20px_50px_rgba(0,0,0,0.28)] backdrop-blur-[18px] transition-all duration-200 ${
        disabled
          ? "cursor-not-allowed opacity-35"
          : "cursor-pointer hover:bg-[rgba(22,32,58,0.92)] hover:text-[#dce7ff]"
      }`;

    return createPortal(
      <div className="fixed inset-0 z-[96] bg-[rgba(3,8,20,0.88)] text-white backdrop-blur-[10px]">
        <button
          type="button"
          aria-label="첨부파일 미리보기 닫기"
          onClick={() => setAttachmentPreviewState(null)}
          className="absolute inset-0 cursor-default"
        />

        <div className="relative z-[97] flex h-full flex-col">
          <div className="flex items-center justify-between gap-4 border-b border-white/10 px-4 py-4 sm:px-6">
            <div className="min-w-0">
              <div className="flex min-w-0 flex-wrap items-end gap-x-2 gap-y-1">
                <strong className="truncate text-[20px] font-semibold tracking-[-0.03em] text-white sm:text-[24px]">
                  {activeAttachmentPreviewFile.name}
                  {activeAttachmentPreviewFile.extension ? (
                    <span className="text-white/52">{activeAttachmentPreviewFile.extension}</span>
                  ) : null}
                </strong>
                <span className="text-[13px] text-white/58">
                  {formatAttachmentSize(activeAttachmentPreview.sizeBytes)}
                </span>
              </div>
            </div>

            <div className="flex shrink-0 items-center gap-2 sm:gap-3">
              {attachmentPreviewItems.length > 1 ? (
                <span className="hidden text-[13px] font-semibold text-white/72 sm:inline-flex">
                  {attachmentPreviewCountLabel}
                </span>
              ) : null}
              <button
                type="button"
                onClick={() => downloadAttachment(attachmentPreviewState.messageId, activeAttachmentPreview)}
                className="inline-flex h-11 items-center gap-2 rounded-full border border-white/12 bg-white/6 px-4 text-[13px] font-semibold text-white transition-colors hover:bg-white/12"
              >
                <DownloadIcon className="h-[16px] w-[16px]" />
                다운로드
              </button>
              <button
                type="button"
                aria-label="첨부파일 미리보기 닫기"
                onClick={() => setAttachmentPreviewState(null)}
                className="inline-flex h-11 w-11 items-center justify-center rounded-full border border-white/12 bg-white/6 text-white transition-colors hover:bg-white/12"
              >
                <CloseIcon className="h-[18px] w-[18px]" />
              </button>
            </div>
          </div>

          <div className="relative flex min-h-0 flex-1 items-center justify-center px-4 py-6 sm:px-6 sm:py-8">
            <div className="relative flex h-full max-h-[calc(100vh-148px)] w-full max-w-[1320px] items-center justify-center">
              {activeAttachmentPreviewIsImage ? (
                // Authenticated attachment URLs are intentionally rendered without the public image optimizer.
                // eslint-disable-next-line @next/next/no-img-element
                <img
                  src={activeAttachmentPreviewUrl}
                  alt={activeAttachmentPreview.filename}
                  draggable={false}
                  className="max-h-full max-w-full select-none rounded-[18px] object-contain shadow-[0_30px_80px_rgba(0,0,0,0.3)]"
                />
              ) : (
                <div className="h-full w-full overflow-hidden rounded-[18px] border border-white/12 bg-white shadow-[0_30px_80px_rgba(0,0,0,0.24)]">
                  <iframe
                    title={activeAttachmentPreview.filename}
                    src={activeAttachmentPreviewUrl}
                    className="h-full w-full bg-white"
                  />
                </div>
              )}

              {attachmentPreviewItems.length > 1 ? (
                <>
                  <button
                    type="button"
                    aria-label="이전 첨부 보기"
                    disabled={!canShowPrevious}
                    onClick={() =>
                      setAttachmentPreviewState((current) => {
                        if (!current) return current;
                        const currentIndex = Math.min(current.currentIndex, attachmentPreviewItems.length - 1);
                        return currentIndex > 0 ? { ...current, currentIndex: currentIndex - 1 } : current;
                      })
                    }
                    className={`absolute left-0 top-1/2 -translate-y-1/2 sm:left-2 ${moveButtonClass(
                      !canShowPrevious
                    )}`}
                  >
                    <BackIcon className="h-6 w-6" />
                  </button>
                  <button
                    type="button"
                    aria-label="다음 첨부 보기"
                    disabled={!canShowNext}
                    onClick={() =>
                      setAttachmentPreviewState((current) => {
                        if (!current) return current;
                        const currentIndex = Math.min(current.currentIndex, attachmentPreviewItems.length - 1);
                        return currentIndex < attachmentPreviewItems.length - 1
                          ? { ...current, currentIndex: currentIndex + 1 }
                          : current;
                      })
                    }
                    className={`absolute right-0 top-1/2 -translate-y-1/2 sm:right-2 ${moveButtonClass(!canShowNext)}`}
                  >
                    <BackIcon className="h-6 w-6 rotate-180" />
                  </button>
                </>
              ) : null}
            </div>

            {attachmentPreviewItems.length > 1 ? (
              <div className="pointer-events-none absolute bottom-6 left-1/2 -translate-x-1/2 rounded-full bg-[rgba(9,14,26,0.72)] px-4 py-2 text-[13px] font-semibold text-white/88 shadow-[0_16px_40px_rgba(0,0,0,0.24)] backdrop-blur-[16px] sm:hidden">
                {attachmentPreviewCountLabel}
              </div>
            ) : null}
          </div>
        </div>
      </div>,
      document.body
    );
  };

  return (
    <div className="relative h-full w-full overflow-hidden bg-[#fcfdff] text-[#13213b]">
      <MailExitDraftRecovery mailbox={mailbox || userEmail} />
      {showDkimRotationNotice ? <MailServiceNoticeDialog domain={domain} /> : null}
      <ContinueMoveCompletionDialog
        onClose={() => setContinueMoveCompletionOpen(false)}
        open={continueMoveCompletionOpen}
      />
      <ContinueMoveReplacementDialog
        onCancel={() => {
          if (!pendingMoveKey) setContinueMoveReplacementPrompt(null);
        }}
        onConfirm={() => {
          const prompt = continueMoveReplacementPrompt;

          if (!prompt || pendingMoveKey) return;

          void moveMessagesByClient(prompt.messageIds, prompt.targetFolder, {
            continueMove: true,
            expectedExistingTargetFolder: prompt.existingTargetFolder,
            formId: prompt.formId,
            moveExistingMessages: prompt.includeExisting,
            newFolderName: prompt.newFolderName,
            replaceExistingRule: true,
            senderAddress: prompt.senderAddress,
            showCompletionDialog: true,
            targetLabel: prompt.targetLabel
          }).then((succeeded) => {
            if (succeeded) setContinueMoveReplacementPrompt(null);
          });
        }}
        onIncludeExistingChange={(value) => {
          setContinueMoveReplacementPrompt((current) => (current ? { ...current, includeExisting: value } : current));
        }}
        pending={pendingMoveKey !== null}
        prompt={continueMoveReplacementPrompt}
      />

      {senderSpamPrompt && senderSpamPrompt.scope === interactionScopeKey ? (
        <div
          className="pointer-events-none fixed inset-x-0 bottom-4 z-[95] flex justify-center px-4 sm:bottom-6"
          style={{ paddingBottom: "env(safe-area-inset-bottom)" }}
        >
          <form
            action={updateMailboxSenderSpamRuleAction}
            onSubmit={() => {
              setSenderSpamPrompt(null);
              setSenderSpamPromptTargetFolder("inbox");
              setSenderSpamPromptNewFolderName("");
            }}
            className="pointer-events-auto relative w-full max-w-[720px] overflow-hidden rounded-[26px] bg-[rgba(12,18,30,0.92)] px-5 py-5 text-white shadow-[0_24px_60px_rgba(2,6,18,0.38)] ring-1 ring-white/10 backdrop-blur-[22px] backdrop-saturate-150"
          >
            <input type="hidden" name="folder" value={currentMailboxFolder} />
            <input type="hidden" name="detail" value={detailView} />
            <input type="hidden" name="page" value={resolvedCurrentPage} />
            <input type="hidden" name="pageSize" value={resolvedPageSize} />
            <input type="hidden" name="tab" value={resolvedMessageFilter} />
            <input type="hidden" name="field" value={resolvedSearchField} />
            <input type="hidden" name="q" value={resolvedSearchQuery} />
            <input type="hidden" name="activeMessageId" value={activeMessageId ? String(activeMessageId) : ""} />
            <input type="hidden" name="senderAddress" value={senderSpamPrompt.senderAddress} />
            <input type="hidden" name="mode" value="unspam" />
            <input type="hidden" name="targetFolder" value={senderSpamPromptTargetFolder} />
            <input type="hidden" name="newFolderName" value={senderSpamPromptNewFolderName.trim()} />

            <SenderRulePromptFieldset>
              <div className="flex items-start justify-between gap-4">
                <div className="min-w-0">
                  <p className="text-[12px] font-semibold tracking-[0.08em] text-[#7ea8ff]">스팸 해제</p>
                  <p className="mt-2 text-[18px] font-semibold tracking-[-0.03em] text-white">
                    {senderSpamPrompt.senderLabel}
                  </p>
                  <p className="mt-2 text-[13px] leading-6 text-white/72">
                    이 발송자를 자동 스팸 분류 대상에서 제외합니다. 기존 스팸메일함 메일을 받은메일함이나 원하는
                    메일함으로 다시 이동할지 선택하세요.
                  </p>
                </div>
                <button
                  type="button"
                  aria-label="확인 닫기"
                  onClick={() => {
                    setSenderSpamPrompt(null);
                    setSenderSpamPromptTargetFolder("inbox");
                    setSenderSpamPromptNewFolderName("");
                  }}
                  className="inline-flex h-9 w-9 shrink-0 cursor-pointer items-center justify-center rounded-full text-white/52 transition-colors hover:bg-white/8 hover:text-white"
                >
                  <CloseIcon className="h-[16px] w-[16px]" />
                </button>
              </div>

              <div className="mt-4 rounded-[20px] border border-white/10 bg-white/5 p-4">
                <p className="text-[12px] font-semibold text-white/66">기존 스팸 메일 이동 대상</p>
                <div className="mt-3 flex flex-wrap gap-2">
                  {senderSpamRestoreFolderOptions.map((item) => {
                    const selected = senderSpamPromptTargetFolder === item.systemName;
                    return (
                      <button
                        key={item.systemName}
                        type="button"
                        onClick={() => setSenderSpamPromptTargetFolder(item.systemName)}
                        className={`inline-flex cursor-pointer items-center gap-2 rounded-full px-3 py-2 text-[12px] font-medium transition-colors ${
                          selected ? "bg-white text-[#12203a]" : "bg-white/7 text-white/82 hover:bg-white/12"
                        }`}
                      >
                        <span>{item.label}</span>
                        <span className={selected ? "text-[#4b607f]" : "text-white/50"}>
                          {compactCount(item.count)}
                        </span>
                      </button>
                    );
                  })}
                </div>

                <div className="mt-4 rounded-[16px] border border-white/10 bg-[rgba(255,255,255,0.04)] p-3">
                  <div className="flex items-center gap-2">
                    <label htmlFor="sender-spam-restore-folder" className="sr-only">
                      새 메일함
                    </label>
                    <input
                      id="sender-spam-restore-folder"
                      type="text"
                      value={senderSpamPromptNewFolderName}
                      onChange={(event) => setSenderSpamPromptNewFolderName(event.target.value)}
                      placeholder="새 메일함"
                      className="h-10 min-w-0 flex-1 rounded-[12px] border border-white/10 bg-white/92 px-3 text-[13px] text-[#223250] outline-none placeholder:text-[#8fa0ba] focus:border-[#bcd1ff]"
                    />
                    <button
                      type="button"
                      onClick={() => {
                        const nextFolderName = senderSpamPromptNewFolderName.trim();

                        if (!nextFolderName) {
                          setLocalNotice({
                            tone: "error",
                            message: "새 메일함 이름을 입력해주세요."
                          });
                          return;
                        }

                        setSenderSpamPromptTargetFolder("__new__");
                        setSenderSpamPromptNewFolderName(nextFolderName);
                      }}
                      className="inline-flex h-10 shrink-0 cursor-pointer items-center justify-center rounded-[12px] border border-white/10 bg-white/8 px-3 text-[13px] font-semibold text-white/88 transition-colors hover:bg-white/12"
                    >
                      추가
                    </button>
                  </div>
                  {senderSpamPromptTargetFolder === "__new__" && senderSpamPromptNewFolderName.trim() ? (
                    <p className="mt-2 text-[12px] text-white/62">
                      새 메일함 <span className="font-semibold text-white">{senderSpamPromptNewFolderName.trim()}</span>
                      으로 이동합니다.
                    </p>
                  ) : null}
                </div>
              </div>

              <div className="mt-4 grid gap-2 sm:grid-cols-[minmax(0,1fr)_180px]">
                <SenderRulePromptSubmitButton
                  label="선택한 메일함으로 이동"
                  pendingLabel="이동 중..."
                  variant="primary"
                  name="moveExistingMessages"
                  value="1"
                />
                <SenderRulePromptSubmitButton
                  label="기존 메일은 그대로"
                  pendingLabel="해제 중..."
                  variant="secondary"
                  name="moveExistingMessages"
                  value="0"
                />
              </div>
            </SenderRulePromptFieldset>
            <SenderRulePromptPendingOverlay />
          </form>
        </div>
      ) : null}

      {runtimeNotice ? (
        <BottomToast
          actionLabel={localNotice && mailboxUndoAction ? "실행 취소" : undefined}
          actionPending={mailboxUndoPending}
          notice={runtimeNotice}
          onAction={mailboxUndoAction ? () => void undoMailboxAction() : undefined}
          onClose={() => {
            if (localNotice) {
              setLocalNotice(null);
              setMailboxUndoAction(null);
              return;
            }

            if (serverNoticeKey) {
              setDismissedServerNoticeKey(serverNoticeKey);
            }
          }}
        />
      ) : null}

      <div className="flex h-full flex-col transition-[opacity,filter,transform] duration-300">
        <header className="relative shrink-0 bg-white">
          <div className="flex min-h-[60px] flex-wrap items-center gap-3 px-3 py-2 sm:px-4 lg:grid lg:h-[60px] lg:grid-cols-[220px_minmax(0,1fr)_auto] lg:py-0">
            <div className="flex min-w-0 flex-1 items-center">
              <button
                type="button"
                aria-label="메일함 메뉴 열기"
                onClick={() => setMobileSidebarOpen(true)}
                className="inline-flex h-[18px] w-[18px] shrink-0 cursor-pointer items-center justify-center text-[#72829f] transition-colors hover:text-[#2f6df6] lg:hidden"
              >
                <MenuIcon className="h-[18px] w-[18px]" />
              </button>

              <Link
                href={mailConfigured ? "/mail?folder=all" : "/mail?panel=setup&section=domain"}
                prefetch={false}
                onClick={(event) =>
                  handleMailboxNavigationLinkClick(
                    event,
                    mailConfigured ? "/mail?folder=all" : "/mail?panel=setup&section=domain",
                    {
                      intent: mailConfigured ? "mailbox" : "setup"
                    }
                  )
                }
                className="flex min-w-0 items-center rounded-[18px]"
              >
                <div className="flex min-w-0 items-center">
                  <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-[10px] text-[#2f6df6]">
                    <IconBase className="h-6 w-6">
                      <rect x="3.5" y="5.5" width="17" height="13" rx="2.5" />
                      <path d="m4.8 7 7.2 5.6L19.2 7" />
                    </IconBase>
                  </span>
                  <span className="truncate text-[18px] font-extrabold tracking-[-0.07em] text-[#2f6df6]">
                    {t("public.brand")}
                  </span>
                </div>
              </Link>
            </div>

            <div
              className={`${
                mobileSearchExpanded ? "order-3 flex basis-full" : "hidden"
              } lg:order-none lg:flex lg:basis-auto lg:justify-center lg:px-4`}
            >
              <div ref={searchMenuRef} data-mail-search-root="true" className="relative w-full lg:max-w-[640px]">
                <div className="flex h-[44px] w-full items-center gap-3 rounded-full border border-[#edf2fb] bg-[#fbfcff] px-4 text-[14px] text-[#9aa8bf] shadow-[0_18px_45px_rgba(47,109,246,0.05)] transition-all duration-200 hover:border-[#d9e4fb] hover:bg-white hover:shadow-[0_22px_52px_rgba(47,109,246,0.08)] focus-within:border-[#cfe0ff] focus-within:bg-white focus-within:shadow-[0_22px_52px_rgba(47,109,246,0.1)] lg:px-5">
                  <button
                    type="button"
                    aria-label="메일 검색 실행"
                    disabled={!mailConfigured}
                    onClick={() => applySearch("all", searchInput)}
                    className="inline-flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-full text-[#8b98b0] transition-colors hover:bg-[#eef4ff] hover:text-[#2f6df6] disabled:cursor-not-allowed"
                  >
                    <SearchIcon className="h-5 w-5" />
                  </button>
                  {activeSearchBadgeVisible ? (
                    <span className="om-badge om-badge-md om-badge-info shrink-0">
                      {searchFieldLabel(resolvedSearchField)}
                    </span>
                  ) : null}
                  <input
                    ref={searchInputRef}
                    type="text"
                    aria-label={t("mail.search")}
                    value={searchInput}
                    disabled={!mailConfigured}
                    placeholder={!mailConfigured ? t("mail.searchAfterDomain") : t("mail.search")}
                    onFocus={() => {
                      if (mailConfigured) {
                        setSearchOpen(true);
                      }
                    }}
                    onChange={(event) => {
                      setSearchInput(event.target.value);
                      setSearchOpen(true);
                      setSearchHighlightedIndex(-1);
                    }}
                    onKeyDown={(event) => {
                      if (event.key === "ArrowDown") {
                        if (visibleRecentSearches.length + visibleSearchSuggestions.length === 0) {
                          return;
                        }

                        event.preventDefault();
                        setSearchOpen(true);
                        setSearchHighlightedIndex((current) =>
                          current >= visibleRecentSearches.length + visibleSearchSuggestions.length - 1 ? 0 : current + 1
                        );
                        return;
                      }

                      if (event.key === "ArrowUp") {
                        if (visibleRecentSearches.length + visibleSearchSuggestions.length === 0) {
                          return;
                        }

                        event.preventDefault();
                        setSearchOpen(true);
                        setSearchHighlightedIndex((current) =>
                          current <= 0 ? visibleRecentSearches.length + visibleSearchSuggestions.length - 1 : current - 1
                        );
                        return;
                      }

                      if (event.key === "Enter") {
                        event.preventDefault();

                        if (searchHighlightedIndex >= 0 && searchHighlightedIndex < visibleRecentSearches.length) {
                          applySearch("all", visibleRecentSearches[searchHighlightedIndex]);
                          return;
                        }

                        const suggestion = visibleSearchSuggestions[searchHighlightedIndex - visibleRecentSearches.length];
                        if (searchHighlightedIndex >= 0 && suggestion) {
                          applySearchSuggestion(suggestion);
                          return;
                        }

                        applySearch("all", searchInput);
                      }
                    }}
                    className="w-full bg-transparent text-[#344767] outline-none placeholder:text-[#9aa8bf] disabled:cursor-not-allowed disabled:text-[#a7b2c6]"
                  />
                  {searchLoading ? (
                    <span className="shrink-0 text-[12px] font-medium text-[#8a98b2]">검색 중</span>
                  ) : null}
                  {(searchInput.trim() || resolvedSearchQuery) ? (
                    <button
                      type="button"
                      aria-label="검색 지우기"
                      onClick={clearSearch}
                      className="inline-flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-full text-[#8b98b0] transition-colors hover:bg-[#eef4ff] hover:text-[#2f6df6]"
                    >
                      <CloseIcon className="h-[14px] w-[14px]" />
                    </button>
                  ) : null}
                </div>

                {searchDropdownVisible ? (
                  <div className="absolute inset-x-0 top-[calc(100%+10px)] z-40 max-h-[420px] overflow-y-auto rounded-[22px] border border-[#dfe8f8] bg-white py-2 shadow-[0_30px_65px_rgba(18,32,65,0.16)]">
                    {visibleRecentSearches.length > 0 ? (
                      <div>
                        <p className="px-5 pb-1 pt-2 text-[11px] font-semibold text-[#8999b3]">최근 검색</p>
                        {visibleRecentSearches.map((recent, index) => (
                          <button
                            key={`recent:${recent}`}
                            type="button"
                            onMouseDown={(event) => event.preventDefault()}
                            onClick={() => applySearch("all", recent)}
                            className={`flex w-full cursor-pointer items-center gap-3 px-5 py-2.5 text-left transition-colors ${
                              index === searchHighlightedIndex ? "bg-[#f4f8ff]" : "hover:bg-[#f7faff]"
                            }`}
                          >
                            <RecentSearchIcon className="h-[17px] w-[17px] shrink-0 text-[#8b9ab4]" />
                            <span className="min-w-0 truncate text-[13px] font-medium text-[#344767]">{recent}</span>
                          </button>
                        ))}
                      </div>
                    ) : null}
                    {searchInput.trim() ? (
                      <div className={visibleRecentSearches.length > 0 ? "mt-1 border-t border-[#edf2fa] pt-1" : ""}>
                        <p className="px-5 pb-1 pt-2 text-[11px] font-semibold text-[#8999b3]">일치하는 메일</p>
                        {searchLoading || searchInput.trim() !== deferredSearchInput ? (
                          <div className="px-5 py-3 text-[13px] text-[#7b89a2]">검색 중입니다.</div>
                        ) : visibleSearchSuggestions.length > 0 ? (
                          visibleSearchSuggestions.map((suggestion, index) => (
                            <button
                              key={`${suggestion.field}:${suggestion.messageId}:${suggestion.value}:${index}`}
                              type="button"
                              onMouseDown={(event) => event.preventDefault()}
                              onClick={() => applySearchSuggestion(suggestion)}
                              className={`flex w-full cursor-pointer items-start gap-4 px-5 py-3 text-left transition-colors ${
                                index + visibleRecentSearches.length === searchHighlightedIndex
                                  ? "bg-[#f4f8ff]"
                                  : "hover:bg-[#f7faff]"
                              }`}
                            >
                              <span className="mt-0.5 om-badge om-badge-sm om-badge-info shrink-0">
                                {searchFieldLabel(suggestion.field)}
                              </span>
                              <span className="min-w-0 flex-1">
                                <span className="block truncate text-[14px] font-semibold text-[#22314e]">
                                  {suggestion.value}
                                </span>
                                <span className="mt-1 block truncate text-[12px] text-[#70819d]">
                                  {suggestion.preview}
                                </span>
                              </span>
                              <span className="shrink-0 text-[11px] text-[#96a4bc]">
                                {formatInboxTime(suggestion.receivedAt)}
                              </span>
                            </button>
                          ))
                        ) : (
                          <div className="px-5 py-3 text-[13px] text-[#7b89a2]">일치하는 메일이 없습니다.</div>
                        )}
                      </div>
                    ) : null}
                  </div>
                ) : null}
              </div>
            </div>

            <div className="ml-auto flex shrink-0 items-center gap-2 sm:gap-3">
              <button
                type="button"
                aria-label={mobileSearchExpanded ? t("mail.search.close") : t("mail.search.open")}
                onClick={() => {
                  setMobileSearchVisible((current) => {
                    const next = !current;
                    if (next) {
                      window.requestAnimationFrame(() => searchInputRef.current?.focus());
                    }
                    return next;
                  });
                }}
                className={`inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-full transition-colors lg:hidden ${
                  mobileSearchExpanded
                    ? "bg-[#eef4ff] text-[#2f6df6]"
                    : "text-[#72829f] hover:bg-[#f4f7fc] hover:text-[#2f6df6]"
                }`}
              >
                <SearchIcon className="h-[18px] w-[18px]" />
              </button>

              <button
                type="button"
                aria-label={t("mail.windowsDownload")}
                title={t("mail.windowsDownloadShort")}
                onClick={() => {
                  window.location.assign("/api/app/releases/windows/download/latest?channel=stable");
                }}
                className={`hidden lg:inline-flex ${circleIconButtonClass()}`}
              >
                <DownloadIcon className="h-5 w-5" />
              </button>

              <Link
                href={OFFICIAL_MAIL_GUIDE_URL}
                aria-label={t("mail.guide")}
                target="_blank"
                rel="noopener noreferrer"
                className={`hidden lg:inline-flex ${circleIconButtonClass()}`}
              >
                <HelpIcon className="h-5 w-5" />
              </Link>

              {canManageBilling ? (
                <Link
                  href={setupHref("billing")}
                  prefetch={false}
                  className={`group hidden h-9 items-center gap-2 px-1 text-[13px] font-semibold transition-colors duration-200 lg:inline-flex ${
                    planSummary.tier === "growth"
                      ? "text-[#2f6df6] hover:text-[#255ce0]"
                      : "text-[#5f6f89] hover:text-[#2d3d5c]"
                  }`}
                >
                  <span
                    className={`h-2.5 w-2.5 rounded-full ${
                      planSummary.aiAssistAvailable ? "bg-[#2f6df6]" : "bg-[#9aa8bf]"
                    }`}
                  />
                  <span className="whitespace-nowrap">{ui(planSummary.label)}</span>
                  <span
                    className={`hidden md:inline-flex items-center rounded-full border px-2 py-1 text-[12px] font-semibold leading-none ${
                      planSummary.aiAssistAvailable
                        ? "border-[#bfd4ff] text-[#2f6df6]"
                        : "border-[#d8e0ec] text-[#7f8da3]"
                    }`}
                  >
                    {ui(planSummary.badgeLabel)}
                  </span>
                </Link>
              ) : (
                <div
                  className={`hidden h-9 items-center gap-2 px-1 text-[13px] font-semibold lg:inline-flex ${
                    planSummary.tier === "growth" ? "text-[#2f6df6]" : "text-[#5f6f89]"
                  }`}
                >
                  <span
                    className={`h-2.5 w-2.5 rounded-full ${
                      planSummary.aiAssistAvailable ? "bg-[#2f6df6]" : "bg-[#9aa8bf]"
                    }`}
                  />
                  <span className="whitespace-nowrap">{ui(planSummary.label)}</span>
                  <span
                    className={`hidden md:inline-flex items-center rounded-full border px-2 py-1 text-[12px] font-semibold leading-none ${
                      planSummary.aiAssistAvailable
                        ? "border-[#bfd4ff] text-[#2f6df6]"
                        : "border-[#d8e0ec] text-[#7f8da3]"
                    }`}
                  >
                    {ui(planSummary.badgeLabel)}
                  </span>
                </div>
              )}

              <div ref={settingsMenuRef} data-settings-menu-root="true" className="relative">
                <button
                  type="button"
                  aria-expanded={settingsMenuOpen}
                  aria-haspopup="menu"
                  onClick={() =>
                    setSettingsMenuState((current) =>
                      current.open && current.scope === settingsMenuScopeKey
                        ? { open: false, scope: settingsMenuScopeKey }
                        : { open: true, scope: settingsMenuScopeKey }
                    )
                  }
                  className={`inline-flex cursor-pointer items-center gap-3 rounded-full border px-3 py-2 transition-all duration-200 ${
                    settingsMenuOpen || isSetupPanel
                      ? "border-transparent bg-white/78 text-[#2f6df6] backdrop-blur-xl backdrop-saturate-150"
                      : "border-transparent bg-white text-[#62738f] hover:bg-white/70 hover:text-[#2d3d5c] hover:backdrop-blur-xl hover:backdrop-saturate-150"
                  }`}
                >
                  <span className="text-[13px] font-semibold text-[#1f2c45] lg:hidden">설정</span>
                  <div className="hidden min-w-0 text-left lg:block">
                    <p className="truncate text-[15px] font-semibold text-[#1f2c45]">{userDisplayName}</p>
                    <p className="truncate text-[14px] text-[#6981a6]">{compactMailbox}</p>
                  </div>
                  <ChevronDownIcon
                    className={`h-[15px] w-[15px] transition-transform duration-200 ${
                      settingsMenuOpen ? "rotate-180" : ""
                    }`}
                  />
                </button>

                {settingsMenuOpen ? (
                  <div className="absolute right-0 top-[calc(100%+12px)] z-50 w-[284px] overflow-hidden rounded-[22px] border border-[#dfe8f8] bg-white shadow-[0_28px_60px_rgba(18,32,65,0.16)]">
                    <div className="border-b border-[#edf2fb] px-5 py-4">
                      <p className="text-[14px] font-semibold text-[#1f2c45]">{userDisplayName}</p>
                      <p className="mt-1 truncate text-[12px] font-medium text-[#2f6df6]">{compactMailbox}</p>
                    </div>

                    <div className="p-2.5">
                      {settingsMenuItems.map((item) => {
                        const active = item.id !== "logout" && isSetupPanel && setupSection === item.id;

                        if (item.id === "logout") {
                          return (
                            <form key={item.id} action={logoutAction} className="contents">
                              <button type="submit" className={settingsMenuButtonClass()}>
                                {iconForSetupNav(item.id, settingsMenuItemIconClass())}
                                <span className="truncate">{item.label}</span>
                              </button>
                            </form>
                          );
                        }

                        return (
                          <Link
                            key={item.id}
                            href={setupHref(item.id as MailSetupSection)}
                            prefetch={false}
                            onClick={(event) =>
                              handleMailboxNavigationLinkClick(event, setupHref(item.id as MailSetupSection), {
                                intent: "setup"
                              })
                            }
                            className={settingsMenuItemClass(active)}
                          >
                            {iconForSetupNav(item.id, settingsMenuItemIconClass(active))}
                            <span className="truncate">{item.label}</span>
                          </Link>
                        );
                      })}
                    </div>
                  </div>
                ) : null}
              </div>
            </div>
          </div>
          <span
            aria-hidden="true"
            className="pointer-events-none absolute inset-x-0 bottom-0 z-[60] h-px bg-[#e9eef8]"
          />
        </header>

        {!isLgViewport && mobileSidebarOpen ? (
          <button
            type="button"
            aria-label="메일함 메뉴 닫기"
            onClick={() => setMobileSidebarOpen(false)}
            className="fixed inset-0 z-[84] bg-[rgba(15,23,42,0.34)] backdrop-blur-[2px] lg:hidden"
          />
        ) : null}

        <div
          data-mail-layout-root="true"
          data-mail-vertical-split-root={
            isMailboxPanel && isSplitVerticalMode && showSplitDetailPanel ? "true" : undefined
          }
          style={layoutGridStyle}
          className={`relative grid min-h-0 flex-1 grid-cols-1 ${
            isStandaloneContentPanel
              ? "lg:grid-cols-[var(--mail-sidebar-width)_minmax(0,1fr)]"
              : isSplitHorizontalMode && showSplitDetailPanel
                ? "lg:grid-cols-[var(--mail-sidebar-width)_minmax(0,1fr)] xl:grid-cols-[var(--mail-sidebar-width)_var(--mail-list-width)_minmax(0,1fr)]"
                : isSplitVerticalMode && showSplitDetailPanel
                  ? "lg:grid-cols-[var(--mail-sidebar-width)_minmax(0,1fr)] lg:grid-rows-[var(--mail-list-height-clamped)_minmax(0,1fr)]"
                  : "lg:grid-cols-[var(--mail-sidebar-width)_minmax(0,1fr)]"
          }`}
        >
          {showMobileFullContentNavigationOverlay
            ? renderContentNavigationOverlay(
                "absolute inset-0 z-[120] flex items-center justify-center bg-[rgba(252,253,255,0.96)] transition-opacity duration-200 lg:hidden"
              )
            : null}
          <ResizeHandle
            active={dragState?.panel === "sidebar"}
            ariaLabel={t("mail.sidebar.resize")}
            className="hidden lg:block"
            left={layoutState.sidebarWidth}
            onPointerDown={beginResize("sidebar")}
          />
          {isMailboxPanel && isSplitHorizontalMode && showSplitDetailPanel ? (
            <ResizeHandle
              active={dragState?.panel === "list"}
              ariaLabel={t("mail.list.resizeWidth")}
              className="hidden xl:block"
              left={layoutState.sidebarWidth + layoutState.listWidth}
              onPointerDown={beginResize("list")}
            />
          ) : null}
          {isMailboxPanel && isSplitVerticalMode && showSplitDetailPanel ? (
            <HorizontalResizeHandle
              active={dragState?.panel === "list-height"}
              ariaLabel={t("mail.list.resizeHeight")}
              className="hidden lg:block"
              left={layoutState.sidebarWidth}
              onPointerDown={beginResize("list-height")}
              top="var(--mail-list-height-clamped)"
            />
          ) : null}

          <aside
            className={`min-h-0 overflow-visible border-r border-[#e9eef8] bg-white ${
              mobileSidebarOpen
                ? "fixed inset-y-0 left-0 z-[85] flex w-[min(70vw,360px)] flex-col shadow-[0_28px_64px_rgba(15,23,42,0.18)] lg:static lg:z-auto lg:w-auto lg:shadow-none"
                : "hidden lg:flex lg:flex-col"
            } ${isMailboxPanel && isSplitVerticalMode && showSplitDetailPanel ? "lg:row-span-2" : ""}`}
          >
            <div className="flex h-[60px] shrink-0 items-center justify-between border-b border-[#e9eef8] px-4 lg:hidden">
              <div className="flex min-w-0 items-center">
                <LogoMark compact />
                <span className="truncate text-[18px] font-extrabold tracking-[-0.07em] text-[#2f6df6]">
                  {t("public.brand")}
                </span>
              </div>
              <button
                type="button"
                aria-label={t("mail.closeSidebar")}
                onClick={() => setMobileSidebarOpen(false)}
                className="inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-[#72829f] transition-colors hover:bg-[#f4f7fc] hover:text-[#2f6df6]"
              >
                <CloseIcon className="h-[18px] w-[18px]" />
              </button>
            </div>

            <div className="flex min-h-0 flex-1 flex-col px-4 pt-4">
              <Link
                href={composeHref}
                prefetch={false}
                className="inline-flex h-10 shrink-0 items-center justify-center gap-3 rounded-[14px] bg-[linear-gradient(135deg,#2f6df6,#245de3)] px-4 text-[13px] font-semibold leading-none text-white transition-colors hover:bg-[linear-gradient(135deg,#2b64e6,#2057d7)]"
              >
                <ComposeIcon className="h-[18px] w-[18px]" />
                {t("mail.compose")}
              </Link>

              <Link
                href={fileDrawerHref}
                prefetch={false}
                onClick={() => setMobileSidebarOpen(false)}
                className={`mt-2 inline-flex h-10 shrink-0 items-center justify-center gap-3 rounded-[14px] border px-4 text-[13px] font-semibold leading-none transition-colors ${
                  isFileDrawerView
                    ? "border-[#bcd1ff] bg-[#edf4ff] text-[#2f6df6]"
                    : "border-[#dfe7f5] bg-white text-[#405575] hover:border-[#c7d8f7] hover:bg-[#f8faff] hover:text-[#2f6df6]"
                }`}
              >
                <FileDrawerIcon className="h-[18px] w-[18px]" />
                파일서랍
              </Link>

              <div className="mt-5 min-h-0 flex-1 overflow-y-auto pr-1">
                <div className="flex flex-col gap-[4px]">
                  <div className="group mb-0 flex items-center gap-1 rounded-[10px]">
                    <div
                      className={`relative flex min-w-0 flex-1 cursor-pointer items-center rounded-[10px] px-2 py-2 text-[14px] transition-all duration-200 ${
                        showMailboxSidebarActiveState && currentMailboxFolder === "all"
                          ? "bg-[#eaf2ff] text-[#2f6df6] shadow-[inset_0_0_0_1px_rgba(47,109,246,0.04)]"
                          : "text-[#33445f] hover:bg-[#f4f6fa] hover:text-[#2f6df6]"
                      }`}
                      onClick={(event) => {
                        if (shouldBypassClientNavigation(event)) {
                          return;
                        }

                        navigateWithinMailbox(allFolderHref, {
                          intent: allFolderNavigationIntent
                        });
                      }}
                    >
                      <Link
                        href={allFolderHref}
                        prefetch={false}
                        scroll={false}
                        onClick={(event) =>
                          handleMailboxNavigationLinkClick(event, allFolderHref, {
                            intent: allFolderNavigationIntent
                          })
                        }
                        className={`flex min-w-0 flex-1 items-center ${SIDEBAR_MENU_TEXT_GAP_CLASS}`}
                      >
                        {iconForNav("all", SIDEBAR_MENU_ICON_CLASS)}
                        <span className="flex min-w-0 items-center gap-1.5">
                          <span
                            className={`truncate ${
                              showMailboxSidebarActiveState && currentMailboxFolder === "all"
                                ? "font-semibold"
                                : "font-medium"
                            }`}
                          >
                            {t("mail.all")}
                          </span>
                          <span
                            className={`shrink-0 text-[12px] ${
                              showMailboxSidebarActiveState && currentMailboxFolder === "all"
                                ? "font-semibold text-[#2f6df6]"
                                : "text-[#70829f]"
                            }`}
                          >
                            {compactCount(allFolderMessages)}
                          </span>
                        </span>
                      </Link>
                    </div>
                  </div>

                  {primaryFolders.map((item) => renderSidebarFolderItem(item))}

                  <div className="mt-1 border-t border-[#eef2fa] pt-1">
                    <div className="group mb-0 flex items-center gap-1 rounded-[10px]">
                      <div className="relative flex min-w-0 flex-1 items-center rounded-[10px] px-2 py-2 text-[14px] text-[#33445f] transition-all duration-200 hover:bg-[#f4f6fa] hover:text-[#2f6df6]">
                        <button
                          type="button"
                          onClick={() => setPersonalFoldersOpen((current) => !current)}
                          className="flex min-w-0 flex-1 cursor-pointer items-center text-left"
                        >
                          <span className={`flex min-w-0 items-center ${SIDEBAR_MENU_TEXT_GAP_CLASS}`}>
                            <FolderIcon className={SIDEBAR_MENU_ICON_CLASS} />
                            <span className="flex min-w-0 items-center gap-1.5">
                              <span className="truncate font-medium">{t("mail.myMailboxes")}</span>
                              <span className="shrink-0 text-[12px] text-[#70829f]">
                                {compactCount(personalFolders.length)}
                              </span>
                            </span>
                          </span>
                        </button>
                        <div className="ml-2 flex shrink-0 items-center gap-1">
                          <button
                            type="button"
                            aria-label={t("mail.addMailbox")}
                            onClick={() =>
                              openSidebarFolderCreateEditor({
                                insertAtTop: true
                              })
                            }
                            className={`inline-flex h-5 w-5 items-center justify-center rounded-[7px] text-[#8a96ab] transition-all duration-150 ${
                              sidebarFolderEditorState?.mode === "create" && sidebarFolderEditorState.insertAtTop
                                ? "bg-white text-[#556885]"
                                : "pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100 hover:bg-white hover:text-[#2f6df6]"
                            }`}
                          >
                            <PlusIcon className="h-[13px] w-[13px]" />
                          </button>
                          <button
                            type="button"
                            aria-label={personalFoldersExpanded ? t("mail.collapsePersonal") : t("mail.expandPersonal")}
                            onClick={() => setPersonalFoldersOpen((current) => !current)}
                            className="inline-flex h-5 w-5 cursor-pointer items-center justify-center rounded-[7px] text-[#8a96ab] transition-colors hover:bg-white hover:text-[#2f6df6]"
                          >
                            <ChevronDownIcon
                              className={`h-[13px] w-[13px] transition-transform duration-200 ${
                                personalFoldersExpanded ? "rotate-180" : ""
                              }`}
                            />
                          </button>
                        </div>
                      </div>
                    </div>

                    <div
                      className={`overflow-hidden transition-[max-height,opacity] duration-300 ${
                        personalFoldersExpanded ? "max-h-[720px] opacity-100" : "max-h-0 opacity-0"
                      }`}
                    >
                      <div className="mt-1 pl-1">
                        {sidebarFolderEditorState?.mode === "create" && sidebarFolderEditorState.insertAtTop
                          ? renderSidebarFolderInlineEditor()
                          : null}
                        {personalFolders.map((item) => (
                          <Fragment key={item.id}>
                            {sidebarFolderEditorState?.mode === "rename" &&
                            sidebarFolderEditorState.folderId === item.id
                              ? renderSidebarFolderInlineEditor()
                              : renderSidebarFolderItem(item, {
                                  draggable: true
                                })}
                            {sidebarFolderEditorState?.mode === "create" &&
                            sidebarFolderEditorState.insertAfterSystemName === item.systemName
                              ? renderSidebarFolderInlineEditor()
                              : null}
                          </Fragment>
                        ))}
                      </div>
                    </div>
                  </div>

                  {secondarySystemFolders.length > 0 ? (
                    <div className="mt-1 border-t border-[#eef2fa] pt-1">
                      {secondarySystemFolders.map((item) => renderSidebarFolderItem(item))}
                    </div>
                  ) : null}

                  <div className="mt-1">
                    {planSummary.aiAssistAvailable ? (
                      <Link
                        href={autoSendHref()}
                        prefetch={false}
                        onClick={(event) =>
                          handleMailboxNavigationLinkClick(event, autoSendHref(), {
                            intent: "auto-send"
                          })
                        }
                        className={`flex min-h-10 w-full items-center gap-[5px] rounded-[10px] px-2 py-2 text-[14px] font-medium transition-all duration-200 ${
                          isAutoSendPanel
                            ? "bg-[#eaf2ff] text-[#2f6df6] shadow-[inset_0_0_0_1px_rgba(47,109,246,0.04)]"
                            : "text-[#33445f] hover:bg-[#f4f6fa] hover:text-[#2f6df6]"
                        }`}
                      >
                        <ScheduleIcon className={SIDEBAR_MENU_ICON_CLASS} />
                        <span className="min-w-0 flex-1 truncate">{t("mail.autoSend")}</span>
                        <span className="shrink-0 text-[12px] font-semibold text-[#70829f]">{t("mail.open")}</span>
                      </Link>
                    ) : (
                      <div
                        aria-disabled="true"
                        title={t("plan.growthOnly")}
                        className="flex min-h-10 w-full cursor-not-allowed items-center gap-[5px] rounded-[10px] px-2 py-2 text-[14px] font-medium text-[#8f9cb0]"
                      >
                        <ScheduleIcon className="h-[18px] w-[18px] shrink-0 text-[#aab6c9]" />
                        <span className="min-w-0 flex-1 truncate">{t("mail.autoSend")}</span>
                        <span className="ml-auto inline-flex h-7 w-7 items-center justify-center text-[#9aa8bf]">
                          <LockIcon className="h-[18px] w-[18px]" />
                        </span>
                      </div>
                    )}
                  </div>
                </div>
              </div>
            </div>

            <div className="shrink-0 border-t border-[#eef2fa]">
              <div className="space-y-2 px-4 pb-3 pt-3 lg:space-y-4 lg:pb-4 lg:pt-6">
                {reconnectRequired ? (
                  <form
                    action={saveMailboxCredentialsAction}
                    className="rounded-[20px] border border-[#ffe2b4] bg-[#fffaf1] p-4 shadow-[0_12px_32px_rgba(252,193,56,0.08)]"
                  >
                    <input type="hidden" name="folder" value={currentMailboxFolder} />
                    <input type="hidden" name="detail" value={detailView} />
                    <input type="hidden" name="page" value={resolvedCurrentPage} />
                    <input type="hidden" name="pageSize" value={resolvedPageSize} />
                    <input type="hidden" name="tab" value={resolvedMessageFilter} />
                    <input type="hidden" name="field" value={resolvedSearchField} />
                    <input type="hidden" name="q" value={resolvedSearchQuery} />
                    <input type="hidden" name="message" value={activeMessageId ? String(activeMessageId) : ""} />
                    <p className="text-[13px] font-semibold text-[#9a5600]">{t("mail.reconnect.title")}</p>
                    <p className="mt-2 text-[13px] leading-6 text-[#856f50]">{t("mail.reconnect.description")}</p>
                    <input
                      required
                      name="mailboxPassword"
                      type="password"
                      autoComplete="current-password"
                      placeholder={t("mail.reconnect.password")}
                      className="mt-3 h-10 w-full rounded-[12px] border border-[#f4cf8a] bg-white px-3 text-[13px] outline-none placeholder:text-[#b9a483]"
                    />
                    <SubmitButton
                      label={t("mail.reconnect.save")}
                      pendingLabel={t("mail.reconnect.connecting")}
                      className="mt-3 inline-flex h-10 w-full items-center justify-center rounded-[12px] bg-[#f4b545] text-[13px] font-semibold text-[#5a3b00] transition-colors hover:bg-[#ecab34]"
                    />
                  </form>
                ) : null}

                <Link
                  href={setupHref("storage")}
                  prefetch={false}
                  onClick={(event) => handleMailboxNavigationLinkClick(event, setupHref("storage"), { intent: "setup" })}
                  className="group block cursor-pointer border-t border-[#edf2fb] px-0 pt-2.5 outline-none lg:pt-4"
                  aria-label="메일함 용량 관리 열기"
                >
                  <div className="flex items-center justify-between">
                    <span className="text-[12px] font-semibold text-[#243555] transition-colors group-hover:text-[#2f6df6] group-focus-visible:text-[#2f6df6] lg:text-[14px]">{t("mail.usage")}</span>
                    <span className="text-[12px] text-[#5a6f95] transition-colors group-hover:text-[#2f6df6] lg:text-[14px]">{usageSummary}</span>
                  </div>
                  <div className="relative mt-2 h-2 overflow-hidden rounded-full bg-[linear-gradient(180deg,#f7faff,#eaf0fb)] shadow-[inset_0_1px_0_rgba(255,255,255,0.92),inset_0_-1px_0_rgba(148,163,184,0.08)] lg:mt-4 lg:h-4">
                    <div
                      className="relative h-full rounded-full transition-[width] duration-300 ease-out"
                      style={{
                        width: `${usageProgressValue}%`,
                        ...usageProgressFillStyle
                      }}
                    >
                      <div className="pointer-events-none absolute inset-0 bg-[linear-gradient(180deg,rgba(255,255,255,0.34),rgba(255,255,255,0))]" />
                    </div>
                    <span
                      className={`absolute inset-0 flex items-center justify-center text-[11px] font-semibold ${
                        usagePercent !== null && usagePercent >= 50 ? "text-white" : "text-[#42536f]"
                      } hidden lg:flex`}
                    >
                      {usagePercent === null ? "집계 중" : `${usagePercent}%`}
                    </span>
                  </div>
                </Link>
              </div>
            </div>
          </aside>

          {isSetupPanel ? (
            <section className="relative h-full min-h-0 min-w-0 overflow-hidden bg-white lg:col-span-1">
              <MailSetupWorkspace
                section={setupSection}
                dkimRegenerated={dkimRegenerated}
                overview={setupOverview}
                aiAssistOverview={aiAssistOverview}
                billingOverview={billingOverview}
                billingProvider={billingProvider}
                usesTossPayments={usesTossPayments}
                polarBillingOverview={polarBillingOverview}
                billingCancellationOverview={billingCancellationOverview}
                billingHistoryPage={billingHistoryPage}
                billingHistoryState={billingHistoryState}
                billingTab={billingTab}
                managedMembers={managedMembers}
                memberMailboxView={memberMailboxView}
                planSummary={planSummary}
                canManageBilling={canManageBilling}
                domainSetupAssistanceAvailable={domainSetupAssistanceAvailable}
                sessionCompanyName={companyName}
                sessionDisplayName={userDisplayName}
                sessionDomain={sessionDomain}
                sessionEmail={userEmail}
                sessionNotificationEmail={userNotificationEmail}
                sessionRecoveryEmail={userRecoveryEmail}
                sessionRecoveryEmailVerified={userRecoveryEmailVerified}
                sessionMailbox={compactMailbox}
                signatureSettings={signatureSettings}
              />
              {showEmbeddedContentNavigationOverlay ? renderContentNavigationOverlay() : null}
            </section>
          ) : isAutoSendPanel ? (
            <section className="relative h-full min-h-0 min-w-0 overflow-y-auto bg-white lg:col-span-1">
              <div className="h-full w-full">
                <MailAutoSendPanel
                  overview={autoSendOverview}
                  planSummary={planSummary}
                  canManageBilling={canManageBilling}
                />
              </div>
              {showEmbeddedContentNavigationOverlay ? renderContentNavigationOverlay() : null}
            </section>
          ) : (
            <>
              <section
                className={`relative min-h-0 bg-white ${isSplitHorizontalMode && showSplitDetailPanel ? "border-r border-[#e9eef8]" : ""}`}
              >
                {currentView === "compose" ? (
                  <div className="relative h-full min-h-0">
                    <MailComposeForm
                      growth={planSummary.tier === "growth"}
                      key={composeFormKey}
                      mailboxAddress={compactMailbox}
                      composeMode={composeMode}
                      sourceDraftMessageId={sourceDraftMessageId}
                      draftAttachments={composeMode === "draft" ? (mailData?.selectedMessage?.attachments ?? []) : []}
                      currentFolder={currentMailboxFolder}
                      defaultBcc={composeBccDefault}
                      defaultBody={composeBodyDefault}
                      defaultBodyHtml={composeBodyHtmlDefault}
                      signatureHtml={composeSignatureHtml}
                      signatureText={composeSignatureText}
                      defaultCc={composeCcDefault}
                      defaultSubject={composeSubjectDefault}
                      defaultTo={composeToDefault}
                    />
                    {showEmbeddedContentNavigationOverlay ? renderContentNavigationOverlay() : null}
                  </div>
                ) : currentView === "files" ? (
                  <div className="relative h-full min-h-0">
                    <MailFileDrawer />
                    {showEmbeddedContentNavigationOverlay ? renderContentNavigationOverlay() : null}
                  </div>
                ) : (
                  <div className="flex h-full min-h-0 flex-col bg-white">
                    {showStandaloneMessageView ? (
                      <div className="relative flex h-full min-h-0 flex-col">
                        {renderMessageDetailContent({ standalone: true })}
                        {showEmbeddedContentNavigationOverlay ? renderContentNavigationOverlay() : null}
                      </div>
                    ) : (
                      <>
                        <div className="border-b border-[#edf1f6] px-3 py-3 sm:px-4 lg:px-8">
                          <div className="flex flex-wrap items-center gap-x-2 gap-y-2 text-[15px] leading-none sm:gap-x-3 sm:text-[16px]">
                            <h2 className="text-[16px] font-semibold tracking-[-0.03em] text-[#111a33] sm:text-[17px]">
                              {currentFolderLabel}
                            </h2>
                            <span className="font-semibold text-[#2f6df6]">{formatCount(totalMessages)}</span>
                            <span className="text-[#7c879b]">/ {formatCount(currentFolderTotalCount)}</span>
                            {renderFolderSummaryTrailingAction()}
                            {selectedIds.length > 0 ? (
                              <span className="text-[#8a94a8]">선택 {formatCount(selectedIds.length)}</span>
                            ) : null}
                          </div>
                        </div>

                        <div className="relative flex min-h-0 flex-1 flex-col">
                          <div className="border-b border-[#edf1f6] px-3 py-3 sm:px-4 lg:px-8">
                            <div className="flex flex-col gap-3 text-[14px] text-[#727f95] lg:flex-row lg:items-center lg:justify-between">
                              <DraggableHorizontalScroll
                                className="-mx-1 overflow-x-auto pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
                                innerClassName="flex min-w-max items-center gap-x-4 gap-y-2 px-1"
                              >
                                <button
                                  type="button"
                                  onClick={toggleSelectAll}
                                  className="inline-flex h-7 cursor-pointer items-center gap-2 rounded-[10px] pr-1 text-[#5e6b81] transition-colors hover:text-[#2f6df6]"
                                >
                                  <input
                                    type="checkbox"
                                    checked={allVisibleSelected}
                                    readOnly
                                    aria-label="전체 선택"
                                    className={mailboxCheckboxClass()}
                                  />
                                  <ChevronDownIcon className="h-[11px] w-[11px]" />
                                </button>

                                {hasCheckedMessages ? (
                                  <form
                                    action={markMailboxMessagesReadAction}
                                    onSubmit={() => {
                                      markMessagesReadOptimistically(
                                        checkedMessageIds,
                                        bulkReadToggleValue !== "unread"
                                      );
                                    }}
                                  >
                                    <input type="hidden" name="folder" value={currentMailboxFolder} />
                                    <input type="hidden" name="detail" value={detailView} />
                                    <input type="hidden" name="page" value={resolvedCurrentPage} />
                                    <input type="hidden" name="pageSize" value={resolvedPageSize} />
                                    <input type="hidden" name="tab" value={resolvedMessageFilter} />
                                    <input type="hidden" name="field" value={resolvedSearchField} />
                                    <input type="hidden" name="q" value={resolvedSearchQuery} />
                                    <input type="hidden" name="messageIds" value={checkedMessageIdsValue} />
                                    <input type="hidden" name="readState" value={bulkReadToggleValue} />
                                    <button type="submit" className={mailboxBulkActionClass()}>
                                      {bulkReadToggleLabel}
                                    </button>
                                  </form>
                                ) : (
                                  <span
                                    className={mailboxBulkActionClass({
                                      enabled: false
                                    })}
                                  >
                                    {bulkReadToggleLabel}
                                  </span>
                                )}

                                {currentMailboxFolder === "trash" ? (
                                  hasCheckedMessages ? (
                                    <button
                                      type="button"
                                      disabled={pendingMoveKey !== null}
                                      onClick={() => void moveMessagesByClient(checkedMessageIds, "inbox")}
                                      className={mailboxBulkActionClass({
                                        enabled: pendingMoveKey === null
                                      })}
                                    >
                                      복구
                                    </button>
                                  ) : (
                                    <span
                                      className={mailboxBulkActionClass({
                                        enabled: false
                                      })}
                                    >
                                      복구
                                    </span>
                                  )
                                ) : null}

                                {hasCheckedMessages ? (
                                  <button
                                    type="button"
                                    disabled={pendingMoveKey !== null}
                                    onClick={() => void moveMessagesByClient(checkedMessageIds, "trash")}
                                    className={mailboxBulkActionClass({
                                      enabled: pendingMoveKey === null
                                    })}
                                  >
                                    {pendingMoveKey ? (
                                      <span className="inline-flex items-center gap-1.5">
                                        <SpinnerIcon className="h-[13px] w-[13px] animate-spin" />
                                        {tr("삭제")}
                                      </span>
                                    ) : (
                                      tr("삭제")
                                    )}
                                  </button>
                                ) : (
                                  <span
                                    className={mailboxBulkActionClass({
                                      enabled: false
                                    })}
                                  >
                                    {tr("삭제")}
                                  </span>
                                )}

                                {hasCheckedMessages ? (
                                  <button
                                    type="button"
                                    disabled={pendingMoveKey !== null}
                                    onClick={() =>
                                      void moveMessagesByClient(
                                        checkedMessageIds,
                                        currentMailboxFolder === "spam" ? "inbox" : "spam",
                                        {
                                          releaseSpam: currentMailboxFolder === "spam"
                                        }
                                      )
                                    }
                                    className={mailboxBulkActionClass({
                                      enabled: pendingMoveKey === null
                                    })}
                                  >
                                    {currentMailboxFolder === "spam" ? tr("스팸 해제") : tr("스팸신고")}
                                  </button>
                                ) : (
                                  <span
                                    className={mailboxBulkActionClass({
                                      enabled: false
                                    })}
                                  >
                                    {currentMailboxFolder === "spam" ? tr("스팸 해제") : tr("스팸신고")}
                                  </span>
                                )}

                                {replyHref ? (
                                  <Link href={replyHref} prefetch={false} className={mailboxBulkActionClass()}>
                                    {tr("답장")}
                                  </Link>
                                ) : (
                                  <span
                                    className={mailboxBulkActionClass({
                                      enabled: false
                                    })}
                                  >
                                    {tr("답장")}
                                  </span>
                                )}

                                {replyAllHref ? (
                                  <Link href={replyAllHref} prefetch={false} className={mailboxBulkActionClass()}>
                                    {tr("전체답장")}
                                  </Link>
                                ) : (
                                  <span
                                    className={mailboxBulkActionClass({
                                      enabled: false
                                    })}
                                  >
                                    {tr("전체답장")}
                                  </span>
                                )}

                                {forwardHref ? (
                                  <Link href={forwardHref} prefetch={false} className={mailboxBulkActionClass()}>
                                    {tr("전달")}
                                  </Link>
                                ) : (
                                  <span
                                    className={mailboxBulkActionClass({
                                      enabled: false
                                    })}
                                  >
                                    {tr("전달")}
                                  </span>
                                )}

                                {hasCheckedMessages ? (
                                  <div className="relative inline-flex" data-move-menu-root="true">
                                    <button
                                      type="button"
                                      onClick={(event) => {
                                        setMoveMenuAnchor(event.currentTarget);
                                        setMoveTargetFolder(
                                          (currentMailboxFolder === "all" ? "inbox" : currentMailboxFolder) || "inbox"
                                        );
                                        setMoveMenuState((prev) => ({
                                          open: !(prev.scope === interactionScopeKey && prev.open),
                                          scope: interactionScopeKey
                                        }));
                                      }}
                                      className={mailboxBulkActionClass({
                                        gapClassName: "gap-1"
                                      })}
                                    >
                                      {tr("이동")}
                                      <ChevronDownIcon className="h-[11px] w-[11px]" />
                                    </button>
                                    {moveMenuOpen ? renderMoveMenuPopover(checkedMessageIdsValue) : null}
                                  </div>
                                ) : (
                                  <span
                                    className={mailboxBulkActionClass({
                                      enabled: false,
                                      gapClassName: "gap-1"
                                    })}
                                  >
                                    {tr("이동")}
                                    <ChevronDownIcon className="h-[11px] w-[11px]" />
                                  </span>
                                )}

                                <div className="relative inline-flex" data-toolbar-menu-root="true">
                                  <button
                                    ref={filterMenuButtonRef}
                                    type="button"
                                    onClick={() => setToolbarMenuKind(filterMenuOpen ? null : "filter")}
                                    className={toolbarMenuButtonClass(filterMenuOpen)}
                                  >
                                    <FilterIcon className="h-[15px] w-[15px]" />
                                    {tr("필터")}
                                    <ChevronDownIcon
                                      className={`h-[11px] w-[11px] transition-transform duration-200 ${
                                        filterMenuOpen ? "rotate-180" : ""
                                      }`}
                                    />
                                  </button>

                                  <ToolbarPortalMenu
                                    anchorRef={filterMenuButtonRef}
                                    className={`z-[9999] w-max min-w-[188px] ${popoverMenuSurfaceClass()}`}
                                    open={filterMenuOpen}
                                  >
                                    {FILTER_OPTIONS.map((option) => {
                                      const selected = resolvedMessageFilter === option.id;
                                      return (
                                        <button
                                          key={option.id}
                                          type="button"
                                          onClick={() => {
                                            setToolbarMenuKind(null);
                                            setSelectionState({
                                              ids: [],
                                              scope: interactionScopeKey
                                            });
                                            setMoveMenuState({
                                              open: false,
                                              scope: interactionScopeKey
                                            });
                                            router.push(
                                              folderHref(currentMailboxFolder, {
                                                page: 1,
                                                pageSize: resolvedPageSize,
                                                searchField: resolvedSearchField,
                                                searchQuery: resolvedSearchQuery,
                                                sortOrder: currentSortOrder,
                                                tab: option.id
                                              }),
                                              { scroll: false }
                                            );
                                          }}
                                          className={popoverMenuItemClass({
                                            active: selected
                                          })}
                                        >
                                          {tr(option.label)}
                                          {selected ? <span className="sr-only">{tr("선택됨")}</span> : null}
                                        </button>
                                      );
                                    })}
                                  </ToolbarPortalMenu>
                                </div>
                              </DraggableHorizontalScroll>

                              <div className="flex items-center justify-end gap-2">
                                <button
                                  type="button"
                                  onClick={() => {
                                    void syncMailbox();
                                  }}
                                  aria-label={tr("메일함 동기화")}
                                  className="inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-full text-[#7d8798] transition-colors hover:bg-[#f4f7fc] hover:text-[#2f6df6]"
                                >
                                  <RefreshIcon className="h-[15px] w-[15px]" />
                                </button>

                                <div className="relative inline-flex" data-toolbar-menu-root="true">
                                  <button
                                    ref={sortMenuButtonRef}
                                    type="button"
                                    aria-label={tr("메일 정렬")}
                                    aria-expanded={sortMenuOpen}
                                    aria-haspopup="menu"
                                    onClick={() => setToolbarMenuKind(sortMenuOpen ? null : "sort")}
                                    className={toolbarTextMenuButtonClass(sortMenuOpen)}
                                  >
                                    <span>{tr(currentSortOrder === "oldest" ? "오래된순" : "최신순")}</span>
                                    <ChevronDownIcon
                                      className={`h-[11px] w-[11px] transition-transform duration-200 ${
                                        sortMenuOpen ? "rotate-180" : ""
                                      }`}
                                    />
                                  </button>

                                  <ToolbarPortalMenu
                                    align="right"
                                    anchorRef={sortMenuButtonRef}
                                    className={`z-[9999] w-max min-w-[188px] ${popoverMenuSurfaceClass()}`}
                                    open={sortMenuOpen}
                                  >
                                    {MAIL_SORT_OPTIONS.map((option) => {
                                      const selected = currentSortOrder === option.value;
                                      return (
                                        <button
                                          key={option.value}
                                          type="button"
                                          onClick={() => {
                                            if (selected) {
                                              setToolbarMenuKind(null);
                                              return;
                                            }

                                            void applySortOrder(option.value);
                                          }}
                                          className={viewModeMenuItemClass(selected)}
                                        >
                                          <span>{tr(option.label)}</span>
                                          {selected ? (
                                            <>
                                              <span className="ml-auto inline-flex items-center text-[#2f6df6]">
                                                <MenuCheckIcon className="h-[14px] w-[14px]" />
                                              </span>
                                              <span className="sr-only">{tr("선택됨")}</span>
                                            </>
                                          ) : null}
                                        </button>
                                      );
                                    })}
                                  </ToolbarPortalMenu>
                                </div>

                                <div className="relative inline-flex" data-toolbar-menu-root="true">
                                  <button
                                    ref={pageSizeMenuButtonRef}
                                    type="button"
                                    aria-label="페이지당 메일 표시 개수"
                                    aria-expanded={pageSizeMenuOpen}
                                    aria-haspopup="menu"
                                    onClick={() => setToolbarMenuKind(pageSizeMenuOpen ? null : "pageSize")}
                                    className={toolbarTextMenuButtonClass(pageSizeMenuOpen)}
                                  >
                                    <span>{tr(`${resolvedPageSize}개씩 보기`)}</span>
                                    <ChevronDownIcon
                                      className={`h-[11px] w-[11px] transition-transform duration-200 ${
                                        pageSizeMenuOpen ? "rotate-180" : ""
                                      }`}
                                    />
                                  </button>

                                  <ToolbarPortalMenu
                                    align="right"
                                    anchorRef={pageSizeMenuButtonRef}
                                    className={`z-[9999] w-max min-w-[188px] ${popoverMenuSurfaceClass()}`}
                                    open={pageSizeMenuOpen}
                                  >
                                    {MAILBOX_PAGE_SIZE_OPTIONS.map((pageSize) => {
                                      const selected = resolvedPageSize === pageSize;
                                      return (
                                        <button
                                          key={pageSize}
                                          type="button"
                                          onClick={() => {
                                            if (selected) {
                                              setToolbarMenuKind(null);
                                              return;
                                            }

                                            void applyPageSize(pageSize);
                                          }}
                                          className={viewModeMenuItemClass(selected)}
                                        >
                                          <span>{tr(`${pageSize}개씩 보기`)}</span>
                                          {selected ? (
                                            <>
                                              <span className="ml-auto inline-flex items-center text-[#2f6df6]">
                                                <MenuCheckIcon className="h-[14px] w-[14px]" />
                                              </span>
                                              <span className="sr-only">{tr("선택됨")}</span>
                                            </>
                                          ) : null}
                                        </button>
                                      );
                                    })}
                                  </ToolbarPortalMenu>
                                </div>

                                <div className="relative inline-flex" data-toolbar-menu-root="true">
                                  <button
                                    ref={viewMenuButtonRef}
                                    type="button"
                                    onClick={() => setToolbarMenuKind(viewMenuOpen ? null : "view")}
                                    className={toolbarMenuButtonClass(viewMenuOpen, {
                                      mobileIconOnly: true
                                    })}
                                    aria-label={tr("보기방식")}
                                  >
                                    {iconForViewMode(currentViewMode, "h-[15px] w-[15px]")}
                                    <span className="hidden whitespace-nowrap xl:inline">{tr("보기방식")}</span>
                                    <ChevronDownIcon
                                      className={`hidden h-[11px] w-[11px] transition-transform duration-200 xl:block ${
                                        viewMenuOpen ? "rotate-180" : ""
                                      }`}
                                    />
                                  </button>

                                  <ToolbarPortalMenu
                                    align="right"
                                    anchorRef={viewMenuButtonRef}
                                    className={`z-[9999] w-max min-w-[188px] ${popoverMenuSurfaceClass()}`}
                                    open={viewMenuOpen}
                                  >
                                    {VIEW_MODE_OPTIONS.map((mode) => {
                                      const selected = currentViewMode === mode;
                                      return (
                                        <button
                                          key={mode}
                                          type="button"
                                          onClick={() => applyViewMode(mode)}
                                          className={viewModeMenuItemClass(selected)}
                                        >
                                          {iconForViewMode(mode, "h-[15px] w-[15px]")}
                                          <span>{tr(VIEW_MODE_LABEL[mode])}</span>
                                          {selected ? (
                                            <>
                                              <span className="ml-auto inline-flex items-center text-[#2f6df6]">
                                                <svg
                                                  viewBox="0 0 24 24"
                                                  fill="none"
                                                  stroke="currentColor"
                                                  strokeWidth="2"
                                                  strokeLinecap="round"
                                                  strokeLinejoin="round"
                                                  className="h-[14px] w-[14px]"
                                                >
                                                  <path d="m5 12 4.2 4.2L19 6.7" />
                                                </svg>
                                              </span>
                                              <span className="sr-only">선택됨</span>
                                            </>
                                          ) : null}
                                        </button>
                                      );
                                    })}
                                  </ToolbarPortalMenu>
                                </div>
                              </div>
                            </div>
                          </div>

                          <div className="min-h-0 flex-1 overflow-y-auto">
                            {filteredMessages.length > 0 ? (
                              <div className="divide-y divide-[#edf1f6]">
                                {filteredMessages.map((message) => {
                                  const active = activeMessageId === message.id;
                                  const checked = selectedIds.includes(message.id);
                                  const unread = !message.isRead;
                                  const showAttachment = looksLikeAttachment(message);
                                  const important = message.isStarred;
                                  const sentMessage = message.direction === "outbound";
                                  const recipient = primaryRecipient(message.toAddresses);
                                  const messageContactLabel = sentMessage
                                    ? recipient.label
                                    : message.senderContactName || senderLabel(message.fromName, message.fromAddress);
                                  const rowFolderLabel =
                                    currentMailboxFolder === "all"
                                      ? folderLabelForHeader(message.folderSystemName, message.folderName, t)
                                      : "";
                                  const aiStatusLabel = aiSummaryStatusLabel(message);
                                  const handleRowClick = (event: ReactMouseEvent<HTMLDivElement>) => {
                                    if (shouldPreventMailRowOpen(event.target)) {
                                      return;
                                    }

                                    openMessage(message);
                                  };
                                  const renderSenderCell = (compactLayout: boolean) => (
                                    <div
                                      className={`relative min-w-0 ${compactLayout ? "flex-1" : "pr-4"}`}
                                      data-row-action-stop="true"
                                      data-sender-menu-root="true"
                                    >
                                      <button
                                        type="button"
                                        onClick={(event) => {
                                          setDetailSenderMenuAnchor(null);
                                          setSenderMenuAnchor(event.currentTarget);
                                          setSenderMenuState((prev) => ({
                                            messageId:
                                              prev.scope === interactionScopeKey && prev.messageId === message.id
                                                ? null
                                                : message.id,
                                            scope: interactionScopeKey
                                          }));
                                        }}
                                        onContextMenu={(event) => {
                                          event.preventDefault();
                                          setDetailSenderMenuAnchor(null);
                                          setSenderMenuAnchor(event.currentTarget);
                                          setSenderMenuState({
                                            messageId: message.id,
                                            scope: interactionScopeKey
                                          });
                                        }}
                                        className={`block w-full min-w-0 truncate cursor-pointer text-left transition-all hover:text-[#2f6df6] hover:underline ${
                                          message.senderIsVip
                                            ? "font-bold text-[#2f6df6]"
                                            : unread
                                              ? "font-bold text-[#111a33]"
                                              : "font-normal text-[#2f3d54]"
                                        }`}
                                      >
                                        {messageContactLabel}
                                      </button>

                                    </div>
                                  );
                                  const messageContentCell = (
                                    <div className="flex min-w-0 items-center gap-2 text-left">
                                      <div className="min-w-0 truncate text-[12px] text-[#4b576b]">
                                        {rowFolderLabel ? (
                                          <span className="mr-1 text-[#7a879c]">[{rowFolderLabel}]</span>
                                        ) : null}
                                        <span
                                          className={`mr-2 ${
                                            unread ? "font-bold text-[#16233c]" : "font-normal text-[#1f2c45]"
                                          }`}
                                        >
                                          {message.subject.trim() || "(제목 없음)"}
                                        </span>
                                        {message.snippet.trim() ? (
                                          <span className="text-[#6f7c92]">{message.snippet.trim()}</span>
                                        ) : null}
                                      </div>
                                      <div
                                        className="flex shrink-0 items-center gap-1 text-[#9aa5b7]"
                                        data-row-action-stop="true"
                                      >
                                        {message.aiSummaryStatus === "failed" ? (
                                          <span
                                            className="inline-flex h-[15px] w-[15px] items-center justify-center text-[#e5484d]"
                                            title={aiStatusLabel}
                                            aria-label={aiStatusLabel}
                                          >
                                            <AlertIcon className="h-[14px] w-[14px]" />
                                          </span>
                                        ) : null}
                                        {message.aiSummaryStatus === "sending" ? (
                                          <span
                                            className="inline-flex h-[15px] w-[15px] items-center justify-center text-[#2f6df6]"
                                            title={aiStatusLabel}
                                            aria-label={aiStatusLabel}
                                          >
                                            <SpinnerIcon className="h-[13px] w-[13px] animate-spin" />
                                          </span>
                                        ) : null}
                                        {showAttachment ? <PaperclipIcon className="h-[13px] w-[13px]" /> : null}
                                        <button
                                          type="button"
                                          onClick={(event) => {
                                            event.preventDefault();
                                            event.stopPropagation();
                                            openMessagePopup(message.id);
                                          }}
                                          className="inline-flex cursor-pointer items-center justify-center rounded-[8px] transition-colors hover:text-[#2f6df6]"
                                          aria-label="새 창으로 열기"
                                        >
                                          <ExternalIcon className="h-[13px] w-[13px]" />
                                        </button>
                                      </div>
                                    </div>
                                  );
                                  const renderMessageTimeCell = (className: string) => (
                                    <div
                                      className={`${className} ${
                                        unread ? "font-semibold text-[#23324b]" : "font-normal text-[#7c8799]"
                                      }`}
                                    >
                                      {formatInboxTime(message.receivedAt)}
                                    </div>
                                  );

                                  return (
                                    <div
                                      key={message.id}
                                      ref={(node) => {
                                        if (node) {
                                          messageRowRefs.current.set(message.id, node);
                                        } else {
                                          messageRowRefs.current.delete(message.id);
                                        }
                                      }}
                                      draggable
                                      onDragStart={beginMessageDrag(message.id)}
                                      onDragEnd={finishMessageDrag}
                                      onClick={handleRowClick}
                                      className={`grid cursor-pointer px-3 text-[12px] leading-[1.25] transition-colors min-h-0 grid-cols-[15px_32px_32px_minmax(0,1fr)] items-start py-1.5 sm:px-4 lg:min-h-[39px] lg:grid-cols-[15px_32px_32px_154px_minmax(0,1fr)_94px] lg:items-center lg:px-8 lg:py-0 ${
                                        checked || draggingMessageIdSet.has(message.id)
                                          ? "bg-[#edf4ff]"
                                          : active
                                            ? "bg-[#f5f9ff]"
                                            : "hover:bg-[#fafcff]"
                                      }`}
                                    >
                                      <div className="flex items-center justify-center" data-row-action-stop="true">
                                        <input
                                          type="checkbox"
                                          checked={checked}
                                          onChange={() => toggleSelection(message.id)}
                                          className={mailboxCheckboxClass()}
                                        />
                                      </div>

                                      <div className="flex items-center justify-center" data-row-action-stop="true">
                                        <button
                                          type="button"
                                          onClick={() => {
                                            void toggleMessageStarByClient(message.id);
                                          }}
                                          disabled={pendingStarMessageIds.includes(message.id)}
                                          className={`inline-flex h-[17px] w-[17px] items-center justify-center transition-colors ${
                                            pendingStarMessageIds.includes(message.id)
                                              ? "cursor-wait opacity-60"
                                              : "cursor-pointer"
                                          } ${
                                            important
                                              ? "text-[#5b88ff] hover:text-[#2f6df6]"
                                              : "text-[#c4cddd] hover:text-[#89a7ef]"
                                          }`}
                                          aria-label={important ? "중요 표시 해제" : "중요 표시"}
                                        >
                                          <StarIcon filled={important} className="h-[17px] w-[17px]" />
                                        </button>
                                      </div>

                                      <button
                                        type="button"
                                        onClick={() => openComposeToAddress(message.fromAddress)}
                                        data-row-action-stop="true"
                                        className="flex cursor-pointer items-center justify-center rounded-[8px] text-left transition-colors hover:text-[#2f6df6]"
                                        aria-label={`${message.fromAddress} 주소로 메일 쓰기`}
                                      >
                                        <MailRowIcon
                                          unread={unread}
                                          className={`h-[18px] w-[18px] ${
                                            unread ? "text-[#8ec0ff]" : "text-[#c5cfdf]"
                                          }`}
                                        />
                                      </button>

                                      <>
                                        <div className="min-w-0 lg:hidden">
                                          <div className="flex items-start justify-between gap-3">
                                            {renderSenderCell(true)}
                                            {renderMessageTimeCell("shrink-0 whitespace-nowrap pt-px text-right")}
                                          </div>
                                          <div className="mt-1.5">{messageContentCell}</div>
                                        </div>
                                        <div className="hidden min-w-0 lg:block">{renderSenderCell(false)}</div>
                                        <div className="hidden py-[11px] lg:block">{messageContentCell}</div>
                                        {renderMessageTimeCell(
                                          "hidden whitespace-nowrap py-[11px] pr-3 text-right lg:block"
                                        )}
                                      </>
                                    </div>
                                  );
                                })}
                              </div>
                            ) : (
                              <div className="flex h-full items-center justify-center px-8 text-[13px] text-[#7f8da7]">
                                표시할 메일이 없습니다.
                              </div>
                            )}
                          </div>

                          <div className="flex items-center justify-center gap-2 border-t border-[#edf1f6] px-6 py-4 text-[12px] text-[#72829f]">
                            {resolvedCurrentPage > 1 ? (
                              <Link
                                href={pageHref(currentMailboxFolder, resolvedCurrentPage - 1, resolvedMessageFilter, {
                                  pageSize: resolvedPageSize,
                                  searchField: resolvedSearchField,
                                  searchQuery: resolvedSearchQuery,
                                  sortOrder: currentSortOrder
                                })}
                                prefetch={false}
                                scroll={false}
                                className="inline-flex h-8 min-w-8 items-center justify-center rounded-full transition-colors hover:bg-[#f4f7fc] hover:text-[#2f6df6]"
                              >
                                ‹
                              </Link>
                            ) : (
                              <span className="inline-flex h-8 min-w-8 items-center justify-center rounded-full text-[#c0cadb]">
                                ‹
                              </span>
                            )}

                            {paginationItems.map((item, index) =>
                              item === "ellipsis" ? (
                                <span key={`ellipsis-${index}`} className="px-1 text-[#a1aec5]">
                                  …
                                </span>
                              ) : item === resolvedCurrentPage ? (
                                <span
                                  key={item}
                                  className="inline-flex h-8 min-w-8 items-center justify-center rounded-[9px] border border-[#b9d0ff] bg-[#ffffff] px-2 font-semibold text-[#2f6df6]"
                                >
                                  {item}
                                </span>
                              ) : (
                                <Link
                                  key={item}
                                  href={pageHref(currentMailboxFolder, item, resolvedMessageFilter, {
                                    pageSize: resolvedPageSize,
                                    searchField: resolvedSearchField,
                                    searchQuery: resolvedSearchQuery,
                                    sortOrder: currentSortOrder
                                  })}
                                  prefetch={false}
                                  scroll={false}
                                  className="inline-flex h-8 min-w-8 items-center justify-center rounded-[9px] px-2 transition-colors hover:bg-[#f4f7fc] hover:text-[#2f6df6]"
                                >
                                  {item}
                                </Link>
                              )
                            )}

                            {resolvedCurrentPage < totalPages ? (
                              <Link
                                href={pageHref(currentMailboxFolder, resolvedCurrentPage + 1, resolvedMessageFilter, {
                                  pageSize: resolvedPageSize,
                                  searchField: resolvedSearchField,
                                  searchQuery: resolvedSearchQuery,
                                  sortOrder: currentSortOrder
                                })}
                                prefetch={false}
                                scroll={false}
                                className="inline-flex h-8 min-w-8 items-center justify-center rounded-full transition-colors hover:bg-[#f4f7fc] hover:text-[#2f6df6]"
                              >
                                ›
                              </Link>
                            ) : (
                              <span className="inline-flex h-8 min-w-8 items-center justify-center rounded-full text-[#c0cadb]">
                                ›
                              </span>
                            )}
                          </div>

                          {showPrimaryContentNavigationOverlay
                            ? renderContentNavigationOverlay(
                                "absolute inset-0 z-[120] flex items-center justify-center bg-[rgba(252,253,255,0.96)] transition-opacity duration-200"
                              )
                            : null}
                        </div>
                      </>
                    )}
                  </div>
                )}
              </section>

              {showSplitDetailPanel ? (
                <section
                  className={`${
                    isSplitHorizontalMode
                      ? "relative hidden min-h-0 overflow-hidden xl:flex xl:flex-col"
                      : "relative min-h-0 flex flex-col overflow-hidden border-t border-[#e9eef8]"
                  } bg-white`}
                >
                  {renderMessageDetailContent({ standalone: false })}
                  {showDetailContentNavigationOverlay ? renderContentNavigationOverlay() : null}
                </section>
              ) : null}
            </>
          )}
        </div>
      </div>

      {renderSidebarFolderMenuPortal()}
      {renderAttachmentPreviewModal()}
      {senderProfileTarget && senderMenuAnchor ? (
        <MailSenderProfilePopover
          key={senderProfileTarget.address}
          anchorElement={senderMenuAnchor}
          currentFolder={currentMailboxFolder}
          displayName={senderProfileTarget.label}
          email={senderProfileTarget.address}
          onClose={() => {
            setSenderMenuState({ messageId: null, scope: interactionScopeKey });
            setSenderMenuAnchor(null);
          }}
          onCompose={openComposeToAddress}
          onMailboxChanged={() => { void fetchMailboxData({ silent: true, preserveSelection: true }); }}
          onSaveClassification={saveSenderClassificationByClient}
          onNavigate={(href) => navigateWithinMailbox(href)}
        />
      ) : null}

      {rawSourceModalOpen && previewMessage ? (
        <div className="fixed inset-0 z-[80] flex items-center justify-center bg-[rgba(15,23,42,0.42)] px-6 py-8">
          <button
            type="button"
            aria-label="메일 원본 보기 닫기"
            onClick={() =>
              setRawSourceModalState({
                open: false,
                scope: detailPanelScopeKey
              })
            }
            className="absolute inset-0 cursor-default"
          />
          <div className="relative z-[81] flex max-h-[min(88vh,960px)] w-full max-w-[1100px] flex-col overflow-hidden rounded-[24px] border border-[#dfe7f5] bg-white shadow-[0_30px_80px_rgba(15,23,42,0.24)]">
            <div className="flex items-center justify-between border-b border-[#edf2fb] px-7 py-5">
              <div>
                <p className="text-[18px] font-semibold text-[#1a2943]">메일 원본 보기</p>
                <p className="mt-1 text-[13px] text-[#72839f]">{previewMessage.subject || "(제목 없음)"}</p>
              </div>
              <div className="flex items-center gap-2">
                <button
                  type="button"
                  disabled={!rawSourceText || rawSourceLoading}
                  onClick={() => {
                    void copyToClipboard(rawSourceText, "메일 원본을 복사했습니다.");
                  }}
                  className="om-btn-secondary h-10 rounded-[12px] px-4 text-[13px] text-[#344767] disabled:cursor-not-allowed disabled:opacity-45"
                >
                  복사
                </button>
                <CloseIconButton
                  ariaLabel="메일 원본 보기 닫기"
                  onClick={() =>
                    setRawSourceModalState({
                      open: false,
                      scope: detailPanelScopeKey
                    })
                  }
                />
              </div>
            </div>
            <div className="min-h-0 flex-1 overflow-auto bg-[#f8fbff] px-7 py-6">
              <pre className="whitespace-pre-wrap break-all rounded-[18px] border border-[#e7eef9] bg-white p-5 text-[12px] leading-6 text-[#243555]">
                {rawSourceLoading
                  ? "메일 원본을 불러오는 중입니다."
                  : rawSourceLoadState.error || rawSourceText || "표시할 메일 원본이 없습니다."}
              </pre>
            </div>
          </div>
        </div>
      ) : null}
    </div>
  );
}
