import { lazy, Suspense, useEffect, useMemo, useRef, useState } from "react";
import { buildCollections, MAX_HUNTER_NAME_LENGTH, MODE_COPY, normalizeHunterName } from "../frontend/data";
import { formatPlayTime, formatSaveTimestamp } from "../frontend/saveRepository";
import { resolveSaveContinuation, saveVersionsMatch } from "../frontend/saveContinuation";
import { useActiveHunter, useFrontendStore } from "../frontend/store";
import type { GameModeId, SaveSlotId, SaveSlotState } from "../frontend/types";
import { useMenuController, type MenuAction } from "../input/useMenuController";
import { HEALER_CLASSES, HEALER_CLASS_ORDER } from "../game/healers";
import { ABILITY_ORDER } from "../game/data";
import { BOSS_DEFINITIONS, BOSS_GROUP_BY_ID, BOSS_GROUPS } from "../game/bossCatalog";
import { bossMechanicIsPassive, bossMechanicName } from "../game/bosses/mechanicPool";
import { RUN_BUFFS, formatRunBuffEffect, selectRandomBossPair } from "../game/roguelike";
import type { AbilityId, BossId } from "../game/types";
import {
  GEAR_OWNER_LABELS,
  GEAR_OWNER_ORDER,
  GEAR_RECIPES,
  GEAR_SLOT_LABELS,
  GEAR_SLOT_ORDER,
  GEAR_STAT_LABELS,
  MAX_GEAR_LEVEL,
  canAffordGearUpgrade,
  canUpgradeGearSlot,
  gearBonusText,
  gearUpgradeCosts,
  type GearOwnerId,
} from "../game/progression/gear";
import { DIFFICULTIES, DIFFICULTY_BY_SLUG, bossGroupDrop } from "../game/progression/loot";
import {
  ACTIVE_INFUSION_MIN_GEAR_LEVEL,
  PASSIVE_INFUSIONS,
  PASSIVE_INFUSION_MIN_GEAR_LEVEL,
  activeInfusionUnlocked,
  infusionCosts,
  infusionsForOwner,
  passiveInfusionUnlocked,
} from "../game/progression/infusions";
import { requestDisplaySurface } from "../platform/displayRouting";
import { onlineRepository, type LeaderboardResult } from "../frontend/onlineRepository";
import { DualDisplayFrame } from "./DualDisplayFrame";
import { DEFAULT_CONTROLLER_GLYPHS } from "../input/controllerGlyphs";

const BossTrophyPortrait = lazy(() => import("./BossTrophyPortrait").then((module) => ({ default: module.BossTrophyPortrait })));

function FocusButton({
  id,
  focusedId,
  focus,
  className = "",
  ...props
}: React.ButtonHTMLAttributes<HTMLButtonElement> & { id: string; focusedId: string; focus: (id: string) => void }) {
  return (
    <button
      {...props}
      className={`${className} ${focusedId === id ? "is-controller-focused" : ""}`}
      onFocus={(event) => { focus(id); props.onFocus?.(event); }}
      onPointerEnter={(event) => { focus(id); props.onPointerEnter?.(event); }}
    />
  );
}

function FrontSurface({ className = "", children, bottom = false, ariaLabel }: { className?: string; children: React.ReactNode; bottom?: boolean; ariaLabel: string }) {
  return (
    <section className={`display front-surface ${bottom ? "bottom-display front-bottom" : "top-display front-top"} ${className}`} aria-label={ariaLabel}>
      <div className="front-grain" aria-hidden="true" />
      {children}
    </section>
  );
}

function BrandMark({ compact = false }: { compact?: boolean }) {
  return (
    <div className={`front-brand ${compact ? "is-compact" : ""}`}>
      <span className="brand-sigil">✦</span>
      <span><small>Healers answer the call</small><strong>I Want To Heal</strong></span>
    </div>
  );
}

function ControllerLegend({ back = false }: { back?: boolean }) {
  return <div className="controller-legend"><span><b>{DEFAULT_CONTROLLER_GLYPHS.confirm}</b> Select</span>{back && <span><b>{DEFAULT_CONTROLLER_GLYPHS.back}</b> Back</span>}<span><b>＋</b> Navigate</span></div>;
}

function SaveLibraryContext({ slots, accountId }: { slots: readonly SaveSlotState[]; accountId: string | null }) {
  return (
    <FrontSurface className="login-save-context" bottom ariaLabel="Save slot information">
      <header className="context-header"><span>Device saves</span><b>{accountId ? "SERVER LINKED" : "OFFLINE READY"}</b></header>
      <div className="login-save-list">
        {slots.map((slot) => {
          const save = slot.local ?? slot.online;
          const healer = save ? HEALER_CLASSES[save.activeClassId] : null;
          return (
            <article key={slot.id} className={save ? "has-save" : "is-empty"}>
              <b>{String(slot.id).padStart(2, "0")}</b>
              {save ? (
                <>
                  <div className="login-save-avatar">{save.hunterName[0]}</div>
                  <span>
                    <small>{slot.local ? "On this Thor" : "Online copy"}</small>
                    <strong>{save.hunterName}</strong>
                    <em>Level {save.healers[save.activeClassId].level} {healer?.name} · {save.location}</em>
                  </span>
                  <time><strong>{formatPlayTime(save.playSeconds)}</strong><small>{formatSaveTimestamp(save.updatedAt)}</small></time>
                </>
              ) : (
                <span className="login-empty-copy"><small>Available slot</small><strong>New hunter</strong><em>Continue offline to create</em></span>
              )}
            </article>
          );
        })}
      </div>
      <footer className="login-save-footer"><span>Save details update from upper-screen selection</span><b>LOWER DISPLAY · INFORMATION ONLY</b></footer>
    </FrontSurface>
  );
}

function LoginScreen() {
  const restoreSession = useFrontendStore((state) => state.restoreSession);
  const signIn = useFrontendStore((state) => state.signIn);
  const createAccount = useFrontendStore((state) => state.createAccount);
  const continueOffline = useFrontendStore((state) => state.continueOffline);
  const slots = useFrontendStore((state) => state.slots);
  const accountId = useFrontendStore((state) => state.accountId);
  const notice = useFrontendStore((state) => state.notice);
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const usernameRef = useRef<HTMLInputElement>(null);
  const passwordRef = useRef<HTMLInputElement>(null);
  const restoreStarted = useRef(false);
  useEffect(() => {
    if (restoreStarted.current) return;
    restoreStarted.current = true;
    void restoreSession();
  }, [restoreSession]);
  const actions = useMemo<MenuAction[]>(() => [
    { id: "username", run: () => usernameRef.current?.focus() },
    { id: "password", run: () => passwordRef.current?.focus() },
    { id: "sign-in", run: () => { void signIn(username, password); } },
    { id: "create-account", run: () => { void createAccount(username, password); } },
    { id: "continue", run: continueOffline },
  ], [continueOffline, createAccount, password, signIn, username]);
  const controller = useMenuController(actions);

  const submitSignIn = () => { void signIn(username, password); };

  return (
    <DualDisplayFrame
      top={
        <FrontSurface className="login-surface" ariaLabel="I Want To Heal login">
          <div className="login-aura" aria-hidden="true"><i /><b>+</b><i /></div>
          <BrandMark />
          <div className="login-copy">
            <span>Offline-first hunter records</span>
            <h1>Keep everyone standing.</h1>
            <p>Continue to your saved hunters. Sign in when you want online copies for PC ↔ AYN Thor handoff.</p>
          </div>
          <form className="login-panel" onSubmit={(event) => { event.preventDefault(); submitSignIn(); }}>
            <label htmlFor="account-username">Username</label>
            <input
              ref={usernameRef}
              id="account-username"
              className={controller.focusedId === "username" ? "is-controller-focused" : ""}
              value={username}
              onChange={(event) => setUsername(event.target.value)}
              onFocus={() => controller.focus("username")}
              autoComplete="username"
              maxLength={20}
              minLength={3}
              pattern="[A-Za-z0-9_]+"
              required
            />
            <label htmlFor="account-password">Password</label>
            <input
              ref={passwordRef}
              id="account-password"
              className={controller.focusedId === "password" ? "is-controller-focused" : ""}
              type="password"
              value={password}
              onChange={(event) => setPassword(event.target.value)}
              onFocus={() => controller.focus("password")}
              autoComplete="current-password"
              maxLength={128}
              minLength={10}
              required
            />
            <FocusButton id="sign-in" focusedId={controller.focusedId} focus={controller.focus} className="front-primary" type="submit">
              <span>Sign in & sync</span><small>Online saves enabled</small>
            </FocusButton>
            <FocusButton id="create-account" focusedId={controller.focusedId} focus={controller.focus} className="front-secondary" type="button" onClick={() => { void createAccount(username, password); }}>
              <span>Create account</span><small>Required for first sync</small>
            </FocusButton>
            <FocusButton id="continue" focusedId={controller.focusedId} focus={controller.focus} className="front-secondary" type="button" onClick={continueOffline}>
              <span>Continue</span><small>Choose saved hunter</small>
            </FocusButton>
          </form>
          {notice && <div className="front-notice" role="status" aria-live="polite">{notice}</div>}
          <ControllerLegend />
        </FrontSurface>
      }
      bottom={<SaveLibraryContext slots={slots} accountId={accountId} />}
    />
  );
}

function SlotCard({ slot, selected, focused, onSelect, onFocus }: { slot: SaveSlotState; selected: boolean; focused: boolean; onSelect: () => void; onFocus: () => void }) {
  const continuation = resolveSaveContinuation(slot);
  const save = slot.local ?? slot.online;
  const healer = save ? HEALER_CLASSES[save.activeClassId] : null;
  const copyStatus = continuation === "choose" ? "Newer online" : continuation === "online" ? "Online only" : null;
  return (
    <button className={`save-slot ${selected ? "is-selected" : ""} ${focused ? "is-controller-focused" : ""}`} onClick={onSelect} onFocus={onFocus} onPointerEnter={onFocus}>
      <span className="slot-number">Slot {String(slot.id).padStart(2, "0")}{copyStatus && <b>{copyStatus}</b>}</span>
      {save ? (
        <>
          <div className="slot-portrait">{save.hunterName[0]}<i>✦</i></div>
          <span className="slot-name"><strong>{save.hunterName}</strong><small>Level {save.healers[save.activeClassId].level} · {healer?.name}</small></span>
          <span className="slot-location">{save.location}</span>
          <span className="slot-meta"><b>{formatPlayTime(save.playSeconds)}</b><small>{formatSaveTimestamp(save.updatedAt)}</small></span>
        </>
      ) : (
        <div className="empty-slot"><b>＋</b><strong>New hunter</strong><small>Empty offline slot</small></div>
      )}
      <i className="selection-chevron">›</i>
    </button>
  );
}

