// MobileApp — touch-first interface mirroring all four worlds + diegetic terminal.
// Loaded BEFORE App.jsx; App.jsx routes mobile UAs / coarse pointers here.
// Reuses globals from data.jsx (WORLDS, ABOUT, PROJECTS, TIMELINE),
// Personal.jsx (BRANCHES), Creative.jsx (TRACKS, A2_ESSAYS, B1_NOTES, B2_LIVE).

const { useState: useStateM, useEffect: useEffectM, useRef: useRefM, useCallback: useCallbackM } = React;

const HUE_BY_WORLD = { projects: 30, professional: 70, creative: 200, personal: 260 };
const hueAccent = (h, l = 80) => `oklch(${l}% 0.14 ${h})`;

// ─────────────────────────────────────────────────────────────────────────────
// Styles
// ─────────────────────────────────────────────────────────────────────────────
const mStyles = {
  root: {
    position: "fixed", inset: 0,
    display: "flex", flexDirection: "column",
    background: "transparent",
    color: "var(--fg)",
    fontFamily: "var(--serif)",
  },
  topbar: {
    position: "sticky", top: 0, zIndex: 40,
    display: "flex", alignItems: "center", justifyContent: "space-between",
    padding: "14px 16px 10px",
    background: "linear-gradient(to bottom, oklch(13% 0.012 60 / 0.94) 60%, oklch(13% 0.012 60 / 0) 100%)",
    backdropFilter: "blur(6px)",
    WebkitBackdropFilter: "blur(6px)",
  },
  crumb: {
    fontFamily: "var(--mono)",
    fontSize: 11, letterSpacing: ".18em",
    color: "var(--muted)",
    textTransform: "uppercase",
    overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
    flex: 1, paddingRight: 12,
  },
  chip: {
    fontFamily: "var(--mono)",
    fontSize: 11, letterSpacing: ".22em", textTransform: "uppercase",
    color: "var(--fg-2)",
    background: "oklch(11% 0.012 60 / 0.7)",
    border: "1px solid var(--line)",
    padding: "8px 12px",
    borderRadius: 2,
    cursor: "pointer",
    WebkitTapHighlightColor: "transparent",
  },
  scroll: {
    flex: 1,
    overflowY: "auto",
    overflowX: "hidden",
    WebkitOverflowScrolling: "touch",
    paddingBottom: "calc(var(--terminal-h) + 24px)",
  },
  page: { padding: "8px 18px 24px" },
  h1: {
    fontFamily: "var(--serif)",
    fontSize: 36, lineHeight: 1.05,
    margin: "10px 0 4px",
    color: "var(--fg)",
    fontStyle: "italic",
  },
  h2: {
    fontFamily: "var(--serif)",
    fontSize: 26, lineHeight: 1.1,
    margin: "18px 0 6px",
    color: "var(--fg)",
  },
  sub: {
    fontFamily: "var(--mono)",
    fontSize: 10, letterSpacing: ".24em",
    textTransform: "uppercase",
    color: "var(--muted)",
    margin: "0 0 14px",
  },
  para: {
    fontFamily: "var(--serif)",
    fontSize: 17, lineHeight: 1.55,
    color: "var(--fg-2)",
    margin: "0 0 14px",
  },
  meta: {
    fontFamily: "var(--mono)",
    fontSize: 10, letterSpacing: ".24em",
    textTransform: "uppercase",
    color: "var(--dim)",
  },
  card: {
    display: "block", width: "100%",
    textAlign: "left",
    background: "oklch(15% 0.013 60 / 0.6)",
    border: "1px solid var(--line)",
    borderLeftWidth: 3,
    padding: "16px 16px 14px",
    margin: "10px 0",
    borderRadius: 2,
    cursor: "pointer",
    color: "inherit",
    WebkitTapHighlightColor: "transparent",
    font: "inherit",
  },
  cardN: {
    fontFamily: "var(--mono)",
    fontSize: 10, letterSpacing: ".24em",
    color: "var(--amber)",
    marginBottom: 6,
  },
  cardTitle: {
    fontFamily: "var(--serif)",
    fontSize: 24, lineHeight: 1.05,
    color: "var(--fg)",
    margin: 0,
    fontStyle: "italic",
  },
  cardSub: {
    fontFamily: "var(--mono)",
    fontSize: 10, letterSpacing: ".22em",
    textTransform: "uppercase",
    color: "var(--muted)",
    margin: "4px 0 10px",
  },
  cardBlurb: {
    fontFamily: "var(--serif)",
    fontSize: 15, lineHeight: 1.5,
    color: "var(--fg-2)",
    margin: 0,
  },
  rule: { border: 0, height: 1, background: "var(--line)", margin: "18px 0" },
  pillRow: {
    display: "flex", flexWrap: "wrap", gap: 6, margin: "8px 0 14px",
  },
  pill: {
    fontFamily: "var(--mono)",
    fontSize: 10, letterSpacing: ".18em",
    color: "var(--fg-2)",
    background: "oklch(20% 0.014 60)",
    border: "1px solid var(--line)",
    padding: "4px 8px",
    borderRadius: 2,
  },
  list: { margin: "6px 0 14px", padding: 0, listStyle: "none" },
  listItem: {
    fontFamily: "var(--serif)",
    fontSize: 16, lineHeight: 1.45,
    color: "var(--fg-2)",
    padding: "8px 0",
    borderBottom: "1px dashed var(--line)",
    display: "flex", gap: 10,
  },
  bullet: { color: "var(--amber)", fontFamily: "var(--mono)", fontSize: 12, paddingTop: 3 },
  status: {
    display: "inline-block",
    fontFamily: "var(--mono)",
    fontSize: 10, letterSpacing: ".22em",
    padding: "3px 8px",
    border: "1px solid var(--amber)",
    color: "var(--amber)",
    borderRadius: 2,
    marginLeft: 6,
  },
  // ── timeline
  timelineWrap: { position: "relative", padding: "8px 0 0 22px" },
  timelineSpine: {
    position: "absolute", left: 9, top: 16, bottom: 0,
    width: 2, background: "linear-gradient(to bottom, var(--amber), var(--line) 90%)",
  },
  tNode: {
    position: "relative",
    margin: "0 0 22px",
  },
  tDot: {
    position: "absolute", left: -22, top: 8,
    width: 12, height: 12, borderRadius: 12,
    background: "var(--amber)",
    boxShadow: "0 0 0 3px oklch(13% 0.012 60), 0 0 14px var(--amber)",
  },
  tEra: {
    fontFamily: "var(--mono)", fontSize: 9, letterSpacing: ".3em",
    color: "var(--amber-2)", textTransform: "uppercase",
  },
  tTitle: {
    fontFamily: "var(--serif)", fontSize: 19, lineHeight: 1.2,
    color: "var(--fg)", margin: "2px 0 6px", fontStyle: "italic",
  },
  tBody: {
    fontFamily: "var(--serif)", fontSize: 15, lineHeight: 1.5,
    color: "var(--fg-2)", margin: 0,
  },
  link: {
    fontFamily: "var(--mono)", fontSize: 11, letterSpacing: ".18em",
    color: "var(--amber)", textDecoration: "none",
    borderBottom: "1px solid var(--amber)",
    paddingBottom: 2, marginTop: 6, display: "inline-block",
  },
  // ── terminal
  termRoot: {
    position: "fixed", left: 0, right: 0, bottom: 0,
    zIndex: 60,
    background: "linear-gradient(to top, oklch(10% 0.012 60) 0%, oklch(13% 0.012 60) 100%)",
    borderTop: "1px solid var(--line)",
    boxShadow: "0 -8px 24px oklch(0% 0 0 / 0.5)",
    transform: "translateZ(0)",
  },
  termStrip: {
    height: "var(--terminal-h)",
    padding: "0 14px",
    display: "flex", alignItems: "center", gap: 10,
  },
  termPrompt: {
    fontFamily: "var(--mono)", fontSize: 13, letterSpacing: ".06em",
    color: "var(--amber)",
  },
  termBreadcrumb: {
    fontFamily: "var(--mono)", fontSize: 11, letterSpacing: ".14em",
    color: "var(--fg-2)",
    flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
  },
  termToggle: {
    fontFamily: "var(--mono)", fontSize: 10, letterSpacing: ".22em",
    color: "var(--fg-2)",
    background: "oklch(18% 0.014 60)",
    border: "1px solid var(--line)",
    padding: "8px 12px",
    borderRadius: 2,
    cursor: "pointer",
    WebkitTapHighlightColor: "transparent",
  },
  termPanel: {
    position: "fixed", left: 0, right: 0, bottom: "var(--terminal-h)",
    height: "min(55vh, 460px)",
    zIndex: 59,
    display: "flex", flexDirection: "column",
    background: "oklch(10% 0.012 60 / 0.98)",
    borderTop: "1px solid var(--line)",
    backdropFilter: "blur(8px)",
    WebkitBackdropFilter: "blur(8px)",
    transition: "transform 220ms cubic-bezier(.3,.7,.2,1), opacity 200ms",
    transformOrigin: "bottom",
  },
  termLines: {
    flex: 1,
    overflowY: "auto",
    WebkitOverflowScrolling: "touch",
    padding: "12px 14px 6px",
    fontFamily: "var(--mono)", fontSize: 12, lineHeight: 1.55,
  },
  termLineCmd: { color: "var(--amber)" },
  termLineOut: { color: "var(--fg-2)" },
  termLineErr: { color: "var(--rust)" },
  termQuick: {
    display: "flex", gap: 6, padding: "6px 10px 8px",
    overflowX: "auto", borderTop: "1px solid var(--line)",
    WebkitOverflowScrolling: "touch",
  },
  termQuickChip: {
    fontFamily: "var(--mono)", fontSize: 10, letterSpacing: ".18em",
    color: "var(--fg-2)",
    background: "oklch(16% 0.014 60)",
    border: "1px solid var(--line)",
    padding: "6px 10px",
    borderRadius: 2,
    whiteSpace: "nowrap",
    cursor: "pointer",
    WebkitTapHighlightColor: "transparent",
    flex: "0 0 auto",
  },
  termInputRow: {
    display: "flex", alignItems: "center",
    padding: "8px 12px 10px",
    borderTop: "1px solid var(--line)",
    gap: 8,
  },
  termInputPrompt: {
    fontFamily: "var(--mono)", fontSize: 13, color: "var(--amber)",
  },
  termInput: {
    flex: 1,
    background: "transparent",
    border: 0,
    outline: 0,
    color: "var(--fg)",
    fontFamily: "var(--mono)", fontSize: 14,
    padding: "6px 0",
  },
  termSend: {
    fontFamily: "var(--mono)", fontSize: 10, letterSpacing: ".22em",
    color: "var(--amber)",
    background: "transparent",
    border: "1px solid var(--amber)",
    padding: "6px 10px",
    borderRadius: 2,
    cursor: "pointer",
    WebkitTapHighlightColor: "transparent",
  },
};

