const { useState, useEffect, useRef } = React;

/* ---------- Tweaks ---------- */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "username": "dxead",
  "domain": ".xyz",
  "status": "Online",
  "rippleRows": 8,
  "rippleCols": 27,
  "cellSize": 56,
  "showProjects": true
}/*EDITMODE-END*/;

/* ---------- Icons ---------- */
const Icons = {
  Discord: () => (<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.37a19.79 19.79 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.74 19.74 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.1 13.1 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.3 12.3 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.84 19.84 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>),
  Github: () => (<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .5C5.65.5.5 5.65.5 12.05c0 5.1 3.29 9.41 7.86 10.95.57.1.78-.25.78-.55v-2.13c-3.2.7-3.87-1.36-3.87-1.36-.52-1.33-1.27-1.69-1.27-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.02 1.76 2.69 1.25 3.34.95.1-.74.4-1.25.73-1.54-2.55-.29-5.23-1.27-5.23-5.66 0-1.25.45-2.27 1.18-3.07-.12-.29-.51-1.46.11-3.04 0 0 .96-.31 3.15 1.17.91-.25 1.89-.38 2.86-.38s1.95.13 2.86.38c2.18-1.48 3.14-1.17 3.14-1.17.62 1.58.23 2.75.11 3.04.74.8 1.18 1.82 1.18 3.07 0 4.4-2.69 5.36-5.25 5.65.41.36.78 1.05.78 2.13v3.16c0 .31.21.66.79.55C20.21 21.45 23.5 17.14 23.5 12.05 23.5 5.65 18.35.5 12 .5z"/></svg>),
  Telegram: () => (<svg viewBox="0 0 24 24" fill="currentColor"><path d="M9.78 18.65l.28-4.23 7.68-6.92c.34-.31-.07-.46-.52-.19L7.74 13.3 3.64 12c-.88-.25-.89-.86.2-1.3l15.97-6.16c.73-.33 1.43.18 1.15 1.3l-2.72 12.81c-.19.91-.74 1.13-1.5.71L12.6 16.3l-1.99 1.93c-.23.23-.42.42-.83.42z"/></svg>),
  Mail: () => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="5" width="18" height="14" rx="1.5"/><path d="M3 7l9 7 9-7"/></svg>),
  Globe: () => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>),
  Chat: () => (<svg viewBox="0 -2 24 24" fill="currentColor"><path d="M20 2H4a2 2 0 0 0-2 2v18l4-4h14a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z"/></svg>),
  X: () => (<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2H21l-6.49 7.41L22 22h-6.844l-4.78-6.246L4.8 22H2.04l6.94-7.93L2 2h6.94l4.32 5.71L18.244 2zm-1.2 18h1.512L7.04 4H5.42l11.624 16z"/></svg>),
  Spotify: () => (<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .5C5.65.5.5 5.65.5 12s5.15 11.5 11.5 11.5S23.5 18.35 23.5 12 18.35.5 12 .5zm5.27 16.6c-.22.36-.7.48-1.06.26-2.9-1.77-6.55-2.17-10.85-1.18-.42.1-.84-.17-.93-.59s.17-.84.59-.93c4.7-1.07 8.74-.6 11.99 1.39.36.22.48.69.26 1.05zm1.4-3.13c-.28.45-.86.6-1.31.32-3.32-2.04-8.39-2.63-12.32-1.44-.5.15-1.04-.13-1.19-.63s.13-1.04.63-1.19c4.49-1.36 10.06-.7 13.86 1.63.46.28.6.86.33 1.31zm.12-3.27C15 8.07 8.27 7.83 4.5 8.95c-.6.18-1.23-.16-1.41-.76-.18-.6.16-1.23.76-1.41 4.32-1.31 11.74-1.06 16.36 1.7.54.32.72 1.03.4 1.57-.32.55-1.03.72-1.57.4z"/></svg>),
};

const SOCIALS = [
  { id: "discord", label: "Discord", meta: ".dxead",         href: "https://discord.gg/fWEVDZ6C2t", icon: "Discord" },
  { id: "contact", label: "Contact", meta: "DM on Discord",  action: "copy-discord",                 icon: "Chat" },
  { id: "site",    label: "Website", meta: "frostix.win",    href: "https://frostix.win",            icon: "Globe" },
  { id: "github",  label: "GitHub",  meta: "soon",           action: "soon",                         icon: "Github" },
];

const PROJECTS = [
  { id: "P/01", name: "Project Vector",    desc: "Anti-cheat bypass framework. Kernel-mode driver, undetected memory access primitives.", tag: "C++ / KMDF" },
  { id: "P/02", name: "GhostESP",          desc: "External overlay engine. DXGI shared-surface compositor.",                                  tag: "Rust / DX11" },
  { id: "P/03", name: "PayloadOS",         desc: "Modular runtime for game-specific exploit chains.",                                          tag: "C / Lua" },
  { id: "P/04", name: "Reflex Engine",     desc: "Aim assist research — kalman-filtered prediction, screen-space target solver.",              tag: "CUDA / C++" },
];

/* ---------- Encrypted text (cycles a list of phrases) ---------- */
const CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-={}[];:,.<>/?";
function randChar() { return CHARSET[Math.floor(Math.random() * CHARSET.length)]; }

function EncryptedCycle({ phrases, revealMs = 50, holdMs = 1800, flipMs = 50 }) {
  const [phraseIdx, setPhraseIdx] = useState(0);
  const [revealCount, setRevealCount] = useState(0);
  const [, force] = useState(0);
  const scrambleRef = useRef([]);

  const text = phrases[phraseIdx];

  // initialize scramble buffer when phrase changes
  useEffect(() => {
    scrambleRef.current = text.split("").map((c) => (c === " " ? " " : randChar()));
    setRevealCount(0);
  }, [text]);

  // reveal loop
  useEffect(() => {
    let cancelled = false;
    let raf;
    const start = performance.now();
    let lastFlip = start;

    const tick = (now) => {
      if (cancelled) return;
      const elapsed = now - start;
      const total = text.length;
      const rc = Math.min(total, Math.floor(elapsed / Math.max(1, revealMs)));
      setRevealCount(rc);

      if (rc >= total) {
        // hold, then advance to next phrase
        setTimeout(() => {
          if (!cancelled) setPhraseIdx((i) => (i + 1) % phrases.length);
        }, holdMs);
        return;
      }
      if (now - lastFlip >= flipMs) {
        for (let i = rc; i < total; i++) {
          scrambleRef.current[i] = text[i] === " " ? " " : randChar();
        }
        lastFlip = now;
        force((x) => x + 1);
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => { cancelled = true; cancelAnimationFrame(raf); };
  }, [text, revealMs, holdMs, flipMs, phrases.length]);

  return (
    <span aria-label={text} role="text">
      {text.split("").map((ch, i) => {
        const revealed = i < revealCount;
        const display = revealed ? ch : (ch === " " ? " " : (scrambleRef.current[i] || randChar()));
        return <span key={i} className={`enc-char ${revealed ? "revealed" : "scrambled"}`}>{display === " " ? "\u00A0" : display}</span>;
      })}
    </span>
  );
}

/* ---------- Text Generate (blur in by word) ---------- */
function TextGenerate({ words, className, duration = 0.6 }) {
  const arr = words.split(" ");
  const [shown, setShown] = useState(0);
  useEffect(() => {
    const timers = arr.map((_, i) => setTimeout(() => setShown((n) => Math.max(n, i + 1)), 200 * i));
    return () => timers.forEach(clearTimeout);
  }, []);
  return (
    <span className={className}>
      {arr.map((w, i) => (
        <span key={i} className={`gen-word ${i < shown ? "in" : ""}`} style={{ transitionDelay: `${i * 60}ms` }}>
          {w}
        </span>
      ))}
    </span>
  );
}

/* ---------- Hero ---------- */
function Hero({ t }) {
  const phrases = [
    "Game Hacking",
    `${t.username}${t.domain}`,
    "Reverse Engineering",
    `${t.username}${t.domain}`,
  ];
  return (
    <section className="hero" data-screen-label="Hero">
      <div className="avatar-wrap fade-in">
        <image-slot id="avatar" shape="circle" src="assets/pfp.jpg" placeholder="Drop your avatar"></image-slot>
      </div>

      <h1 className="brand">
        <TextGenerate words={`${t.username}${t.domain}`} />
      </h1>

      <div className="status fade-in d2">
        <div className="status-dot"></div>
        <span className="status-text">{t.status}</span>
      </div>

      <p className="tagline fade-in d3">
        <EncryptedCycle phrases={phrases} />
      </p>

      <SocialsGrid username={t.username} />
    </section>
  );
}

/* ---------- Toast (one-shot status message) ---------- */
const ToastCtx = React.createContext(() => {});
function ToastProvider({ children }) {
  const [toast, setToast] = useState(null);
  const show = (msg) => {
    setToast({ msg, key: Date.now() });
  };
  useEffect(() => {
    if (!toast) return;
    const t = setTimeout(() => setToast(null), 2200);
    return () => clearTimeout(t);
  }, [toast]);
  return (
    <ToastCtx.Provider value={show}>
      {children}
      <div className={`toast-wrap ${toast ? "in" : ""}`} aria-live="polite">
        {toast && <div key={toast.key} className="toast">{toast.msg}</div>}
      </div>
    </ToastCtx.Provider>
  );
}
function useToast() { return React.useContext(ToastCtx); }

/* ---------- Socials with 3D tilt + focus-blur (matches Projects) ---------- */
function SocialCard({ s, username }) {
  const ref = useRef(null);
  const innerRef = useRef(null);
  const Icon = Icons[s.icon];
  const toast = useToast();
  const onMove = (e) => {
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const x = (e.clientX - r.left - r.width / 2) / 14;
    const y = (e.clientY - r.top - r.height / 2) / 14;
    if (innerRef.current) innerRef.current.style.transform = `rotateY(${x}deg) rotateX(${-y}deg) translateZ(0)`;
  };
  const onLeave = () => {
    if (innerRef.current) innerRef.current.style.transform = `rotateY(0) rotateX(0) translateZ(0)`;
  };
  const onClick = (e) => {
    if (s.action === "copy-discord") {
      e.preventDefault();
      const handle = `.${username || "dxead"}`;
      const done = () => toast("Discord username copied!");
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(handle).then(done, () => {
          // fallback
          const ta = document.createElement("textarea");
          ta.value = handle; document.body.appendChild(ta);
          ta.select(); try { document.execCommand("copy"); } catch (_) {}
          ta.remove(); done();
        });
      } else {
        done();
      }
    } else if (s.action === "soon") {
      e.preventDefault();
      toast("Coming soon");
    }
  };
  const isAction = !!s.action;
  return (
    <div ref={ref} className="social-tilt" onMouseMove={onMove} onMouseLeave={onLeave}>
      <div ref={innerRef} className="social-tilt-inner">
        <a
          href={isAction ? "#" : s.href}
          className="social"
          target={isAction ? "_self" : "_blank"}
          rel="noopener noreferrer"
          onClick={onClick}
        >
          <Icon />
          <span className="label">{s.label}</span>
          <span className="meta">{s.meta}</span>
        </a>
      </div>
    </div>
  );
}

function SocialsGrid({ username }) {
  const [hovering, setHovering] = useState(false);
  return (
    <div
      className={`socials fade-in d4 ${hovering ? "has-hover" : ""}`}
      onMouseOver={() => setHovering(true)}
      onMouseLeave={() => setHovering(false)}
    >
      {SOCIALS.map((s) => <SocialCard key={s.id} s={s} username={username} />)}
    </div>
  );
}

/* ---------- Projects ---------- */
function useTilt(divisor = 22) {
  const ref = useRef(null);
  const innerRef = useRef(null);
  const onMove = (e) => {
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const x = (e.clientX - r.left - r.width / 2) / divisor;
    const y = (e.clientY - r.top - r.height / 2) / divisor;
    if (innerRef.current) innerRef.current.style.transform = `rotateY(${x}deg) rotateX(${-y}deg)`;
  };
  const onLeave = () => {
    if (innerRef.current) innerRef.current.style.transform = `rotateY(0) rotateX(0)`;
  };
  return { ref, innerRef, onMove, onLeave };
}

function FeaturedCard() {
  const { ref, innerRef, onMove, onLeave } = useTilt(28);
  return (
    <div ref={ref} className="project-tilt" onMouseMove={onMove} onMouseLeave={onLeave}>
      <div ref={innerRef} className="project-tilt-inner">
        <article className="project project-featured">
          <div className="scan" />
          <div className="pf-img">
            <img src="assets/madcausebad.png" alt="madcausebad" />
            <div className="pf-img-fade" />
          </div>
          <div className="pf-body">
            <span className="pf-eyebrow">// Dev Work</span>
            <a href="https://madcausebad.cc" className="pf-site" target="_blank" rel="noopener noreferrer">
              madcausebad.cc
            </a>
            <div className="pf-date">
              <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{display:"block",flexShrink:0}}><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>
              08 / 05 / 26
            </div>
            <p className="pf-desc">If you would like any dev work please add me on discord and give me a dm!</p>
          </div>
        </article>
      </div>
    </div>
  );
}

function ProjectCard({ p }) {
  const { ref, innerRef, onMove, onLeave } = useTilt(22);
  return (
    <div ref={ref} className="project-tilt" onMouseMove={onMove} onMouseLeave={onLeave}>
      <div ref={innerRef} className="project-tilt-inner">
        <article className="project project-empty">
          <div className="scan" />
        </article>
      </div>
    </div>
  );
}

function Projects() {
  const [hovering, setHovering] = useState(false);
  return (
    <>
      <div className="section-head">
        <span className="num">// 01</span>
        <span className="title">Projects</span>
        <span className="rule" />
        <span className="count">04 / 04</span>
      </div>
      <div
        className={`projects ${hovering ? "has-hover" : ""}`}
        data-screen-label="Projects"
        onMouseOver={() => setHovering(true)}
        onMouseLeave={() => setHovering(false)}
      >
        <FeaturedCard />
        {PROJECTS.slice(1).map((p) => <ProjectCard key={p.id} p={p} />)}
      </div>
    </>
  );
}

/* ---------- Music Player ---------- */
function fmtTime(s) {
  if (!isFinite(s) || s < 0) s = 0;
  const m = Math.floor(s / 60);
  const r = Math.floor(s % 60);
  return `${m}:${r.toString().padStart(2, "0")}`;
}

const DEFAULT_AUDIO_SRC = "assets/Memories (feat. Kid Cudi).mp3";
const DEFAULT_AUDIO_TITLE = "Memories (feat. Kid Cudi)";

function MusicPlayer() {
  const audioRef = useRef(null);
  const [playing, setPlaying] = useState(false);
  const [t, setT] = useState(0);
  const [dur, setDur] = useState(0);
  const [vol, setVol] = useState(0.3);
  const [collapsed, setCollapsed] = useState(false);

  // set src and start playing immediately — no async delay
  useEffect(() => {
    const a = audioRef.current;
    if (!a) return;
    a.volume = 0.3;
    a.src = DEFAULT_AUDIO_SRC;
    a.play().catch(() => {
      const start = () => {
        a.play().catch(() => {});
        window.removeEventListener("pointerdown", start, true);
        window.removeEventListener("keydown", start, true);
      };
      window.addEventListener("pointerdown", start, true);
      window.addEventListener("keydown", start, true);
    });
  }, []);

  // tweaks reset — restart from beginning
  useEffect(() => {
    const onReset = () => {
      const a = audioRef.current;
      if (!a) return;
      a.currentTime = 0;
      a.play().catch(() => {});
    };
    window.addEventListener("dxead:reset-song", onReset);
    return () => window.removeEventListener("dxead:reset-song", onReset);
  }, []);

  const togglePlay = () => {
    const a = audioRef.current; if (!a) return;
    if (a.paused) a.play().catch(() => {}); else a.pause();
  };

  const seek = (e) => {
    const a = audioRef.current; if (!a || !dur) return;
    const r = e.currentTarget.getBoundingClientRect();
    const p = Math.min(1, Math.max(0, (e.clientX - r.left) / r.width));
    a.currentTime = p * dur;
  };

  // wire audio events
  useEffect(() => {
    const a = audioRef.current; if (!a) return;
    const onTime = () => setT(a.currentTime);
    const onLoaded = () => setDur(a.duration || 0);
    const onPlay = () => setPlaying(true);
    const onPause = () => setPlaying(false);
    const onEnd = () => { a.currentTime = 0; a.play().catch(() => {}); }; // loop
    a.addEventListener("timeupdate", onTime);
    a.addEventListener("loadedmetadata", onLoaded);
    a.addEventListener("play", onPlay);
    a.addEventListener("pause", onPause);
    a.addEventListener("ended", onEnd);
    a.volume = vol;
    return () => {
      a.removeEventListener("timeupdate", onTime);
      a.removeEventListener("loadedmetadata", onLoaded);
      a.removeEventListener("play", onPlay);
      a.removeEventListener("pause", onPause);
      a.removeEventListener("ended", onEnd);
    };
  }, []);

  useEffect(() => { if (audioRef.current) audioRef.current.volume = vol; }, [vol]);

  const pct = dur ? (t / dur) * 100 : 0;

  return (
    <div className={`mp ${collapsed ? "collapsed" : ""}`} data-playing={playing ? "1" : "0"}>
      <audio ref={audioRef} preload="auto"></audio>

      <button className="mp-collapse" title={collapsed ? "Expand player" : "Collapse player"} onClick={() => setCollapsed((c) => !c)}>
        {collapsed
          ? (<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{display:"block"}}><path d="M9 18l6-6-6-6"/></svg>)
          : (<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{display:"block"}}><path d="M15 18l-6-6 6-6"/></svg>)
        }
      </button>

      {collapsed ? (
        <button className={`mp-mini ${playing ? "playing" : ""}`} onClick={togglePlay} title={DEFAULT_AUDIO_TITLE}>
          <div className="mp-mini-disc" />
          <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" className="mp-mini-icon" style={{display:"block"}}>
            {playing
              ? (<><rect x="6" y="5" width="4" height="14"/><rect x="14" y="5" width="4" height="14"/></>)
              : (<polygon points="6,4 20,12 6,20"/>)}
          </svg>
        </button>
      ) : (
        <>
          <div className="mp-head">
            <div className="mp-disc" data-spin={playing ? "1" : "0"}>
              <div className="mp-disc-inner" />
              <div className="mp-disc-hole" />
            </div>
            <div className="mp-info">
              <div className="mp-title">{DEFAULT_AUDIO_TITLE}</div>
              <div className="mp-sub">{playing ? "Playing · loops" : "Paused"}</div>
            </div>
          </div>

          <div className="mp-bar" onClick={seek}>
            <div className="mp-bar-fill" style={{ width: `${pct}%` }} />
            <div className="mp-bar-thumb" style={{ left: `${pct}%` }} />
          </div>
          <div className="mp-time">
            <span>{fmtTime(t)}</span>
            <span>{fmtTime(dur)}</span>
          </div>

          <div className="mp-controls">
            <button className="mp-btn primary" onClick={togglePlay} title={playing ? "Pause" : "Play"}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style={{display:"block"}}>
                {playing
                  ? (<><rect x="6" y="5" width="4" height="14"/><rect x="14" y="5" width="4" height="14"/></>)
                  : (<polygon points="6,4 20,12 6,20"/>)}
              </svg>
            </button>
            <div className="mp-vol">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{display:"block"}}>
                <path d="M11 5L6 9H2v6h4l5 4V5z"/>
                <path d="M15.5 8.5a5 5 0 0 1 0 7"/>
              </svg>
              <input type="range" min={0} max={1} step={0.01} value={vol} onChange={(e) => setVol(parseFloat(e.target.value))}/>
            </div>
          </div>
        </>
      )}
    </div>
  );
}