function SaveScreen() {
  const slots = useFrontendStore((state) => state.slots);
  const accountId = useFrontendStore((state) => state.accountId);
  const selectedSlotId = useFrontendStore((state) => state.selectedSlotId);
  const notice = useFrontendStore((state) => state.notice);
  const selectSlot = useFrontendStore((state) => state.selectSlot);
  const createSlot = useFrontendStore((state) => state.createSlot);
  const playSlot = useFrontendStore((state) => state.playSlot);
  const uploadSlot = useFrontendStore((state) => state.uploadSlot);
  const downloadSlot = useFrontendStore((state) => state.downloadSlot);
  const copySlot = useFrontendStore((state) => state.copySlot);
  const deleteSlot = useFrontendStore((state) => state.deleteSlot);
  const navigate = useFrontendStore((state) => state.navigate);
  const [dialog, setDialog] = useState<"create" | "copy" | "delete" | "version" | null>(null);
  const [hunterName, setHunterName] = useState("");
  const [resolvingOnline, setResolvingOnline] = useState(false);
  const [versionError, setVersionError] = useState("");
  const selected = slots.find((slot) => slot.id === selectedSlotId)!;
  const hasLocal = Boolean(selected.local);
  const hasOnline = Boolean(selected.online);
  const continuation = resolveSaveContinuation(selected);
  const primaryActionId = continuation === "create" ? "create" : "play";

  const finishCreation = () => {
    if (createSlot(selectedSlotId, hunterName)) {
      setHunterName("");
      setDialog(null);
    }
  };
  const openCreation = () => {
    setHunterName("");
    setDialog("create");
    requestDisplaySurface("top");
  };
  const openSaveDialog = (nextDialog: "copy" | "delete") => {
    setDialog(nextDialog);
    requestDisplaySurface("top");
  };

  const continueWithOnline = async () => {
    if (resolvingOnline) return;
    setResolvingOnline(true);
    setVersionError("");
    await downloadSlot(selectedSlotId);
    const refreshed = useFrontendStore.getState().slots.find((slot) => slot.id === selectedSlotId);
    if (refreshed && saveVersionsMatch(refreshed.local, refreshed.online)) {
      setDialog(null);
      setResolvingOnline(false);
      playSlot(selectedSlotId);
      return;
    }
    setVersionError(useFrontendStore.getState().notice || "Online save could not be loaded.");
    setResolvingOnline(false);
  };

  const continueSelected = () => {
    if (continuation === "create") return openCreation();
    if (continuation === "local") return playSlot(selectedSlotId);
    if (continuation === "online") {
      void continueWithOnline();
      return;
    }
    setVersionError("");
    setDialog("version");
    requestDisplaySurface("top");
  };

  const actions = useMemo<MenuAction[]>(() => dialog === "version"
    ? [
        { id: "version-online", run: () => { void continueWithOnline(); }, enabled: !resolvingOnline },
        { id: "version-local", run: () => { setDialog(null); playSlot(selectedSlotId); }, enabled: !resolvingOnline },
        { id: "cancel-version", run: () => setDialog(null), enabled: !resolvingOnline },
      ]
    : dialog === "create"
    ? [
        { id: "confirm-create", run: finishCreation },
        { id: "cancel-create", run: () => setDialog(null) },
      ]
    : dialog === "copy"
    ? slots.filter((slot) => slot.id !== selectedSlotId).map((slot) => ({ id: `copy-${slot.id}`, run: () => { copySlot(selectedSlotId, slot.id); setDialog(null); } }))
    : dialog === "delete"
      ? [
          { id: "confirm-delete", run: () => { deleteSlot(selectedSlotId); setDialog(null); } },
          { id: "cancel-delete", run: () => setDialog(null) },
        ]
      : [
          ...slots.map((slot, index) => ({
            id: `slot-${slot.id}`,
            run: () => selectSlot(slot.id),
            neighbors: {
              left: `slot-${slots[Math.max(0, index - 1)].id}`,
              right: `slot-${slots[Math.min(slots.length - 1, index + 1)].id}`,
              down: primaryActionId,
            },
          })),
          { id: primaryActionId, run: continueSelected, enabled: !resolvingOnline, neighbors: { up: `slot-${selectedSlotId}`, right: "upload" } },
          { id: "upload", run: () => uploadSlot(selectedSlotId), enabled: hasLocal && Boolean(accountId), neighbors: { left: primaryActionId, right: "download", up: "slot-1" } },
          { id: "download", run: () => downloadSlot(selectedSlotId), enabled: hasOnline && Boolean(accountId), neighbors: { left: "upload", right: "copy", up: "slot-2" } },
          { id: "copy", run: () => openSaveDialog("copy"), enabled: hasLocal, neighbors: { left: "download", right: "delete", up: "slot-2" } },
          { id: "delete", run: () => openSaveDialog("delete"), enabled: hasLocal, neighbors: { left: "copy", right: "back", up: "slot-3" } },
          { id: "back", run: () => navigate("login"), neighbors: { left: "delete", up: "slot-3" } },
        ], [accountId, continuation, copySlot, createSlot, deleteSlot, dialog, downloadSlot, hasLocal, hasOnline, hunterName, navigate, playSlot, primaryActionId, resolvingOnline, selectSlot, selectedSlotId, slots, uploadSlot]);
  const controller = useMenuController(actions, { onBack: () => dialog ? resolvingOnline ? undefined : setDialog(null) : navigate("login") });

  const cloudStatus = continuation === "choose" ? "Newer online save" : !accountId ? "Offline mode" : selected.online ? "Online version available" : "No online version";
  return (
    <DualDisplayFrame
      top={
        <FrontSurface className={`save-surface ${dialog ? "has-dialog" : ""}`} ariaLabel="Save slots">
          <header className="front-screen-header"><BrandMark compact /><div><span>Hunter records</span><h1>Choose a save</h1></div><b className={accountId ? "is-online" : ""}>{accountId ? `● ${accountId}` : "○ OFFLINE"}</b></header>
          <div className="save-slot-grid">
            {slots.map((slot) => (
              <SlotCard
                key={slot.id}
                slot={slot}
                selected={selectedSlotId === slot.id}
                focused={controller.isFocused(`slot-${slot.id}`)}
                onSelect={() => selectSlot(slot.id)}
                onFocus={() => controller.focus(`slot-${slot.id}`)}
              />
            ))}
          </div>
          <div className="save-top-actions">
            <FocusButton id={primaryActionId} focusedId={controller.focusedId} focus={controller.focus} className="front-primary" disabled={resolvingOnline} onClick={continueSelected}>
              <span>{continuation === "create" ? "Create hunter" : resolvingOnline ? "Loading online save…" : "Continue"}</span>
              <small>{continuation === "create" ? `Use slot ${selectedSlotId}` : continuation === "online" ? "Download online copy" : continuation === "choose" ? "Choose online or device copy" : `Slot ${selectedSlotId} · ${selected.local?.hunterName}`}</small>
            </FocusButton>
            <FocusButton id="upload" focusedId={controller.focusedId} focus={controller.focus} disabled={!hasLocal || !accountId} onClick={() => uploadSlot(selectedSlotId)}><strong>Upload</strong><small>Device → server</small></FocusButton>
            <FocusButton id="download" focusedId={controller.focusedId} focus={controller.focus} disabled={!hasOnline || !accountId} onClick={() => downloadSlot(selectedSlotId)}><strong>Download</strong><small>Server → device</small></FocusButton>
            <FocusButton id="copy" focusedId={controller.focusedId} focus={controller.focus} disabled={!hasLocal} onClick={() => openSaveDialog("copy")}><strong>Copy</strong><small>Duplicate save</small></FocusButton>
            <FocusButton id="delete" focusedId={controller.focusedId} focus={controller.focus} disabled={!hasLocal} className="danger-link" onClick={() => openSaveDialog("delete")}><strong>Delete</strong><small>Erase device copy</small></FocusButton>
            <FocusButton id="back" focusedId={controller.focusedId} focus={controller.focus} onClick={() => navigate("login")}><strong>Back</strong><small>Login screen</small></FocusButton>
          </div>
          <div className="save-footer"><span>Autosave <b>OFFLINE FIRST</b></span><div className="save-top-notice" role="status" aria-live="polite">{notice || "Lower display shows selected save details."}</div><ControllerLegend back /></div>
          {dialog && (
            <div className={`front-dialog ${dialog === "version" ? "version-dialog" : ""}`} role="dialog" aria-modal="true" aria-label={dialog === "version" ? "Choose save version" : dialog === "create" ? "Name new hunter" : dialog === "copy" ? "Copy save" : "Delete save"}>
              {dialog === "version" && selected.local && selected.online ? (
                <div className="version-choice-dialog">
                  <span>Newer online save found</span>
                  <h2>Which save do you want?</h2>
                  <p>Online choice replaces this device copy. Device choice keeps online copy unchanged.</p>
                  <div className="version-comparison">
                    <article className="is-newer">
                      <header><span>Online copy</span><b>NEWER</b></header>
                      <strong>{selected.online.hunterName}</strong>
                      <time>{formatSaveTimestamp(selected.online.updatedAt)}</time>
                      <small>{formatPlayTime(selected.online.playSeconds)} · Level {selected.online.healers[selected.online.activeClassId].level}</small>
                    </article>
                    <article>
                      <header><span>Device copy</span><b>OFFLINE</b></header>
                      <strong>{selected.local.hunterName}</strong>
                      <time>{formatSaveTimestamp(selected.local.updatedAt)}</time>
                      <small>{formatPlayTime(selected.local.playSeconds)} · Level {selected.local.healers[selected.local.activeClassId].level}</small>
                    </article>
                  </div>
                  <div className="dialog-actions version-actions">
                    <FocusButton id="version-online" focusedId={controller.focusedId} focus={controller.focus} className="front-primary" disabled={resolvingOnline} onClick={() => { void continueWithOnline(); }}><span>{resolvingOnline ? "Loading…" : "Continue online copy"}</span><small>{formatSaveTimestamp(selected.online.updatedAt)}</small></FocusButton>
                    <FocusButton id="version-local" focusedId={controller.focusedId} focus={controller.focus} disabled={resolvingOnline} onClick={() => { setDialog(null); playSlot(selectedSlotId); }}><span>Continue device copy</span><small>{formatSaveTimestamp(selected.local.updatedAt)}</small></FocusButton>
                    <FocusButton id="cancel-version" focusedId={controller.focusedId} focus={controller.focus} disabled={resolvingOnline} onClick={() => setDialog(null)}>Cancel</FocusButton>
                  </div>
                  {versionError && <div className="version-choice-error" role="alert">{versionError}</div>}
                </div>
              ) : dialog === "create" ? (
                <form onSubmit={(event) => { event.preventDefault(); finishCreation(); }}>
                  <span>New offline save</span><h2>Name your hunter</h2><p>This name identifies the character in local and online save lists.</p>
                  <label htmlFor="new-hunter-name">Hunter name</label>
                  <input
                    id="new-hunter-name"
                    value={hunterName}
                    maxLength={MAX_HUNTER_NAME_LENGTH}
                    autoComplete="off"
                    autoFocus
                    onChange={(event) => setHunterName(event.target.value)}
                    onKeyDown={(event) => { if (event.key === "Escape") setDialog(null); }}
                    placeholder="Enter name"
                  />
                  <small>{normalizeHunterName(hunterName).length}/{MAX_HUNTER_NAME_LENGTH}</small>
                  <div className="dialog-actions">
                    <FocusButton id="confirm-create" focusedId={controller.focusedId} focus={controller.focus} className="front-primary" type="submit" disabled={!normalizeHunterName(hunterName)}>Create hunter</FocusButton>
                    <FocusButton id="cancel-create" focusedId={controller.focusedId} focus={controller.focus} type="button" onClick={() => setDialog(null)}>Cancel</FocusButton>
                  </div>
                </form>
              ) : dialog === "copy" ? (
                <>
                  <span>Copy local save</span><h2>Choose destination</h2><p>Destination local save will be overwritten. Online copies stay unchanged.</p>
                  <div className="dialog-actions">
                    {slots.filter((slot) => slot.id !== selectedSlotId).map((slot) => (
                      <FocusButton key={slot.id} id={`copy-${slot.id}`} focusedId={controller.focusedId} focus={controller.focus} onClick={() => { copySlot(selectedSlotId, slot.id); setDialog(null); }}>
                        Slot {slot.id}<small>{slot.local ? "Overwrite" : "Empty"}</small>
                      </FocusButton>
                    ))}
                  </div>
                </>
              ) : (
                <>
                  <span>Delete local save</span><h2>Erase slot {selectedSlotId}?</h2><p>Device copy will be removed. Existing online version remains available for download.</p>
                  <div className="dialog-actions">
                    <FocusButton id="confirm-delete" focusedId={controller.focusedId} focus={controller.focus} className="is-danger" onClick={() => { deleteSlot(selectedSlotId); setDialog(null); }}>Delete local</FocusButton>
                    <FocusButton id="cancel-delete" focusedId={controller.focusedId} focus={controller.focus} onClick={() => setDialog(null)}>Cancel</FocusButton>
                  </div>
                </>
              )}
            </div>
          )}
        </FrontSurface>
      }
      bottom={
        <FrontSurface className="save-context" bottom ariaLabel="Selected save information">
          <header className="context-header"><span>Slot {selectedSlotId}</span><b>{cloudStatus}</b></header>
          <div className="selected-save-summary">
            {selected.local ?? selected.online ? (
              <><div className="summary-avatar">{(selected.local ?? selected.online)!.hunterName[0]}</div><span><small>{selected.local ? "Device save" : "Online copy only"}</small><h2>{(selected.local ?? selected.online)!.hunterName}</h2><p>{(selected.local ?? selected.online)!.location}</p><time>{formatSaveTimestamp((selected.local ?? selected.online)!.updatedAt)}</time></span></>
            ) : (
              <><div className="summary-avatar is-empty">＋</div><span><small>Local record</small><h2>Empty slot</h2><p>Create a hunter or download an online version.</p></span></>
            )}
          </div>
          {(selected.local ?? selected.online) && (() => {
            const save = (selected.local ?? selected.online)!;
            const healer = HEALER_CLASSES[save.activeClassId];
            return (
              <>
                <div className="save-dossier-stats">
                  <span><small>Active healer</small><strong>Lv {save.healers[save.activeClassId].level}</strong><em>{healer.name}</em></span>
                  <span><small>Play time</small><strong>{formatPlayTime(save.playSeconds)}</strong><em>Local activity</em></span>
                  <span><small>Boss kills</small><strong>{save.stats.totalBossKills}</strong><em>{save.stats.flawlessClears} flawless</em></span>
                </div>
                <div className="save-dossier-records">
                  <span><small>Roguelike best</small><b>Round {save.stats.highestRoguelikeRound}</b></span>
                  <span><small>Endless best</small><b>{save.stats.highestRogueTrialsEndlessKills} kills</b></span>
                </div>
              </>
            );
          })()}
          <div className="save-copy-state">
            <span><i className={selected.local ? "is-present" : ""} />Device copy<b>{selected.local ? formatSaveTimestamp(selected.local.updatedAt) : "Not present"}</b></span>
            <span><i className={selected.online ? "is-present" : ""} />Online copy<b>{selected.online ? formatSaveTimestamp(selected.online.updatedAt) : accountId ? "Not uploaded" : "Sign-in required"}</b></span>
          </div>
          <div className="front-notice is-lower">{notice || "Use upper display for every save action. Details here follow selected slot."}</div>
        </FrontSurface>
      }
    />
  );
}