// ─────────────────────────────────────────────────────────────────────────────
// Top bar (back / breadcrumb / home)
// ─────────────────────────────────────────────────────────────────────────────
function MTopbar({ route, onBack, onHome }) {
  const crumbs = route.world === "home"
    ? ["~"]
    : route.detail
      ? ["~", route.world, route.detail]
      : ["~", route.world];
  const breadcrumb = crumbs.join(" / ");
  const showBack = route.world !== "home";
  return (
    <div style={mStyles.topbar}>
      {showBack ? (
        <button style={mStyles.chip} onClick={onBack}>← BACK</button>
      ) : (
        <div style={{ ...mStyles.chip, opacity: 0.35, pointerEvents: "none" }}>AD</div>
      )}
      <div style={mStyles.crumb}>{breadcrumb}</div>
      {showBack && <button style={mStyles.chip} onClick={onHome}>HOME</button>}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Home
// ─────────────────────────────────────────────────────────────────────────────
function MHome({ onSelect }) {
  const about = window.ABOUT;
  return (
    <div style={mStyles.page}>
      <div style={{ ...mStyles.meta, marginTop: 8 }}>PORTFOLIO.EXE · v1.0.0</div>
      <h1 style={mStyles.h1}>{about.name}</h1>
      <div style={mStyles.sub}>{about.tagline}</div>
      <p style={mStyles.para}>{about.bio}</p>
      <a
        href={`https://${about.linkedin}`}
        target="_blank" rel="noopener noreferrer"
        style={mStyles.link}
      >LINKEDIN →</a>

      <hr style={mStyles.rule} />
      <div style={mStyles.meta}>WORLDS · 04</div>

      {window.WORLDS.map(w => (
        <button
          key={w.id}
          onClick={() => onSelect(w.id)}
          style={{
            ...mStyles.card,
            borderLeftColor: hueAccent(w.hue, 70),
          }}
        >
          <div style={{ ...mStyles.cardN, color: hueAccent(w.hue) }}>
            {w.n} · {w.id.toUpperCase()}
          </div>
          <h3 style={mStyles.cardTitle}>{w.title}</h3>
          <div style={mStyles.cardSub}>{w.sub}</div>
          <p style={mStyles.cardBlurb}>{w.blurb}</p>
        </button>
      ))}

      <div style={{ ...mStyles.meta, marginTop: 22, textAlign: "center" }}>
        TAP A WORLD · OR OPEN THE TERMINAL BELOW
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Projects
// ─────────────────────────────────────────────────────────────────────────────
function MProjects({ detail, onSelect }) {
  const proj = detail ? window.PROJECTS.find(p => p.id === detail) : null;
  if (proj) return <MProjectDetail p={proj} />;
  return (
    <div style={mStyles.page}>
      <div style={mStyles.meta}>WORLD 01 · BLUEPRINTS</div>
      <h1 style={mStyles.h1}>Projects</h1>
      <p style={mStyles.para}>
        Engineering plates. Each one is a thing I've built — shipped, breaking,
        or quietly running on a server somewhere.
      </p>
      {window.PROJECTS.map(p => (
        <button
          key={p.id}
          onClick={() => onSelect(p.id)}
          style={{ ...mStyles.card, borderLeftColor: "var(--amber)" }}
        >
          <div style={mStyles.cardN}>
            {p.part} · PLATE {p.plate}
            <span style={mStyles.status}>{p.status}</span>
          </div>
          <h3 style={mStyles.cardTitle}>{p.name}</h3>
          <div style={mStyles.cardSub}>{p.kind}</div>
          <p style={mStyles.cardBlurb}>{p.one}</p>
        </button>
      ))}
    </div>
  );
}

function MProjectDetail({ p }) {
  return (
    <div style={mStyles.page}>
      <div style={mStyles.meta}>{p.part} · PLATE {p.plate} · {p.status}</div>
      <h1 style={mStyles.h1}>{p.name}</h1>
      <div style={mStyles.sub}>{p.kind}</div>
      <p style={mStyles.para}>{p.one}</p>

      <div style={mStyles.meta}>STACK</div>
      <div style={mStyles.pillRow}>
        {p.stack.map(s => <span key={s} style={mStyles.pill}>{s}</span>)}
      </div>

      <div style={mStyles.meta}>INSIDE</div>
      <ul style={mStyles.list}>
        {p.inside.map((x, i) => (
          <li key={i} style={mStyles.listItem}>
            <span style={mStyles.bullet}>▸</span><span>{x}</span>
          </li>
        ))}
      </ul>

      <div style={mStyles.meta}>BEYOND</div>
      <ul style={mStyles.list}>
        {p.beyond.map((x, i) => (
          <li key={i} style={mStyles.listItem}>
            <span style={mStyles.bullet}>▸</span><span>{x}</span>
          </li>
        ))}
      </ul>

      <div style={mStyles.meta}>DATA</div>
      <div style={mStyles.pillRow}>
        {p.data.map(d => <span key={d} style={mStyles.pill}>{d}</span>)}
      </div>

      <div style={mStyles.meta}>PLATFORM</div>
      <ul style={mStyles.list}>
        {p.platform.map((x, i) => (
          <li key={i} style={mStyles.listItem}>
            <span style={mStyles.bullet}>▸</span><span>{x}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Professional (vertical timeline)
// ─────────────────────────────────────────────────────────────────────────────
function MProfessional() {
  return (
    <div style={mStyles.page}>
      <div style={mStyles.meta}>WORLD 02 · THE SPINE</div>
      <h1 style={mStyles.h1}>Professional</h1>
      <p style={mStyles.para}>
        Where I've been, in chronological order. From the first ticker to the
        current seat.
      </p>
      <div style={mStyles.timelineWrap}>
        <div style={mStyles.timelineSpine} />
        {window.TIMELINE.map(node => (
          <div key={node.n} style={mStyles.tNode}>
            <div style={mStyles.tDot} />
            <div style={mStyles.tEra}>{String(node.n).padStart(2, "0")} · {node.era}</div>
            <div style={mStyles.tTitle}>{node.title}</div>
            <p style={mStyles.tBody}>{node.body}</p>
            {node.tags && (
              <div style={{ ...mStyles.pillRow, marginTop: 6 }}>
                {node.tags.map(t => <span key={t} style={mStyles.pill}>{t}</span>)}
              </div>
            )}
            {node.link && (
              <a href="#" onClick={(e) => { e.preventDefault(); }} style={mStyles.link}>
                {node.link.label}
              </a>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Creative (DEVA tracklist)
// ─────────────────────────────────────────────────────────────────────────────
function MCreative({ detail, onSelect }) {
  const t = detail && window.TRACKS[detail];
  if (t) return <MTrackDetail t={t} id={detail} />;
  const tracks = window.TRACKS;
  return (
    <div style={mStyles.page}>
      <div style={mStyles.meta}>WORLD 03 · DEVA · LP</div>
      <h1 style={mStyles.h1}>Creative</h1>
      <p style={mStyles.para}>
        Pinned tiles, half-finished thoughts. A vinyl LP — two sides, four
        tracks. Pick a side.
      </p>

      <div style={{ ...mStyles.meta, marginTop: 18 }}>SIDE A</div>
      {["a1", "a2"].map(k => (
        <MTrackCard key={k} id={k} t={tracks[k]} onSelect={onSelect} />
      ))}

      <div style={{ ...mStyles.meta, marginTop: 18 }}>SIDE B</div>
      {["b1", "b2"].map(k => (
        <MTrackCard key={k} id={k} t={tracks[k]} onSelect={onSelect} />
      ))}
    </div>
  );
}

function MTrackCard({ id, t, onSelect }) {
  return (
    <button
      onClick={() => onSelect(id)}
      style={{ ...mStyles.card, borderLeftColor: hueAccent(200, 65) }}
    >
      <div style={{ ...mStyles.cardN, color: hueAccent(200) }}>{t.num} · {t.credit}</div>
      <h3 style={mStyles.cardTitle}>{t.title}</h3>
      <div style={mStyles.cardSub}>{t.sub}</div>
      <p style={mStyles.cardBlurb}>{t.body}</p>
    </button>
  );
}

function MTrackDetail({ t, id }) {
  const essays = id === "a2" ? window.A2_ESSAYS : null;
  const notes  = id === "b1" ? window.B1_NOTES  : null;
  const live   = id === "b2" ? window.B2_LIVE   : null;
  return (
    <div style={mStyles.page}>
      <div style={mStyles.meta}>SIDE {t.side} · {t.num} · LINER NOTES</div>
      <h1 style={mStyles.h1}>{t.title}</h1>
      <div style={mStyles.sub}>{t.sub}</div>
      <p style={mStyles.para}>{t.body}</p>

      {essays && essays.length > 0 && (
        <>
          <div style={mStyles.meta}>CATALOG</div>
          <ul style={mStyles.list}>
            {essays.map((e, i) => (
              <li key={i} style={{ ...mStyles.listItem, flexDirection: "column", gap: 4 }}>
                <span style={{ ...mStyles.meta, color: "var(--amber-2)" }}>{e.date}</span>
                <span style={{ fontFamily: "var(--serif)", fontStyle: "italic", color: "var(--fg)" }}>
                  {e.title}
                </span>
                <span style={{ fontSize: 14, color: "var(--muted)" }}>{e.abs}</span>
              </li>
            ))}
          </ul>
        </>
      )}

      {notes && (
        <>
          <div style={mStyles.meta}>NOTES</div>
          <p style={{ ...mStyles.para, fontStyle: "italic", color: "var(--muted)" }}>
            {notes.length === 0 ? "— blank tape —" : null}
          </p>
        </>
      )}

      {live && (
        <>
          <div style={mStyles.meta}>APPEARANCES</div>
          <p style={{ ...mStyles.para, fontStyle: "italic", color: "var(--muted)" }}>
            {live.length === 0 ? "— nothing pressed yet —" : null}
          </p>
        </>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Personal (radial → linear on mobile)
// ─────────────────────────────────────────────────────────────────────────────
function MPersonal({ detail, onSelect }) {
  const branch = detail ? window.BRANCHES.find(b => b.id === detail) : null;
  if (branch) return <MBranchDetail b={branch} />;
  return (
    <div style={mStyles.page}>
      <div style={mStyles.meta}>WORLD 04 · MIND-MAP</div>
      <h1 style={mStyles.h1}>Personal</h1>
      <p style={mStyles.para}>
        A map of the off-screen me. Six branches; tap one to drop in.
      </p>
      {window.BRANCHES.map(b => (
        <button
          key={b.id}
          onClick={() => onSelect(b.id)}
          style={{ ...mStyles.card, borderLeftColor: hueAccent(260, 65) }}
        >
          <div style={{ ...mStyles.cardN, color: hueAccent(260) }}>{b.n}</div>
          <h3 style={mStyles.cardTitle}>{b.label}</h3>
          <div style={mStyles.cardSub}>{b.blurb}</div>
        </button>
      ))}
    </div>
  );
}

function MBranchDetail({ b }) {
  return (
    <div style={mStyles.page}>
      <div style={mStyles.meta}>BRANCH {b.n} · {b.label.toUpperCase()}</div>
      <h1 style={mStyles.h1}>{b.label}</h1>
      <p style={mStyles.para}>{b.blurb}</p>

      <div style={mStyles.meta}>LEAVES</div>
      <ul style={mStyles.list}>
        {b.leaves.map((leaf, i) => (
          <li key={i} style={{ ...mStyles.listItem, flexDirection: "column", gap: 4 }}>
            <span style={{
              fontFamily: "var(--serif)",
              fontStyle: leaf.placeholder ? "italic" : "normal",
              color: leaf.placeholder ? "var(--muted)" : "var(--fg)",
              fontSize: 17,
            }}>
              {leaf.name}
            </span>
            <span style={{ fontSize: 14, lineHeight: 1.5, color: "var(--fg-2)" }}>
              {leaf.cap}
            </span>
          </li>
        ))}
      </ul>

      {b.bench && b.bench.length > 0 && (
        <>
          <div style={mStyles.meta}>BENCH</div>
          <ul style={mStyles.list}>
            {b.bench.map((x, i) => (
              <li key={i} style={mStyles.listItem}>
                <span style={mStyles.bullet}>·</span>
                <span style={{ fontStyle: "italic", color: "var(--fg-2)" }}>{x}</span>
              </li>
            ))}
          </ul>
        </>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Terminal (bottom sheet with tap-quick chips + text input)
// Mirrors the desktop Terminal.jsx command set.
// ─────────────────────────────────────────────────────────────────────────────
function MTerminal({ route, history, onNavigate, onPushHistory, open, setOpen }) {
  const [input, setInput] = useStateM("");
  const linesRef = useRefM(null);

  useEffectM(() => {
    if (open && linesRef.current) {
      linesRef.current.scrollTop = linesRef.current.scrollHeight;
    }
  }, [open, history]);

  const crumbs = route.world === "home"
    ? ["~"]
    : route.detail
      ? ["~", route.world, route.detail]
      : ["~", route.world];
  const breadcrumb = crumbs.join(" / ");

  const runCommand = useCallbackM((raw) => {
    const cmd = (raw || "").trim();
    if (!cmd) return;
    const push = (text, kind = "out") => onPushHistory({ cmd: null, text, kind });
    onPushHistory({ cmd, text: null });

    const [head, ...rest] = cmd.split(/\s+/);
    const arg = rest.join(" ").toLowerCase();

    switch (head.toLowerCase()) {
      case "help":
      case "?":
        push("commands:");
        push("  cd <projects|professional|creative|personal|home>");
        push("  open <leviathan|jarvis|easyapply|inquire>");
        push("  play <a1|a2|b1|b2 | music|writing|commentary|live>");
        push("  zoom <origins|people|reading|habits|curiosities|convictions>");
        push("  back · home · ls · whoami · contact · clear");
        break;
      case "cd": {
        const target = arg.replace(/^\//, "").replace(/^~$/, "home");
        const valid = ["projects", "professional", "creative", "personal", "home"];
        if (valid.includes(target)) {
          onNavigate(target === "home" ? { world: "home" } : { world: target });
        } else {
          push(`cd: no such world: ${target || "(empty)"}`, "err");
        }
        break;
      }
      case "open": {
        const known = ["leviathan", "jarvis", "easyapply", "inquire"];
        if (known.includes(arg)) onNavigate({ world: "projects", detail: arg });
        else push(`open: unknown plate: ${arg}`, "err");
        break;
      }
      case "play": {
        const alias = {
          a1: "a1", a2: "a2", b1: "b1", b2: "b2",
          music: "a1", writing: "a2", research: "a2", commentary: "b1", live: "b2",
          front: "front", back: "back",
        };
        const target = alias[arg];
        if (target) onNavigate({ world: "creative", detail: target === "front" ? null : target });
        else push(`play: unknown track: ${arg}`, "err");
        break;
      }
      case "zoom": {
        const known = ["origins", "people", "reading", "habits", "curiosities", "convictions"];
        if (known.includes(arg)) onNavigate({ world: "personal", detail: arg });
        else push(`zoom: unknown branch: ${arg}`, "err");
        break;
      }
      case "back":
        if (route.detail) onNavigate({ world: route.world });
        else if (route.world !== "home") onNavigate({ world: "home" });
        else push("back: already at root", "err");
        break;
      case "home":
        onNavigate({ world: "home" });
        break;
      case "ls":
        if (route.world === "home") {
          push("projects/  professional/  creative/  personal/");
        } else if (route.world === "projects" && !route.detail) {
          push("leviathan.plate  jarvis.plate  easyapply.plate  inquire.plate");
        } else if (route.world === "professional") {
          push("timeline.spine  (11 nodes)");
        } else if (route.world === "creative" && !route.detail) {
          push("a1.music  a2.writing  b1.commentary  b2.live");
        } else if (route.world === "creative") {
          push(`now reading: liner notes for ${route.detail}`);
        } else if (route.world === "personal" && !route.detail) {
          push("01.origins  02.people  03.reading  04.habits  05.curiosities  06.convictions");
        } else if (route.world === "personal") {
          push(`zoomed: ${route.detail}.branch`);
        }
        break;
      case "whoami":
        push(`${window.ABOUT.name} — ${window.ABOUT.tagline}`);
        push(window.ABOUT.bio);
        break;
      case "contact":
        push(`linkedin → ${window.ABOUT.linkedin}`);
        break;
      case "clear":
        onPushHistory("__clear");
        break;
      default:
        push(`${head}: command not found. try \`help\`.`, "err");
    }
  }, [onNavigate, onPushHistory, route]);

  const quick = route.world === "home"
    ? ["help", "cd projects", "cd professional", "cd creative", "cd personal", "whoami", "contact"]
    : route.world === "projects"
      ? ["ls", "open leviathan", "open jarvis", "open easyapply", "open inquire", "back", "home"]
      : route.world === "creative"
        ? ["ls", "play a1", "play a2", "play b1", "play b2", "back", "home"]
        : route.world === "personal"
          ? ["ls", "zoom origins", "zoom people", "zoom reading", "zoom habits", "zoom curiosities", "zoom convictions", "back", "home"]
          : ["ls", "back", "home", "whoami"];

  const onSubmit = (e) => {
    e.preventDefault();
    runCommand(input);
    setInput("");
  };

  return (
    <>
      {open && (
        <div style={mStyles.termPanel}>
          <div ref={linesRef} style={mStyles.termLines}>
            {history.map((h, i) => (
              h.cmd != null ? (
                <div key={i} style={mStyles.termLineCmd}>
                  <span style={{ color: "var(--muted)" }}>{breadcrumb} </span>
                  <span style={{ color: "var(--amber)" }}>$ </span>
                  {h.cmd}
                </div>
              ) : (
                <div key={i} style={h.kind === "err" ? mStyles.termLineErr : mStyles.termLineOut}>
                  {h.text}
                </div>
              )
            ))}
          </div>
          <div style={mStyles.termQuick}>
            {quick.map(q => (
              <button key={q} style={mStyles.termQuickChip} onClick={() => runCommand(q)}>
                {q}
              </button>
            ))}
          </div>
          <form onSubmit={onSubmit} style={mStyles.termInputRow}>
            <span style={mStyles.termInputPrompt}>$</span>
            <input
              style={mStyles.termInput}
              value={input}
              onChange={(e) => setInput(e.target.value)}
              placeholder="type a command…"
              autoCapitalize="off"
              autoCorrect="off"
              autoComplete="off"
              spellCheck={false}
              inputMode="text"
            />
            <button type="submit" style={mStyles.termSend}>RUN</button>
          </form>
        </div>
      )}
      <div style={mStyles.termRoot}>
        <div style={mStyles.termStrip}>
          <span style={mStyles.termPrompt}>$</span>
          <span style={mStyles.termBreadcrumb}>{breadcrumb}<span className="blink"> _</span></span>
          <button style={mStyles.termToggle} onClick={() => setOpen(o => !o)}>
            {open ? "CLOSE" : "TERM"}
          </button>
        </div>
      </div>
    </>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Shell
// ─────────────────────────────────────────────────────────────────────────────
function MobileApp() {
  const [route, setRoute] = useStateM({ world: "home" });
  const [termOpen, setTermOpen] = useStateM(false);
  const [history, setHistory] = useStateM([
    { cmd: null, text: "portfolio.exe v1.0.0 — © Arihant Deva.", kind: "out" },
    { cmd: null, text: "mobile build · tap a world card or open TERM.", kind: "out" },
  ]);

  const navigate = useCallbackM((next) => {
    setRoute(next);
    if (typeof window !== "undefined" && window.scrollTo) {
      // scroll the inner container; falls through harmlessly if absent
      const el = document.querySelector("[data-mobile-scroll]");
      if (el) el.scrollTop = 0;
    }
  }, []);

  const back = useCallbackM(() => {
    setRoute(r => r.detail ? { world: r.world } : (r.world !== "home" ? { world: "home" } : r));
  }, []);

  const pushHistory = useCallbackM((entry) => {
    if (entry === "__clear") { setHistory([]); return; }
    setHistory(h => [...h, entry]);
  }, []);

  let view = null;
  if (route.world === "home") {
    view = <MHome onSelect={(id) => navigate({ world: id })} />;
  } else if (route.world === "projects") {
    view = <MProjects detail={route.detail} onSelect={(id) => navigate({ world: "projects", detail: id })} />;
  } else if (route.world === "professional") {
    view = <MProfessional />;
  } else if (route.world === "creative") {
    view = <MCreative detail={route.detail} onSelect={(id) => navigate({ world: "creative", detail: id })} />;
  } else if (route.world === "personal") {
    view = <MPersonal detail={route.detail} onSelect={(id) => navigate({ world: "personal", detail: id })} />;
  }

  return (
    <>
      <div style={mStyles.root}>
        <MTopbar route={route} onBack={back} onHome={() => navigate({ world: "home" })} />
        <div
          data-mobile-scroll
          style={mStyles.scroll}
          key={route.world + "/" + (route.detail || "_")}
        >
          {view}
        </div>
      </div>
      <MTerminal
        route={route}
        history={history}
        onNavigate={navigate}
        onPushHistory={pushHistory}
        open={termOpen}
        setOpen={setTermOpen}
      />
    </>
  );
}

window.MobileApp = MobileApp;