/* ---------- App ---------- */
function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Scramble/reveal animation on the browser tab title
  useEffect(() => {
    const CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-={}[]";
    const phrases = ["dxead.xyz", "Game Hacking", "dxead.xyz", "Reverse Engineering"];
    let idx = 0, cancelled = false;
    const rand = () => CHARS[Math.floor(Math.random() * CHARS.length)];
    const run = () => {
      if (cancelled) return;
      const text = phrases[idx];
      const len = text.length;
      let buf = text.split("").map(c => c === " " ? " " : rand());
      const t0 = performance.now();
      const tick = (now) => {
        if (cancelled) return;
        const revealed = Math.min(len, Math.floor((now - t0) / 50));
        buf = buf.map((_, i) => i < revealed ? text[i] : text[i] === " " ? " " : rand());
        document.title = buf.join("");
        if (revealed < len) { requestAnimationFrame(tick); }
        else { document.title = text; setTimeout(() => { idx = (idx + 1) % phrases.length; run(); }, 1800); }
      };
      requestAnimationFrame(tick);
    };
    run();
    return () => { cancelled = true; document.title = "dxead.xyz"; };
  }, []);

  return (
    <ToastProvider>
    <div className="app">
      <BackgroundRippleEffect rows={t.rippleRows} cols={t.rippleCols} cellSize={t.cellSize} />
      <div className="stage">
        <Hero t={t} />
        {t.showProjects && <Projects />}
        <div className="foot-bar">
          <span>© 2026 {t.username}{t.domain}</span>
          <span>CONTACT .{t.username} on discord for enquiries</span>
        </div>
      </div>

      <CursorParticles />
      <MusicPlayer />

      <TweaksPanel title="Tweaks">
        <TweakSection title="Identity">
          <TweakText label="Username" value={t.username} onChange={(v) => setTweak("username", v)} />
          <TweakText label="Domain"   value={t.domain}   onChange={(v) => setTweak("domain",   v)} />
          <TweakText label="Status"   value={t.status}   onChange={(v) => setTweak("status",   v)} />
        </TweakSection>
        <TweakSection title="Ripple grid">
          <TweakSlider label="Rows" value={t.rippleRows} min={4}  max={14} step={1} onChange={(v) => setTweak("rippleRows", v)} />
          <TweakSlider label="Cols" value={t.rippleCols} min={12} max={36} step={1} onChange={(v) => setTweak("rippleCols", v)} />
          <TweakSlider label="Cell" value={t.cellSize}   min={32} max={80} step={2} onChange={(v) => setTweak("cellSize",   v)} />
        </TweakSection>
        <TweakSection title="Sections">
          <TweakToggle label="Show projects" value={t.showProjects} onChange={(v) => setTweak("showProjects", v)} />
          <TweakButton label="Reset avatar" onClick={() => {
            const slot = document.getElementById("avatar");
            const btn = slot && slot.shadowRoot && slot.shadowRoot.querySelector('[data-act="clear"]');
            if (btn) btn.click();
          }} />
        </TweakSection>
        <TweakSection title="Music">
          <TweakButton label="Reset song" onClick={() => window.dispatchEvent(new CustomEvent("dxead:reset-song"))} />
        </TweakSection>
      </TweaksPanel>
    </div>
    </ToastProvider>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