const HOME_MODES: { id: GameModeId; icon: string; label: string; copy: string }[] = [
  { id: "roguelike-pve", icon: "✦", label: "PVE", copy: "Randomized roguelike runs" },
  { id: "rogue-trials", icon: "Ⅲ", label: "Rogue Trials", copy: "Four rounds, then a boss trio" },
  { id: "dungeons", icon: "♜", label: "Dungeons", copy: "Choose your boss encounter" },
  { id: "roguelike-pvp", icon: "⚔", label: "Roguelike PvP", copy: "Draft, race, sabotage" },
  { id: "stadium-pvp", icon: "◉", label: "Stadium PvP", copy: "Prepared 5v5 rounds" },
];

function HomeScreen() {
  const hunter = useActiveHunter();
  const accountId = useFrontendStore((state) => state.accountId);
  const selectMode = useFrontendStore((state) => state.selectMode);
  const selectHealerClass = useFrontendStore((state) => state.selectHealerClass);
  const navigate = useFrontendStore((state) => state.navigate);
  const actions = useMemo<MenuAction[]>(() => [
    { id: "roguelike-pve", run: () => selectMode("roguelike-pve"), neighbors: { right: "rogue-trials", down: "roguelike-pvp" } },
    { id: "rogue-trials", run: () => selectMode("rogue-trials"), neighbors: { left: "roguelike-pve", right: "dungeons", down: "stadium-pvp" } },
    { id: "dungeons", run: () => selectMode("dungeons"), neighbors: { left: "rogue-trials", down: "stadium-pvp" } },
    { id: "roguelike-pvp", run: () => selectMode("roguelike-pvp"), neighbors: { left: "roguelike-pve", right: "stadium-pvp", up: "roguelike-pve", down: "profile" } },
    { id: "stadium-pvp", run: () => selectMode("stadium-pvp"), neighbors: { left: "roguelike-pvp", up: "rogue-trials", down: "settings" } },
    { id: "profile", run: () => navigate("profile"), neighbors: { up: "roguelike-pvp", right: "gear", down: "class-priest" } },
    { id: "gear", run: () => navigate("gear"), neighbors: { up: "roguelike-pvp", left: "profile", right: "settings", down: "class-druid" } },
    { id: "settings", run: () => navigate("settings"), neighbors: { up: "stadium-pvp", left: "gear", down: "class-shaman" } },
    ...HEALER_CLASS_ORDER.map((classId, index) => ({
      id: `class-${classId}`,
      run: () => selectHealerClass(classId),
      neighbors: {
        left: `class-${HEALER_CLASS_ORDER[(index + HEALER_CLASS_ORDER.length - 1) % HEALER_CLASS_ORDER.length]}`,
        right: `class-${HEALER_CLASS_ORDER[(index + 1) % HEALER_CLASS_ORDER.length]}`,
        up: index === 0 ? "profile" : index === 1 ? "gear" : "settings",
        down: "change-save",
      },
    })),
    { id: "change-save", run: () => navigate("saves"), neighbors: { up: "class-druid" } },
  ], [navigate, selectHealerClass, selectMode]);
  const controller = useMenuController(actions, { columns: 2, onBack: () => navigate("saves") });
  if (!hunter) return null;
  const activeHealer = HEALER_CLASSES[hunter.activeClassId];
  const activeProgress = hunter.healers[hunter.activeClassId];

  return (
    <DualDisplayFrame
      top={
        <FrontSurface className="home-surface" ariaLabel="Main menu">
          <header className="home-header"><BrandMark compact /><span>Welcome back, <b>{hunter.hunterName}</b></span><i>{accountId ? "● SYNC READY" : "○ OFFLINE"}</i></header>
          <div className="mode-grid">
            {HOME_MODES.map((mode) => (
              <FocusButton key={mode.id} id={mode.id} focusedId={controller.focusedId} focus={controller.focus} className="mode-card" onClick={() => selectMode(mode.id)}>
                <i>{mode.icon}</i><span><small>{mode.copy}</small><strong>{mode.label}</strong></span><b>›</b>
              </FocusButton>
            ))}
          </div>
          <div className="home-secondary-actions">
            <FocusButton id="profile" focusedId={controller.focusedId} focus={controller.focus} onClick={() => navigate("profile")}><i>♙</i><span><strong>Hunter Profile</strong><small>Stats & collection log</small></span><b>›</b></FocusButton>
            <FocusButton id="gear" focusedId={controller.focusedId} focus={controller.focus} onClick={() => navigate("gear")}><i>⚒</i><span><strong>Gear Upgrade</strong><small>Spend group drops</small></span><b>›</b></FocusButton>
            <FocusButton id="settings" focusedId={controller.focusedId} focus={controller.focus} onClick={() => navigate("settings")}><i>⚙</i><span><strong>Settings</strong><small>Audio, display, controls</small></span><b>›</b></FocusButton>
          </div>
          <ControllerLegend back />
        </FrontSurface>
      }
      bottom={
        <FrontSurface className="hunter-context" bottom ariaLabel="Active hunter summary">
          <header className="context-header"><span>Active hunter</span><b>LOCAL AUTOSAVE</b></header>
          <div className="hunter-card">
            <div className="hunter-crest">{hunter.hunterName[0]}<i style={{ background: activeHealer.color }}>{activeHealer.icon}</i></div>
            <span><small>Level {activeProgress.level} · {activeHealer.specialization}</small><h2>{hunter.hunterName}</h2><p>{hunter.location}</p></span>
          </div>
          <div className="hunter-stat-row">
            <span><small>Boss kills</small><strong>{hunter.stats.totalBossKills}</strong></span>
            <span><small>Flawless</small><strong>{hunter.stats.flawlessClears}</strong></span>
            <span><small>Allies saved</small><strong>{hunter.stats.alliesSaved}</strong></span>
          </div>
          <div className="healer-picker"><span>Choose healer</span><div>{HEALER_CLASS_ORDER.map((classId) => {
            const healer = HEALER_CLASSES[classId];
            const progress = hunter.healers[classId];
            return <FocusButton key={classId} id={`class-${classId}`} focusedId={controller.focusedId} focus={controller.focus} className={classId === hunter.activeClassId ? "is-selected" : ""} aria-pressed={classId === hunter.activeClassId} onClick={() => selectHealerClass(classId)}>
              <i style={{ color: healer.color }}>{healer.icon}</i><span><strong>{healer.name}</strong><small>Level {progress.level} · {progress.inventory.length} items</small></span><b>{classId === hunter.activeClassId ? "✓" : ""}</b>
            </FocusButton>;
          })}</div></div>
          <FocusButton id="change-save" focusedId={controller.focusedId} focus={controller.focus} className="change-save" onClick={() => navigate("saves")}><span>Change save slot</span><small>Last saved {formatSaveTimestamp(hunter.updatedAt)}</small></FocusButton>
        </FrontSurface>
      }
    />
  );
}

type ProfileStatId = BossId | "roguelike" | "rogue-trials-endless";

function ProfileScreen() {
  const hunter = useActiveHunter();
  const accountId = useFrontendStore((state) => state.accountId);
  const navigate = useFrontendStore((state) => state.navigate);
  const collections = useMemo(() => hunter ? buildCollections(hunter.collectionLog, hunter.stats.bossKills) : [], [hunter]);
  const [groupId, setGroupId] = useState(collections[0]?.groupId ?? "");
  const [collectionView, setCollectionView] = useState<"loot" | "trophies" | "stats">("trophies");
  const collection = collections.find((group) => group.groupId === groupId) ?? collections[0];
  const [selectedStat, setSelectedStat] = useState<ProfileStatId>("roguelike");
  const [leaderboard, setLeaderboard] = useState<LeaderboardResult | null>(null);
  const [leaderboardStatus, setLeaderboardStatus] = useState("");
  useEffect(() => {
    if (selectedStat === "roguelike" || selectedStat === "rogue-trials-endless" || collection?.bosses.some((boss) => boss.bossId === selectedStat)) return;
    setSelectedStat(collection?.bosses[0]?.bossId ?? "roguelike");
  }, [collection, selectedStat]);
  useEffect(() => {
    if (!hunter || collectionView !== "stats") return;
    if (!accountId) {
      setLeaderboard(null);
      setLeaderboardStatus("Sign in to view overall rankings.");
      return;
    }
    let cancelled = false;
    setLeaderboardStatus("Loading overall rankings…");
    const request = selectedStat === "roguelike"
      ? onlineRepository.roguelikeLeaderboard(hunter.slotId)
      : selectedStat === "rogue-trials-endless"
        ? onlineRepository.rogueTrialsEndlessLeaderboard(hunter.slotId)
        : onlineRepository.bossLeaderboard(selectedStat, hunter.slotId);
    void request.then((result) => {
      if (cancelled) return;
      setLeaderboard(result);
      setLeaderboardStatus("");
    }).catch((error) => {
      if (cancelled) return;
      setLeaderboard(null);
      setLeaderboardStatus(error instanceof Error ? error.message : "Leaderboard unavailable.");
    });
    return () => { cancelled = true; };
  }, [accountId, collectionView, hunter, selectedStat]);
  const actions = useMemo<MenuAction[]>(() => [
    { id: "view-trophies", run: () => setCollectionView("trophies"), neighbors: { right: "view-stats" } },
    { id: "view-stats", run: () => setCollectionView("stats"), neighbors: { left: "view-trophies", right: "view-loot", down: collectionView === "stats" ? "stat-roguelike" : undefined } },
    { id: "view-loot", run: () => setCollectionView("loot"), neighbors: { left: "view-stats" } },
    ...(collectionView === "stats" ? [
      { id: "stat-roguelike", run: () => setSelectedStat("roguelike"), neighbors: { up: "view-stats", down: "stat-rogue-trials-endless" } },
      { id: "stat-rogue-trials-endless", run: () => setSelectedStat("rogue-trials-endless"), neighbors: { up: "stat-roguelike", down: `stat-${collection.bosses[0].bossId}` } },
      ...collection.bosses.map((boss, index) => ({
        id: `stat-${boss.bossId}`,
        run: () => setSelectedStat(boss.bossId),
        neighbors: {
          up: index === 0 ? "stat-rogue-trials-endless" : `stat-${collection.bosses[index - 1].bossId}`,
          down: index === collection.bosses.length - 1 ? `group-${collection.groupId}` : `stat-${collection.bosses[index + 1].bossId}`,
        },
      })),
    ] : []),
    ...collections.map((group) => ({ id: `group-${group.groupId}`, run: () => setGroupId(group.groupId) })),
    { id: "back", run: () => navigate("home") },
  ], [collection, collectionView, collections, navigate]);
  const controller = useMenuController(actions, { onBack: () => navigate("home") });
  if (!hunter || !collection) return null;
  const activeHealer = HEALER_CLASSES[hunter.activeClassId];
  const activeProgress = hunter.healers[hunter.activeClassId];
  const earned = collection.drops.filter((drop) => drop.count > 0).length;
  const trophiesEarned = collection.bosses.filter((boss) => boss.pet.count > 0).length;
  const selectedStatValue = selectedStat === "roguelike"
    ? hunter.stats.highestRoguelikeRound
    : selectedStat === "rogue-trials-endless"
      ? hunter.stats.highestRogueTrialsEndlessKills
      : hunter.stats.bossKills[selectedStat] ?? 0;
  const selectedStatLabel = selectedStat === "roguelike"
    ? "Roguelike rounds"
    : selectedStat === "rogue-trials-endless"
      ? "Rogue Trials endless kills"
      : BOSS_DEFINITIONS[selectedStat].name;

  return (
    <DualDisplayFrame
      top={
        <FrontSurface className="profile-surface" ariaLabel="Hunter profile collection log">
          <header className="front-screen-header profile-header"><BrandMark compact /><div><span>Hunter profile</span><h1>Collection log</h1></div><div className="profile-view-tabs" role="tablist" aria-label="Collection view">
            <FocusButton id="view-trophies" focusedId={controller.focusedId} focus={controller.focus} className={collectionView === "trophies" ? "is-selected" : ""} role="tab" aria-selected={collectionView === "trophies"} onClick={() => setCollectionView("trophies")}>Trophy Case</FocusButton>
            <FocusButton id="view-stats" focusedId={controller.focusedId} focus={controller.focus} className={collectionView === "stats" ? "is-selected" : ""} role="tab" aria-selected={collectionView === "stats"} onClick={() => setCollectionView("stats")}>Boss Stats</FocusButton>
            <FocusButton id="view-loot" focusedId={controller.focusedId} focus={controller.focus} className={collectionView === "loot" ? "is-selected" : ""} role="tab" aria-selected={collectionView === "loot"} onClick={() => setCollectionView("loot")}>Group Loot</FocusButton>
          </div><FocusButton id="back" focusedId={controller.focusedId} focus={controller.focus} className="header-back" onClick={() => navigate("home")}>{DEFAULT_CONTROLLER_GLYPHS.back} · Back</FocusButton></header>
          {collectionView === "loot" ? <>
            <div className="collection-heading"><span><small>Shared group drops · Core: {collection.coreMechanic}</small><h2>Group {collection.groupLetter} · {collection.groupName}</h2></span><b>{earned} / {collection.drops.length} discovered</b></div>
            <div className="collection-grid">
              {collection.drops.map((drop) => (
                <article key={drop.id} className={`collection-drop rarity-${drop.rarity.toLowerCase()} ${drop.count === 0 ? "is-missing" : ""}`}>
                  <span className="drop-icon">{drop.icon}<b>{drop.count}</b></span>
                  <small>{drop.rarity}</small><strong>{drop.name}</strong>
                  <p>{drop.count ? `${drop.count} earned` : collection.defeated ? "Not yet earned" : `Defeat a Group ${collection.groupLetter} boss`}</p>
                  <small>{drop.chance}{drop.itemLevel ? ` · iLvl ${drop.itemLevel}` : ""}</small>
                </article>
              ))}
            </div>
            <div className="collection-note"><i>✦</i><span><strong>Boss pets stay individual.</strong><small>Open Trophy Case to inspect every guardian pet.</small></span></div>
          </> : collectionView === "trophies" ? <>
            <div className="collection-heading trophy-heading"><span><small>Boss pets · 1 in 500 per victory</small><h2>Group {collection.groupLetter} · {collection.groupName}</h2></span><b>{trophiesEarned} / {collection.bosses.length} trophies lit</b></div>
            <div className={`trophy-case trophy-count-${collection.bosses.length}`}>
              {collection.bosses.map((boss) => {
                const owned = boss.pet.count > 0;
                return <article key={boss.bossId} className={`boss-trophy ${owned ? "is-owned" : "is-locked"}`} style={{ "--boss-accent": BOSS_DEFINITIONS[boss.bossId].accent } as React.CSSProperties}>
                  <Suspense fallback={<div className="trophy-portrait trophy-portrait-fallback" role="img" aria-label={`${boss.bossName} portrait`}><span>{BOSS_DEFINITIONS[boss.bossId].icon}</span></div>}><BossTrophyPortrait bossId={boss.bossId} /></Suspense>
                  <div className="trophy-plaque"><small>{owned ? "Pet secured" : "Pet undiscovered"}</small><strong>{boss.bossName}</strong><span>{boss.kills} kills · {boss.pet.chance}</span></div>
                  <b className="trophy-state">{owned ? `Owned${boss.pet.count > 1 ? ` ×${boss.pet.count}` : ""}` : "Locked"}</b>
                </article>;
              })}
            </div>
            <div className="collection-note trophy-note"><i>♛</i><span><strong>Each guardian keeps its own trophy.</strong><small>Defeat that boss for a 1 in 500 pet roll.</small></span></div>
          </> : <>
            <div className="collection-heading boss-stats-heading"><span><small>Lifetime records · Overall leaderboards</small><h2>Boss Stats</h2></span><b>Endless best {hunter.stats.highestRogueTrialsEndlessKills} kills</b></div>
            <div className="boss-stats-layout">
              <section className="boss-stat-selector" aria-label="Boss statistic selection">
                <FocusButton id="stat-roguelike" focusedId={controller.focusedId} focus={controller.focus} className={selectedStat === "roguelike" ? "is-selected" : ""} onClick={() => setSelectedStat("roguelike")}><i>∞</i><span><strong>Roguelike</strong><small>Highest round before defeat</small></span><b>{hunter.stats.highestRoguelikeRound}</b></FocusButton>
                <FocusButton id="stat-rogue-trials-endless" focusedId={controller.focusedId} focus={controller.focus} className={selectedStat === "rogue-trials-endless" ? "is-selected" : ""} onClick={() => setSelectedStat("rogue-trials-endless")}><i>Ⅲ</i><span><strong>Trials Endless</strong><small>Most bosses in one run</small></span><b>{hunter.stats.highestRogueTrialsEndlessKills}</b></FocusButton>
                {collection.bosses.map((boss) => <FocusButton key={boss.bossId} id={`stat-${boss.bossId}`} focusedId={controller.focusedId} focus={controller.focus} className={selectedStat === boss.bossId ? "is-selected" : ""} onClick={() => setSelectedStat(boss.bossId)}><i>{BOSS_DEFINITIONS[boss.bossId].icon}</i><span><strong>{boss.bossName}</strong><small>Lifetime boss kills</small></span><b>{boss.kills}</b></FocusButton>)}
              </section>
              <section className="leaderboard-panel" aria-label="Overall leaderboard">
                <header><span><small>Overall Top 5</small><strong>{selectedStatLabel}</strong></span><b>{selectedStatValue} {selectedStat === "roguelike" ? "round" : "kills"}</b></header>
                {leaderboardStatus ? <div className="leaderboard-status">{leaderboardStatus}</div> : <div className="leaderboard-rows">
                  {leaderboard?.top.length ? leaderboard.top.map((entry) => <div key={`${entry.username}-${entry.slotId}`} className={entry.username === accountId && entry.slotId === hunter.slotId ? "is-you" : ""}><b>#{entry.rank}</b><span><strong>{entry.hunterName}</strong><small>{entry.username}</small></span><em>{entry.value}</em></div>) : <div className="leaderboard-empty">No ranked hunters yet.</div>}
                </div>}
                <div className="leaderboard-self"><b>{leaderboard?.current ? `#${leaderboard.current.rank}` : "—"}</b><span><strong>Your rank · {hunter.hunterName}</strong><small>{accountId ?? "Offline hunter"}</small></span><em>{selectedStatValue}</em></div>
              </section>
            </div>
            <div className="collection-note trophy-note"><i>◆</i><span><strong>Rankings update with server saves.</strong><small>Top five always shown; your row stays visible at any rank.</small></span></div>
          </>}
        </FrontSurface>
      }
      bottom={
        <FrontSurface className="profile-context" bottom ariaLabel="Hunter statistics and mechanic group list">
          <header className="context-header"><span>{hunter.hunterName} · {activeHealer.name} stats</span><b>LEVEL {activeProgress.level}</b></header>
          <div className="profile-stats">
            <span><small>Total boss kills</small><strong>{hunter.stats.totalBossKills}</strong></span>
            <span><small>Flawless clears</small><strong>{hunter.stats.flawlessClears}</strong></span>
            <span><small>Allies saved</small><strong>{hunter.stats.alliesSaved}</strong></span>
            <span><small>Healing done</small><strong>{hunter.stats.healingDone.toLocaleString()}</strong></span>
            <span><small>Highest roguelike round</small><strong>{hunter.stats.highestRoguelikeRound}</strong></span>
            <span><small>Endless best</small><strong>{hunter.stats.highestRogueTrialsEndlessKills}</strong></span>
          </div>
          <div className="boss-log"><span>Mechanic groups</span>{collections.map((group) => (
            <FocusButton key={group.groupId} id={`group-${group.groupId}`} focusedId={controller.focusedId} focus={controller.focus} className={group.groupId === collection.groupId ? "is-selected" : ""} onClick={() => setGroupId(group.groupId)}>
              <i>{group.defeated ? group.groupLetter : "?"}</i><span><strong>Group {group.groupLetter} · {group.groupName}</strong><small>{group.bosses.reduce((sum, boss) => sum + boss.kills, 0)} kills · {group.coreMechanic}</small></span><b>{group.drops.filter((drop) => drop.count > 0).length}/{group.drops.length}</b>
            </FocusButton>
          ))}</div>
        </FrontSurface>
      }
    />
  );
}

function GearScreen() {
  const hunter = useActiveHunter();
  const navigate = useFrontendStore((state) => state.navigate);
  const notice = useFrontendStore((state) => state.notice);
  const selectedOwnerId = useFrontendStore((state) => state.selectedGearOwnerId);
  const selectedSlotId = useFrontendStore((state) => state.selectedGearSlotId);
  const workshopMode = useFrontendStore((state) => state.gearWorkshopMode);
  const selectedInfusionId = useFrontendStore((state) => state.selectedInfusionId);
  const selectedPassiveAbilityId = useFrontendStore((state) => state.selectedPassiveAbilityId);
  const selectedPassiveInfusionId = useFrontendStore((state) => state.selectedPassiveInfusionId);
  const selectOwner = useFrontendStore((state) => state.selectGearOwner);
  const selectSlot = useFrontendStore((state) => state.selectGearSlot);
  const selectWorkshopMode = useFrontendStore((state) => state.selectGearWorkshopMode);
  const selectInfusion = useFrontendStore((state) => state.selectInfusion);
  const selectPassiveAbility = useFrontendStore((state) => state.selectPassiveAbility);
  const selectPassiveInfusion = useFrontendStore((state) => state.selectPassiveInfusion);
  const upgrade = useFrontendStore((state) => state.upgradeSelectedGear);
  const installInfusion = useFrontendStore((state) => state.equipSelectedInfusion);
  const installPassive = useFrontendStore((state) => state.equipPassiveInfusion);
  const slot = hunter?.gearProgress[selectedOwnerId].slots[selectedSlotId];
  const upgradeReadiness = useMemo(() => {
    const owners = new Set<GearOwnerId>();
    const slots = new Set<string>();
    if (!hunter) return { owners, slots };

    for (const ownerId of GEAR_OWNER_ORDER) {
      for (const slotId of GEAR_SLOT_ORDER) {
        if (!canUpgradeGearSlot(hunter.gearProgress, hunter.materials, ownerId, slotId)) continue;
        owners.add(ownerId);
        slots.add(`${ownerId}:${slotId}`);
      }
    }
    return { owners, slots };
  }, [hunter]);
  const recipe = GEAR_RECIPES[selectedOwnerId][selectedSlotId];
  const costs = hunter && slot ? gearUpgradeCosts(selectedOwnerId, selectedSlotId, slot.level) : [];
  const canUpgrade = upgradeReadiness.slots.has(`${selectedOwnerId}:${selectedSlotId}`);
  const infusionChoices = infusionsForOwner(selectedOwnerId);
  const selectedInfusion = infusionChoices.find((choice) => choice.id === selectedInfusionId) ?? infusionChoices[0];
  const selectedInfusionCosts = hunter ? infusionCosts(selectedOwnerId, selectedSlotId, selectedInfusion.id) : [];
  const activeUnlocked = Boolean(hunter && activeInfusionUnlocked(hunter.gearProgress[selectedOwnerId]));
  const anchorUnlocked = Boolean(slot && slot.level >= ACTIVE_INFUSION_MIN_GEAR_LEVEL);
  const infusionEquipped = hunter?.gearProgress[selectedOwnerId].infusionAbilityId === selectedInfusion.id;
  const canInstallInfusion = Boolean(hunter && activeUnlocked && anchorUnlocked && !infusionEquipped && canAffordGearUpgrade(hunter.materials, selectedInfusionCosts));
  const passiveUnlocked = Boolean(hunter && passiveInfusionUnlocked(hunter.gearProgress));
  const healerOwner = selectedOwnerId === "priest" || selectedOwnerId === "druid" || selectedOwnerId === "shaman";
  const passiveHealerClassId = selectedOwnerId === "priest" || selectedOwnerId === "druid" || selectedOwnerId === "shaman" ? selectedOwnerId : "priest";
  const passiveChoices = PASSIVE_INFUSIONS.filter((passive) => passive.abilityId === selectedPassiveAbilityId);
  const selectedPassive = RUN_BUFFS[selectedPassiveInfusionId];
  const healerAbilities = HEALER_CLASSES[passiveHealerClassId].abilities;
  const previewEntryId = workshopMode === "upgrade" ? "upgrade" : `infusion-${infusionChoices[0].id}`;
  const actions = useMemo<MenuAction[]>(() => [
    ...GEAR_OWNER_ORDER.map((ownerId, index) => ({
      id: `owner-${ownerId}`,
      run: () => selectOwner(ownerId),
      neighbors: {
        up: index === 0 ? "back" : `owner-${GEAR_OWNER_ORDER[index - 1]}`,
        down: index === GEAR_OWNER_ORDER.length - 1 ? previewEntryId : `owner-${GEAR_OWNER_ORDER[index + 1]}`,
        right: `slot-${selectedSlotId}`,
      },
    })),
    ...GEAR_SLOT_ORDER.map((slotId, index) => ({
      id: `slot-${slotId}`,
      run: () => selectSlot(slotId),
      neighbors: {
        up: index === 0 ? "workshop-upgrade" : `slot-${GEAR_SLOT_ORDER[index - 1]}`,
        down: index === GEAR_SLOT_ORDER.length - 1 ? previewEntryId : `slot-${GEAR_SLOT_ORDER[index + 1]}`,
        left: `owner-${selectedOwnerId}`,
        right: previewEntryId,
      },
    })),
    { id: "workshop-upgrade", run: () => selectWorkshopMode("upgrade"), neighbors: { right: "workshop-infusion", down: "slot-weapon" } },
    { id: "workshop-infusion", run: () => selectWorkshopMode("infusion"), neighbors: { left: "workshop-upgrade", down: `infusion-${infusionChoices[0].id}` } },
    ...infusionChoices.map((infusion, index) => ({
      id: `infusion-${infusion.id}`,
      run: () => selectInfusion(infusion.id),
      neighbors: {
        up: index === 0 ? "workshop-infusion" : `infusion-${infusionChoices[index - 1].id}`,
        down: index === infusionChoices.length - 1 ? (healerOwner ? `passive-ability-${selectedPassiveAbilityId}` : "install-infusion") : `infusion-${infusionChoices[index + 1].id}`,
        left: `slot-${selectedSlotId}`,
        right: index === infusionChoices.length - 1 ? "install-infusion" : undefined,
      },
    })),
    ...(healerOwner ? ABILITY_ORDER.map((abilityId, index) => ({
      id: `passive-ability-${abilityId}`,
      run: () => selectPassiveAbility(abilityId),
      neighbors: {
        up: index < 3 ? `infusion-${infusionChoices[infusionChoices.length - 1].id}` : `passive-ability-${ABILITY_ORDER[index - 3]}`,
        down: index < 3
          ? `passive-ability-${ABILITY_ORDER[index + 3]}`
          : `passive-${passiveChoices[Math.min(index - 3, passiveChoices.length - 1)].id}`,
        left: index % 3 > 0 ? `passive-ability-${ABILITY_ORDER[index - 1]}` : `slot-${selectedSlotId}`,
        right: index % 3 < 2 ? `passive-ability-${ABILITY_ORDER[index + 1]}` : undefined,
      },
    })) : []),
    ...(healerOwner ? passiveChoices.map((passive, index) => ({
      id: `passive-${passive.id}`,
      run: () => {
        selectPassiveInfusion(passive.id);
        installPassive(passive.id);
      },
      enabled: passiveUnlocked,
      neighbors: {
        up: index === 0 ? `passive-ability-${selectedPassiveAbilityId}` : `passive-${passiveChoices[index - 1].id}`,
        down: index === passiveChoices.length - 1 ? `passive-ability-${selectedPassiveAbilityId}` : `passive-${passiveChoices[index + 1].id}`,
        left: `slot-${selectedSlotId}`,
      },
    })) : []),
    { id: "upgrade", run: upgrade, enabled: canUpgrade, neighbors: { left: `slot-${selectedSlotId}`, up: `slot-${selectedSlotId}` } },
    { id: "install-infusion", run: installInfusion, enabled: canInstallInfusion, neighbors: { left: `slot-${selectedSlotId}`, up: `infusion-${infusionChoices[infusionChoices.length - 1].id}`, down: healerOwner ? `passive-ability-${selectedPassiveAbilityId}` : undefined } },
    { id: "back", run: () => navigate("home"), neighbors: { left: "workshop-infusion", down: `owner-${GEAR_OWNER_ORDER[0]}` } },
  ], [canInstallInfusion, canUpgrade, healerOwner, infusionChoices, installInfusion, installPassive, navigate, passiveChoices, passiveUnlocked, previewEntryId, selectInfusion, selectOwner, selectPassiveAbility, selectPassiveInfusion, selectSlot, selectWorkshopMode, selectedOwnerId, selectedPassiveAbilityId, selectedSlotId, upgrade]);
  const controller = useMenuController(actions, { onBack: () => navigate("home") });
  const passiveContext = workshopMode === "infusion" && healerOwner && controller.focusedId.startsWith("passive-");
  if (!hunter || !slot) return null;
  const currentBonus = gearBonusText(recipe.statId, slot.level);
  const nextBonus = gearBonusText(recipe.statId, Math.min(MAX_GEAR_LEVEL, slot.level + 1));

  return (
    <DualDisplayFrame
      top={
        <FrontSurface className="gear-surface" ariaLabel="Gear upgrade workshop">
          <header className="front-screen-header"><BrandMark compact /><div><span>Group drop workshop</span><h1>Gear & Infusions</h1></div><div className="gear-mode-tabs"><FocusButton id="workshop-upgrade" focusedId={controller.focusedId} focus={controller.focus} className={workshopMode === "upgrade" ? "is-selected" : ""} onClick={() => selectWorkshopMode("upgrade")}>Upgrade</FocusButton><FocusButton id="workshop-infusion" focusedId={controller.focusedId} focus={controller.focus} className={workshopMode === "infusion" ? "is-selected" : ""} onClick={() => selectWorkshopMode("infusion")}>Infusion</FocusButton></div><FocusButton id="back" focusedId={controller.focusedId} focus={controller.focus} className="header-back" onClick={() => navigate("home")}>{DEFAULT_CONTROLLER_GLYPHS.back} · Back</FocusButton></header>
          <div className="gear-workshop-layout">
            <section className="gear-owner-list" aria-label="Party gear owners">
              {GEAR_OWNER_ORDER.map((ownerId) => {
                const highest = Math.max(...GEAR_SLOT_ORDER.map((slotId) => hunter.gearProgress[ownerId].slots[slotId].level));
                const upgradeReady = upgradeReadiness.owners.has(ownerId);
                return <FocusButton key={ownerId} id={`owner-${ownerId}`} focusedId={controller.focusedId} focus={controller.focus} aria-label={`${GEAR_OWNER_LABELS[ownerId]}${upgradeReady ? ", upgrade available" : ""}`} className={`${ownerId === selectedOwnerId ? "is-selected" : ""} ${upgradeReady ? "is-upgrade-ready" : ""}`} onClick={() => selectOwner(ownerId)}><span><strong>{GEAR_OWNER_LABELS[ownerId]}</strong><small>Highest slot +{highest}</small></span><b>{ownerId === selectedOwnerId ? "✓" : ""}</b></FocusButton>;
              })}
            </section>
            <section className="gear-slot-list" aria-label={`${GEAR_OWNER_LABELS[selectedOwnerId]} gear slots`}>
              {GEAR_SLOT_ORDER.map((slotId) => {
                const progress = hunter.gearProgress[selectedOwnerId].slots[slotId];
                const slotRecipe = GEAR_RECIPES[selectedOwnerId][slotId];
                const upgradeReady = upgradeReadiness.slots.has(`${selectedOwnerId}:${slotId}`);
                return <FocusButton key={slotId} id={`slot-${slotId}`} focusedId={controller.focusedId} focus={controller.focus} aria-label={`${GEAR_SLOT_LABELS[slotId]} +${progress.level}${upgradeReady ? ", upgrade available" : ""}`} className={`${slotId === selectedSlotId ? "is-selected" : ""} ${upgradeReady ? "is-upgrade-ready" : ""}`} onClick={() => selectSlot(slotId)}><i>{slotId === "weapon" ? "⚔" : slotId === "helmet" ? "♙" : slotId === "chest" ? "▧" : slotId === "legs" ? "Ⅱ" : "⌁"}</i><span><strong>{GEAR_SLOT_LABELS[slotId]}</strong><small>{GEAR_STAT_LABELS[slotRecipe.statId]}</small></span><b>+{progress.level}</b></FocusButton>;
              })}
            </section>
            {workshopMode === "upgrade" ? <article className="gear-preview">
              <span>Selected upgrade</span>
              <h2>{GEAR_OWNER_LABELS[selectedOwnerId]} · {GEAR_SLOT_LABELS[selectedSlotId]} +{slot.level}</h2>
              <p>{GEAR_STAT_LABELS[recipe.statId]} from Group {BOSS_GROUP_BY_ID[recipe.primaryGroupId].letter} and Group {BOSS_GROUP_BY_ID[recipe.secondaryGroupId].letter} drops.</p>
              <div className="gear-stat-comparison"><span><small>Current</small><strong>{currentBonus}</strong></span><i>→</i><span><small>{slot.level >= MAX_GEAR_LEVEL ? "Maximum" : `Rank +${slot.level + 1}`}</small><strong>{nextBonus}</strong></span></div>
            </article> : <article className="gear-preview gear-infusion-preview">
              <span>Active infusion · unlock +{ACTIVE_INFUSION_MIN_GEAR_LEVEL}</span>
              <h2>{selectedInfusion.icon} {selectedInfusion.name}</h2>
              <p>{selectedInfusion.description} Anchor purchase to a +{ACTIVE_INFUSION_MIN_GEAR_LEVEL} slot.</p>
              <div className="gear-infusion-options">
                {infusionChoices.map((infusion) => <FocusButton key={infusion.id} id={`infusion-${infusion.id}`} focusedId={controller.focusedId} focus={controller.focus} className={`${infusion.id === selectedInfusion.id ? "is-selected" : ""} ${hunter.gearProgress[selectedOwnerId].infusionAbilityId === infusion.id ? "is-equipped" : ""}`} onClick={() => selectInfusion(infusion.id)}><i>{infusion.icon}</i><span><strong>{infusion.name}</strong><small>{infusion.description}</small></span><b>{hunter.gearProgress[selectedOwnerId].infusionAbilityId === infusion.id ? "✓" : ""}</b></FocusButton>)}
              </div>
              {healerOwner && <div className="gear-passive-options">
                <span>Passive blessing · global +{PASSIVE_INFUSION_MIN_GEAR_LEVEL}</span>
                <div className="gear-passive-ability-filter">
                  {ABILITY_ORDER.map((abilityId) => <FocusButton key={abilityId} id={`passive-ability-${abilityId}`} focusedId={controller.focusedId} focus={controller.focus} className={selectedPassiveAbilityId === abilityId ? "is-selected" : ""} onClick={() => selectPassiveAbility(abilityId)}>{healerAbilities[abilityId].shortName}</FocusButton>)}
                </div>
                <div className="gear-passive-choice-list">
                  {passiveChoices.map((passive) => <FocusButton
                    key={passive.id}
                    id={`passive-${passive.id}`}
                    focusedId={controller.focusedId}
                    focus={controller.focus}
                    disabled={!passiveUnlocked}
                    className={`${selectedPassiveInfusionId === passive.id ? "is-selected" : ""} ${hunter.gearProgress[selectedOwnerId].passiveInfusionId === passive.id ? "is-equipped" : ""}`}
                    onFocus={() => selectPassiveInfusion(passive.id)}
                    onPointerEnter={() => selectPassiveInfusion(passive.id)}
                    onClick={() => { selectPassiveInfusion(passive.id); installPassive(passive.id); }}
                  ><i>{passive.icon}</i><span><strong>{healerAbilities[passive.abilityId].shortName}: {passive.name}</strong><small>{formatRunBuffEffect(passive.id, 1)}</small></span><b>{hunter.gearProgress[selectedOwnerId].passiveInfusionId === passive.id ? "✓" : ""}</b></FocusButton>)}
                </div>
              </div>}
            </article>}
          </div>
          <ControllerLegend back />
        </FrontSurface>
      }
      bottom={
        <FrontSurface className="gear-context" bottom ariaLabel="Gear recipe and material inventory">
          <header className="context-header"><span>{workshopMode === "upgrade" ? `${GEAR_SLOT_LABELS[selectedSlotId]} recipe` : passiveContext ? `${healerAbilities[selectedPassive.abilityId].name}: ${selectedPassive.name}` : `${selectedInfusion.name} infusion`}</span><b>{hunter.materials.reduce((sum, item) => sum + item.quantity, 0)} DROPS</b></header>
          <div className="gear-costs">
            <span>{workshopMode === "upgrade" ? "Upgrade requirements" : passiveContext ? "Passive blessing · Rank 1" : `Infusion requirements · ${GEAR_SLOT_LABELS[selectedSlotId]} +${slot.level} anchor`}</span>
            {passiveContext ? <article className={passiveUnlocked ? "is-met" : "is-missing"}><i>{passiveUnlocked ? "✓" : "×"}</i><span><strong>{formatRunBuffEffect(selectedPassive.id, 1)}</strong><small>{selectedPassive.detail}</small></span><b>{hunter.gearProgress[selectedOwnerId].passiveInfusionId === selectedPassive.id ? "EQUIPPED" : "RANK 1"}</b></article> : (workshopMode === "upgrade" ? costs : selectedInfusionCosts).length ? (workshopMode === "upgrade" ? costs : selectedInfusionCosts).map((cost) => {
              const owned = hunter.materials.find((item) => item.id === cost.itemId)?.quantity ?? 0;
              return <article className={owned >= cost.quantity ? "is-met" : "is-missing"} key={cost.itemId}><i>{owned >= cost.quantity ? "✓" : "×"}</i><span><strong>{cost.itemName}</strong><small>{owned} owned · {cost.quantity} needed</small></span><b>{owned}/{cost.quantity}</b></article>;
            }) : <article className="is-met"><i>✓</i><span><strong>Maximum rank reached</strong><small>No more materials required.</small></span><b>+{MAX_GEAR_LEVEL}</b></article>}
          </div>
          {workshopMode === "upgrade" ? <FocusButton id="upgrade" focusedId={controller.focusedId} focus={controller.focus} className="gear-upgrade-action" disabled={!canUpgrade} onClick={upgrade}><span>{slot.level >= MAX_GEAR_LEVEL ? "Maximum rank" : `Upgrade to +${slot.level + 1}`}</span><small>{canUpgrade ? "Spend group drops · autosave" : "Collect required group drops"}</small></FocusButton> : passiveContext ? <div className="gear-passive-context-action"><span>{hunter.gearProgress[selectedOwnerId].passiveInfusionId === selectedPassive.id ? "Passive equipped" : `${DEFAULT_CONTROLLER_GLYPHS.confirm} · Equip selected passive`}</span><small>Applies at rank 1 next encounter.</small></div> : <FocusButton id="install-infusion" focusedId={controller.focusedId} focus={controller.focus} className="gear-upgrade-action" disabled={!canInstallInfusion} onClick={installInfusion}><span>{infusionEquipped ? "Infusion equipped" : `Install ${selectedInfusion.name}`}</span><small>{!activeUnlocked ? `Raise any ${GEAR_OWNER_LABELS[selectedOwnerId]} slot to +${ACTIVE_INFUSION_MIN_GEAR_LEVEL}` : !anchorUnlocked ? `Select a +${ACTIVE_INFUSION_MIN_GEAR_LEVEL} anchor slot` : canInstallInfusion ? "Spend group drops · autosave" : infusionEquipped ? "Applies next encounter" : "Collect required group drops"}</small></FocusButton>}
          <div className="front-notice is-lower">{notice || "Gear changes save locally and apply when next encounter starts."}</div>
        </FrontSurface>
      }
    />
  );
}

function SettingToggle({ id, label, copy, value, focusedId, focus, onClick }: { id: string; label: string; copy: string; value: boolean; focusedId: string; focus: (id: string) => void; onClick: () => void }) {
  return <FocusButton id={id} focusedId={focusedId} focus={focus} className="setting-row" onClick={onClick}><span><strong>{label}</strong><small>{copy}</small></span><b className={value ? "is-on" : ""}>{value ? "ON" : "OFF"}</b></FocusButton>;
}

function SettingsScreen() {
  const settings = useFrontendStore((state) => state.settings);
  const updateSetting = useFrontendStore((state) => state.updateSetting);
  const navigate = useFrontendStore((state) => state.navigate);
  const notice = useFrontendStore((state) => state.notice);
  const actions = useMemo<MenuAction[]>(() => [
    { id: "volume-down", run: () => updateSetting("masterVolume", Math.max(0, settings.masterVolume - 10)) },
    { id: "volume-up", run: () => updateSetting("masterVolume", Math.min(100, settings.masterVolume + 10)) },
    { id: "motion", run: () => updateSetting("reducedMotion", !settings.reducedMotion) },
    { id: "numbers", run: () => updateSetting("damageNumbers", !settings.damageNumbers) },
    { id: "text", run: () => updateSetting("largeText", !settings.largeText) },
    { id: "back", run: () => navigate("home") },
  ], [navigate, settings, updateSetting]);
  const controller = useMenuController(actions, { onBack: () => navigate("home") });

  return (
    <DualDisplayFrame
      top={
        <FrontSurface className="settings-surface" ariaLabel="Settings">
          <header className="front-screen-header"><BrandMark compact /><div><span>Field configuration</span><h1>Settings</h1></div><FocusButton id="back" focusedId={controller.focusedId} focus={controller.focus} className="header-back" onClick={() => navigate("home")}>{DEFAULT_CONTROLLER_GLYPHS.back} · Back</FocusButton></header>
          <div className="settings-layout">
            <section><span className="settings-section-title">Audio</span><div className="volume-setting"><span><strong>Master volume</strong><small>All music, effects, and voice</small></span><div><FocusButton id="volume-down" focusedId={controller.focusedId} focus={controller.focus} onClick={() => updateSetting("masterVolume", Math.max(0, settings.masterVolume - 10))}>−</FocusButton><b>{settings.masterVolume}%</b><FocusButton id="volume-up" focusedId={controller.focusedId} focus={controller.focus} onClick={() => updateSetting("masterVolume", Math.min(100, settings.masterVolume + 10))}>＋</FocusButton></div><i><em style={{ width: `${settings.masterVolume}%` }} /></i></div></section>
            <section><span className="settings-section-title">Display & accessibility</span><SettingToggle id="motion" label="Reduced motion" copy="Limit non-essential UI movement" value={settings.reducedMotion} focusedId={controller.focusedId} focus={controller.focus} onClick={() => updateSetting("reducedMotion", !settings.reducedMotion)} /><SettingToggle id="numbers" label="Damage numbers" copy="Show combat values over units" value={settings.damageNumbers} focusedId={controller.focusedId} focus={controller.focus} onClick={() => updateSetting("damageNumbers", !settings.damageNumbers)} /><SettingToggle id="text" label="Large interface text" copy="Increase menu and tactical labels" value={settings.largeText} focusedId={controller.focusedId} focus={controller.focus} onClick={() => updateSetting("largeText", !settings.largeText)} /></section>
          </div>
          <div className="settings-save-state">{notice || "Settings write to offline storage immediately."}</div>
        </FrontSurface>
      }
      bottom={
        <FrontSurface className="controls-context" bottom ariaLabel="Controller mapping">
          <header className="context-header"><span>Controller</span><b>BUILT-IN THOR PAD</b></header>
          <div className="controller-map">
            <div className="pad-diagram"><i>↑</i><span>←<b>●</b>→</span><i>↓</i></div>
            <div className="face-diagram"><i className="triangle">{DEFAULT_CONTROLLER_GLYPHS.faceTop}</i><span><i className="square">{DEFAULT_CONTROLLER_GLYPHS.faceLeft}</i><b>●</b><i className="circle">{DEFAULT_CONTROLLER_GLYPHS.faceRight}</i></span><i className="cross">{DEFAULT_CONTROLLER_GLYPHS.faceBottom}</i></div>
          </div>
          <div className="mapping-list"><span><b>{DEFAULT_CONTROLLER_GLYPHS.confirm}</b> Confirm / cast Purify</span><span><b>{DEFAULT_CONTROLLER_GLYPHS.back}</b> Back / cast Shield</span><span><b>D-Pad</b> Navigate / target party</span><span><b>Right stick</b> Rotate camera</span><span><b>{DEFAULT_CONTROLLER_GLYPHS.start}</b> Pause / menu</span></div>
          <div className="control-assurance"><i>✓</i><span><strong>No click-to-focus required</strong><small>Controller input routes through app-level actions.</small></span></div>
        </FrontSurface>
      }
    />
  );
}

function ModeScreen({ onLaunch }: { onLaunch: (bossIds: readonly BossId[], difficultySlug?: (typeof DIFFICULTIES)[number]["slug"]) => void }) {
  const hunter = useActiveHunter();
  const modeId = useFrontendStore((state) => state.selectedMode);
  const selectedBossId = useFrontendStore((state) => state.selectedBossId);
  const selectedDifficultySlug = useFrontendStore((state) => state.selectedDifficultySlug);
  const selectBoss = useFrontendStore((state) => state.selectBoss);
  const selectDifficulty = useFrontendStore((state) => state.selectDifficulty);
  const navigate = useFrontendStore((state) => state.navigate);
  const [message, setMessage] = useState("");
  const mode = MODE_COPY[modeId];
  const healer = hunter ? HEALER_CLASSES[hunter.activeClassId] : HEALER_CLASSES.priest;
  const progress = hunter?.healers[hunter.activeClassId];
  const selectedBoss = BOSS_DEFINITIONS[selectedBossId];
  const selectedDifficulty = DIFFICULTY_BY_SLUG[selectedDifficultySlug];
  const isPve = modeId === "roguelike-pve";
  const isRogueTrials = modeId === "rogue-trials";
  const isPveRun = isPve || isRogueTrials;
  const isDungeon = modeId === "dungeons";
  const selectedBossGroup = BOSS_GROUP_BY_ID[selectedBoss.groupId];
  const visibleBossIds = selectedBossGroup.bossIds;
  const bossGridColumns = Math.min(2, visibleBossIds.length);
  const selectBossGroup = (groupId: (typeof BOSS_GROUPS)[number]["id"]) => {
    selectBoss(BOSS_GROUP_BY_ID[groupId].bossIds[0]);
  };
  const launch = () => {
    if (isPveRun) return onLaunch(selectRandomBossPair(), "initiate");
    if (isDungeon) return onLaunch([selectedBossId], selectedDifficultySlug);
    setMessage("Online matchmaking is not available for this mode yet.");
  };
  const actions = useMemo<MenuAction[]>(() => [
    ...(isDungeon ? BOSS_GROUPS.map((group, index) => {
      const groupColumns = 5;
      const row = Math.floor(index / groupColumns);
      const column = index % groupColumns;
      const groupAt = (targetRow: number, targetColumn: number) => BOSS_GROUPS[targetRow * groupColumns + targetColumn];

      return {
        id: `boss-group-${group.id}`,
        run: () => selectBossGroup(group.id),
        neighbors: {
          up: row > 0 ? `boss-group-${groupAt(row - 1, column)?.id}` : "back",
          down: groupAt(row + 1, column)
            ? `boss-group-${groupAt(row + 1, column)?.id}`
            : group.id === selectedBossGroup.id ? `boss-${selectedBossGroup.bossIds[0]}` : undefined,
          left: column > 0 ? `boss-group-${groupAt(row, column - 1)?.id}` : undefined,
          right: groupAt(row, column + 1) ? `boss-group-${groupAt(row, column + 1)?.id}` : undefined,
        },
      };
    }) : []),
    ...(isDungeon ? visibleBossIds.map((bossId, index) => {
      const row = Math.floor(index / bossGridColumns);
      const column = index % bossGridColumns;
      const bossAt = (targetRow: number, targetColumn: number) => visibleBossIds[targetRow * bossGridColumns + targetColumn];

      return {
        id: `boss-${bossId}`,
        run: () => selectBoss(bossId),
        neighbors: {
          up: row > 0 ? `boss-${bossAt(row - 1, column)}` : `boss-group-${selectedBossGroup.id}`,
          down: bossAt(row + 1, column) ? `boss-${bossAt(row + 1, column)}` : `difficulty-${DIFFICULTIES[0].slug}`,
          left: column > 0 ? `boss-${bossAt(row, column - 1)}` : undefined,
          right: bossAt(row, column + 1) ? `boss-${bossAt(row, column + 1)}` : undefined,
        },
      };
    }) : []),
    ...(isDungeon ? DIFFICULTIES.map((difficulty, index) => ({
      id: `difficulty-${difficulty.slug}`,
      run: () => selectDifficulty(difficulty.slug),
      neighbors: {
        left: index > 0 ? `difficulty-${DIFFICULTIES[index - 1].slug}` : `boss-${selectedBossId}`,
        right: index < DIFFICULTIES.length - 1 ? `difficulty-${DIFFICULTIES[index + 1].slug}` : "launch",
        up: `boss-${selectedBossId}`,
        down: "launch",
      },
    })) : []),
    { id: "launch", run: launch, neighbors: isDungeon ? { up: `difficulty-${DIFFICULTIES[DIFFICULTIES.length - 1].slug}` } : { up: "back" } },
    { id: "back", run: () => navigate("home"), neighbors: isDungeon ? { down: `boss-group-${selectedBossGroup.id}` } : { down: "launch" } },
  ], [bossGridColumns, isDungeon, isPveRun, modeId, navigate, onLaunch, selectBoss, selectDifficulty, selectedBossGroup, selectedBossId, selectedDifficultySlug, visibleBossIds]);
  const controller = useMenuController(actions, { onBack: () => navigate("home") });
  const launchLabel = isRogueTrials ? "Begin Rogue Trials" : isPve ? "Begin randomized run" : isDungeon ? `Challenge ${selectedBoss.name}` : "Enter matchmaking";
  const contextRules = isDungeon
    ? [
      [selectedBoss.name, selectedBoss.summary],
      [bossMechanicName(selectedBoss.mechanicIds[0]), selectedBoss.briefing],
      [bossMechanicName(selectedBoss.mechanicIds[1]), "Controller-ready party behavior and full lower-display support."],
    ]
    : isRogueTrials
      ? [
        ["Four dual rounds", "Clear four randomized pairs while drafting one stacking buff after each win."],
        ["Unseen trio finale", "Round 5 selects three bosses that have not appeared earlier in that run."],
        ["Endless choice", "After the trio falls, quit with the clear or continue while every dead boss is replaced."],
      ]
    : isPve
      ? [
        ["Randomized pair", "Two distinct bosses are selected only when the run begins."],
        ["Dual-boss pressure", "Both guardians fight simultaneously and must be defeated."],
        ["Buff intermission", "Choose one of three stacking buffs after every cleared round."],
      ]
      : [
        ["Draft a healing path", "Choose rites after every completed room."],
        ["Protect the formation", "Boss pressure changes around your build."],
        ["Bank collection drops", "Earned boss loot writes to active offline save."],
      ];
  return (
    <DualDisplayFrame
      top={
        <FrontSurface className={`mode-surface mode-${modeId}`} ariaLabel={`${mode.title} details`}>
          <header className="front-screen-header"><BrandMark compact /><div><span>Game mode</span><h1>{mode.title}</h1></div><FocusButton id="back" focusedId={controller.focusedId} focus={controller.focus} className="header-back" onClick={() => navigate("home")}>{DEFAULT_CONTROLLER_GLYPHS.back} · Back</FocusButton></header>
          {!isDungeon && <div className="mode-hero"><span>{mode.eyebrow}</span><h2>{mode.title}</h2><p>{mode.description}</p><b>{mode.detail}</b></div>}
          {isDungeon && (
            <div className="boss-picker" aria-label="Choose boss encounter">
              <div className="boss-picker-heading"><span>Choose a mechanic group</span></div>
              <div className="boss-group-grid" aria-label="Choose boss group">
                {BOSS_GROUPS.map((group) => (
                  <FocusButton
                    key={group.id}
                    id={`boss-group-${group.id}`}
                    focusedId={controller.focusedId}
                    focus={controller.focus}
                    className={`boss-group-choice ${group.id === selectedBossGroup.id ? "is-selected" : ""}`}
                    aria-pressed={group.id === selectedBossGroup.id}
                    onClick={() => selectBossGroup(group.id)}
                  >
                    <b>{group.letter}</b><span><strong>Group {group.letter}</strong><small>{group.name}</small></span>
                  </FocusButton>
                ))}
              </div>
              <div className="boss-group-heading"><span>Group {selectedBossGroup.letter} · {selectedBossGroup.name}</span><small>{selectedBossGroup.coreMechanic} mechanics · {visibleBossIds.length} guardians</small></div>
              <div className="boss-choice-grid">
                {visibleBossIds.map((bossId) => {
                  const boss = BOSS_DEFINITIONS[bossId];
                  return (
                    <FocusButton
                      key={bossId}
                      id={`boss-${bossId}`}
                      focusedId={controller.focusedId}
                      focus={controller.focus}
                      className={`boss-choice ${selectedBossId === bossId ? "is-selected" : ""}`}
                      style={{ "--boss-accent": boss.accent } as React.CSSProperties}
                      aria-pressed={selectedBossId === bossId}
                      onClick={() => selectBoss(bossId)}
                    >
                      <i>{boss.icon}</i><span><strong>{boss.name}</strong><small>{boss.mechanicIds.filter((id) => !bossMechanicIsPassive(id)).map(bossMechanicName).join(" · ")}</small></span><b>{selectedBossId === bossId ? "✓" : ""}</b>
                    </FocusButton>
                  );
                })}
              </div>
            </div>
          )}
          {isDungeon && (
            <div className="difficulty-picker" aria-label="Choose encounter difficulty">
              <span>Difficulty</span>
              {DIFFICULTIES.map((difficulty) => <FocusButton key={difficulty.slug} id={`difficulty-${difficulty.slug}`} focusedId={controller.focusedId} focus={controller.focus} className={difficulty.slug === selectedDifficultySlug ? "is-selected" : ""} onClick={() => selectDifficulty(difficulty.slug)}><strong>{difficulty.name}</strong><small>iLvl {difficulty.itemLevel}</small></FocusButton>)}
            </div>
          )}
          <FocusButton id="launch" focusedId={controller.focusedId} focus={controller.focus} className="mode-launch" onClick={launch}><span>{launchLabel}</span><small>{mode.status} · {DEFAULT_CONTROLLER_GLYPHS.confirm}</small></FocusButton>
          {message && <div className="front-notice">{message}</div>}
        </FrontSurface>
      }
      bottom={
        <FrontSurface className="mode-context" bottom ariaLabel={`${mode.title} preparation`}>
          <header className="context-header"><span>Run preparation</span><b>{isDungeon ? selectedDifficulty.name.toUpperCase() : mode.status.toUpperCase()}</b></header>
          {contextRules.map(([title, copy], index) => <div className="mode-rule" key={title}><i>0{index + 1}</i><span><strong>{title}</strong><small>{copy}</small></span></div>)}
          <div className="mode-loadout"><span>Equipped role</span><b>{healer.specialization} · Level {progress?.level ?? 1}</b><small>6 abilities · {progress?.inventory.length ?? 0} class items · Controller ready</small></div>
          {isDungeon && <div className="mode-loot-preview"><span>Guaranteed reward</span><b>{bossGroupDrop(selectedBossId, selectedDifficultySlug).name}</b><small>1–3 group drops · {selectedDifficulty.rarity} · Pet chance 1 in 500</small></div>}
        </FrontSurface>
      }
    />
  );
}

export function FrontEnd({ onLaunch }: { onLaunch: (bossIds: readonly BossId[], difficultySlug?: (typeof DIFFICULTIES)[number]["slug"]) => void }) {
  const screen = useFrontendStore((state) => state.screen);
  if (screen === "login") return <LoginScreen />;
  if (screen === "saves") return <SaveScreen />;
  if (screen === "home") return <HomeScreen />;
  if (screen === "profile") return <ProfileScreen />;
  if (screen === "gear") return <GearScreen />;
  if (screen === "settings") return <SettingsScreen />;
  if (screen === "mode") return <ModeScreen onLaunch={onLaunch} />;
  return null;
}
