/* ============ ATHARV.OS — Main App ============ */

function App() {
  // standby | boot | splash | desktop | shutdown | bsod
  // standby = first-visit power gate (crt-fx.js holds the tube dark until the
  // first click unlocks audio); BIOS typing waits so it starts with the beam.
  const [phase, setPhase] = React.useState(() =>
    document.documentElement.classList.contains('tv-boot-gate') ? 'standby' : 'boot');
  React.useEffect(() => {
    if (phase !== 'standby') return;
    // safety: if the gate class vanished before this effect ran (user clicked
    // during Babel compile), start booting immediately
    if (!document.documentElement.classList.contains('tv-boot-gate')) { setPhase('boot'); return; }
    const onWake = () => setPhase('boot');
    window.addEventListener('atharv-os:tv-wake', onWake);
    return () => window.removeEventListener('atharv-os:tv-wake', onWake);
  }, [phase]);
  const [windows, setWindows] = React.useState([]);
  const [focused, setFocused] = React.useState(null);
  const [zCounter, setZCounter] = React.useState(100);
  const [startOpen, setStartOpen] = React.useState(false);
  const [tilt, setTilt] = React.useState({ x: 0, y: 0 });
  const [crt, setCrt] = React.useState(false);
  const [mp, setMp] = React.useState({ playing: false, current: null });
  const [notepadText, setNotepadText] = React.useState(D.guitarTab + "\n\n" + D.notes.map(n => `[${n.date}]\n${n.body}\n`).join('\n'));
  const [termHistory, setTermHistory] = React.useState([]);
  const [sudoMode, setSudoMode] = React.useState(false);
  const [muted, setMutedState] = React.useState(() => {
    if (window.soundsApi) return window.soundsApi.isMuted();
    return true;
  });
  const setMuted = React.useCallback((v) => {
    const next = (typeof v === 'function') ? v(muted) : v;
    setMutedState(next);
    if (window.soundsApi) window.soundsApi.setMuted(next);
    if (!next && window.sfx) window.sfx.ding();
  }, [muted]);
  const [volume, setVolume] = React.useState(75);
  React.useEffect(() => {
    window.__atharvMuted = muted;
    if (window.soundsApi) window.soundsApi.setMuted(muted);
  }, [muted]);
  // Listen for mute changes from other surfaces
  React.useEffect(() => {
    const h = (e) => setMutedState(!!(e.detail && e.detail.muted));
    window.addEventListener('atharv-os:mute', h);
    return () => window.removeEventListener('atharv-os:mute', h);
  }, []);
  const [konami, setKonami] = React.useState([]);
  const [matrixOn, setMatrixOn] = React.useState(false);
  const [confetti, setConfetti] = React.useState(false);
  const desktopRef = React.useRef();

  // Tweaks: project icon overrides
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "iconOverrides": {},
    "folderIcons": {},
    "showBevel": true,
    "showTweaks": true
  }/*EDITMODE-END*/;
  const tweaks = (typeof useTweaks === 'function') ? useTweaks(TWEAK_DEFAULTS) : { t: TWEAK_DEFAULTS, setTweak: ()=>{} };
  const t = tweaks.t || tweaks;
  const setTweak = tweaks.setTweak || (()=>{});

  // Load notepad from localStorage
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem('atharv_notepad');
      if (saved) setNotepadText(saved);
    } catch(e){}
  }, []);
  React.useEffect(() => {
    try { localStorage.setItem('atharv_notepad', notepadText); } catch(e){}
  }, [notepadText]);

  // Parallax tilt disabled — fixed scene
  React.useEffect(() => {
    setTilt({ x: 0, y: 0 });
  }, []);

  // Konami code
  React.useEffect(() => {
    const seq = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'];
    const h = (e) => {
      const k = e.key;
      const next = [...konami, k].slice(-10);
      setKonami(next);
      if (next.length === 10 && next.every((c,i)=>c.toLowerCase() === seq[i].toLowerCase())) {
        if (window.sfx) window.sfx.ding();
        triggerKonami();
      }
      if ((e.key === 'r' || e.key === 'R') && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        openApp('run');
      }
    };
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  }, [konami]);

  // Agentic launch bridge — devarv AI (and any caller) can open windows.
  // Uses a ref so the handler always sees the latest openApp/openProject.
  const launchRef = React.useRef(null);

  // Custom events
  React.useEffect(() => {
    const onMatrix = () => { setMatrixOn(true); setTimeout(()=>setMatrixOn(false), 6000); };
    const onKon = () => triggerKonami();
    const onClear = () => setTermHistory([]);
    const onLaunch = (e) => { if (launchRef.current) launchRef.current(e.detail || {}); };
    window.addEventListener('atharv:matrix', onMatrix);
    window.addEventListener('atharv:konami', onKon);
    window.addEventListener('atharv:clearterm', onClear);
    window.addEventListener('atharv-os:launch', onLaunch);
    return () => {
      window.removeEventListener('atharv:matrix', onMatrix);
      window.removeEventListener('atharv:konami', onKon);
      window.removeEventListener('atharv:clearterm', onClear);
      window.removeEventListener('atharv-os:launch', onLaunch);
    };
  }, []);

  const triggerKonami = () => {
    setConfetti(true);
    setTimeout(()=>setConfetti(false), 5000);
  };

  /* ========== Window manager ========== */
  const openWindow = (def) => {
    setZCounter(z => z + 1);
    const id = newWinId();
    const win = {
      id, ...def,
      x: def.x || 60 + (windows.length % 6) * 36,
      y: def.y || 50 + (windows.length % 6) * 28,
      w: def.w || 560, h: def.h || 420,
      z: zCounter + 1,
      minimized: false,
      opening: true,
    };
    setWindows(ws => [...ws, win]);
    setFocused(id);
    setTimeout(() => setWindows(ws => ws.map(w => w.id===id ? {...w, opening:false} : w)), 250);
    return id;
  };

  const closeWindow = (id) => {
    setWindows(ws => ws.map(w => w.id===id ? {...w, closing:true} : w));
    setTimeout(() => {
      setWindows(ws => ws.filter(w => w.id !== id));
    }, 280);
  };

  const minimizeWindow = (id) => {
    setWindows(ws => ws.map(w => w.id===id ? {...w, minimizing:true} : w));
    setTimeout(() => {
      setWindows(ws => ws.map(w => w.id===id ? {...w, minimized:true, minimizing:false} : w));
    }, 280);
  };

  const restoreWindow = (id) => {
    setZCounter(z => z+1);
    setWindows(ws => ws.map(w => w.id===id ? {...w, minimized:false, z: zCounter+1, opening:true} : w));
    setFocused(id);
    setTimeout(() => setWindows(ws => ws.map(w => w.id===id ? {...w, opening:false} : w)), 250);
  };

  const focusWindow = (id) => {
    setZCounter(z => z+1);
    setWindows(ws => ws.map(w => w.id===id ? {...w, z: zCounter+1} : w));
    setFocused(id);
  };

  const updateWindow = (id, patch) => {
    setWindows(ws => ws.map(w => w.id===id ? {...w, ...patch} : w));
  };

  /* ========== App opener ========== */
  const openApp = (key, opts = {}) => {
    // Find existing
    const existing = windows.find(w => w.appKey === key);
    if (existing) {
      if (existing.minimized) restoreWindow(existing.id);
      else focusWindow(existing.id);
      return;
    }
    if (window.sfx && !opts.silent) window.sfx.click();
    const defs = {
      about: { appKey:'about', title:'About — Atharv Sharma', iconKey:'about', w:560, h:480, content: 'about' },
      mycomputer: { appKey:'mycomputer', title:'My Computer', iconKey:'myComputer', w:600, h:440, content: 'mycomputer' },
      mediaplayer: { appKey:'mediaplayer', title:'Media Player — devarv\u2019s tapes', iconKey:'logo', w:760, h:600, content: 'mediaplayer' },
      photos: { appKey:'photos', title:'Photo Viewer — From the mountains', iconKey:'photos', w:620, h:480, content: 'photos' },
      notepad: { appKey:'notepad', title:'Notes.txt — Notepad', iconKey:'notes', w:520, h:440, content: 'notepad' },
      network: { appKey:'network', title:'Network Neighborhood', iconKey:'network', w:520, h:380, content: 'network' },
      ie: { appKey:'ie', title:'Internet Explorer', iconKey:'ie', w:720, h:540, content: 'ie' },
      readme: { appKey:'readme', title:'README.txt — Notepad', iconKey:'readme', w:480, h:440, content: 'readme' },
      resume: { appKey:'resume', title:'Resume.pdf — Atharv Sharma', iconKey:'pdf', w:760, h:600, content: 'resume' },
      experience: { appKey:'experience', title:'Experience.log — Notepad', iconKey:'experience', w:600, h:520, content: 'experience' },
      skills: { appKey:'skills', title:'Skills — Control Panel', iconKey:'skillsCpl', w:600, h:440, content: 'skills' },
      awards: { appKey:'awards', title:'Awards — Trophy Case', iconKey:'awards', w:560, h:440, content: 'awards' },
      recycle: { appKey:'recycle', title:'Recycle Bin', iconKey:'recycle', w:420, h:300, content: 'recycle' },
      terminal: { appKey:'terminal', title:'MS-DOS Prompt', iconKey:'terminal', w:560, h:380, content: 'terminal' },
      run: { appKey:'run', title:'Run', iconKey:'myComputer', w:380, h:200, content: 'run' },
      welcome: { appKey:'welcome', title:'Welcome to ATHARV.OS', iconKey:'logo', w:520, h:430, content: 'welcome' },
      tip: { appKey:'tip', title:'Tip of the Day', iconKey:'about', w:420, h:220, content: 'tip' },
      solitaire: { appKey:'solitaire', title:'Solitaire', iconKey:'solitaire', w:760, h:580, content: 'solitaire' },
      chat: { appKey:'chat', title:'devarv.ai — Instant Messenger', iconKey:'chat', w:420, h:540, content: 'chat' },
      minesweeper: { appKey:'minesweeper', title:'Minesweeper', iconKey:'mines', w:400, h:480, content: 'minesweeper' },
      hiring: { appKey:'hiring', title:'HIRING.TXT — Notepad', iconKey:'briefcase', w:560, h:600, content: 'hiring' },
      doom: { appKey:'doom', title:'DOOM', iconKey:'doom', w:680, h:520, content: 'doom' },
      systems: { appKey:'systems', title:'Systems Status — Live', iconKey:'systems', w:460, h:420, content: 'systems' },
    };
    const def = defs[key];
    if (!def) return;
    openWindow(def);
    if (window.va) window.va('event', { name: 'open_app', data: { app: key } });
    // Dial-up nostalgia: first UNMUTED IE open per session
    // (a muted open must not consume the one-shot — that made it look broken)
    if (key === 'ie' && !window.__aosModemPlayed && !window.__atharvMuted) {
      window.__aosModemPlayed = true;
      if (window.sfx && window.sfx.modem) window.sfx.modem();
    }
    // Dispatch global event for Bevel + other listeners
    try { window.dispatchEvent(new CustomEvent('atharv-os:open', { detail: { appKey: key } })); } catch(e) {}
  };

  const openProject = (slug) => {
    const proj = D.projects.find(p => p.slug === slug);
    if (!proj) return;
    const existing = windows.find(w => w.appKey === `project:${slug}`);
    if (existing) {
      if (existing.minimized) restoreWindow(existing.id);
      else focusWindow(existing.id);
      return;
    }
    if (window.sfx) window.sfx.click();
    openWindow({
      appKey: `project:${slug}`,
      title: `${proj.name} — Properties`,
      iconKey: proj.iconKey,
      iconColor: proj.themeColor,
      w: 580, h: 500,
      content: 'project', proj,
    });
    if (window.va) window.va('event', { name: 'open_project', data: { project: slug } });
    try { window.dispatchEvent(new CustomEvent('atharv-os:open', { detail: { appKey: `project:${slug}` } })); } catch(e) {}
  };

  // Wire the agentic launch bridge to the current openApp/openProject.
  // Accepts { app } or { project: <slug> }; validates against an allowlist so
  // an injected token can only open real windows (never arbitrary strings).
  const LAUNCH_APPS = ['about','mycomputer','mediaplayer','photos','notepad','network','ie','readme','resume','experience','skills','awards','recycle','terminal','solitaire','chat','minesweeper','hiring','doom','systems'];
  launchRef.current = (detail) => {
    if (detail && detail.project && D.projects.some(p => p.slug === detail.project)) {
      openProject(detail.project);
    } else if (detail && detail.app && LAUNCH_APPS.includes(detail.app)) {
      openApp(detail.app);
    } else if (detail && detail.do) {
      // Harmless UI effects the bot can trigger on request.
      const d = detail.do;
      if (d === 'screensaver' || d === 'katana' || d === 'katakana') startSaver();
      else if (d === 'matrix') { setMatrixOn(true); setTimeout(()=>setMatrixOn(false), 6000); }
      else if (d === 'konami' || d === 'confetti') triggerKonami();
      else if (d === 'scandisk') setScandiskDemo(true);
    }
  };

  /* ========== Run command ========== */
  const handleRun = (raw) => {
    const cmd = raw.trim().toLowerCase();
    // close run dialog
    const runWin = windows.find(w => w.appKey === 'run');
    if (runWin) closeWindow(runWin.id);
    if (!cmd) return;
    const map = {
      'about': 'about', 'mycomputer': 'mycomputer', 'media': 'mediaplayer',
      'spotify': 'mediaplayer', 'mediaplayer': 'mediaplayer',
      'photos': 'photos', 'notepad': 'notepad', 'notes': 'notepad',
      'projects': 'mycomputer', 'resume': 'resume', 'cv': 'resume',
      'skills': 'skills', 'experience': 'experience', 'awards': 'awards',
      'network': 'network', 'recycle': 'recycle', 'terminal': 'terminal',
      'cmd': 'terminal', 'bash': 'terminal', 'readme': 'readme',
      'help': 'tip',
      'sol': 'solitaire', 'solitaire': 'solitaire',
      'chat': 'chat', 'ai': 'chat', 'devarv': 'chat',
      'mines': 'minesweeper', 'minesweeper': 'minesweeper',
      'hiring': 'hiring', 'hire': 'hiring', 'job': 'hiring',
      'doom': 'doom',
      'systems': 'systems', 'status': 'systems', 'uptime': 'systems',
    };
    if (map[cmd]) { openApp(map[cmd]); return; }
    if (cmd === 'crt' || cmd === 'tv') {
      const off = document.documentElement.classList.toggle('no-crt');
      try { localStorage.setItem('aos_crt', off ? 'off' : 'on'); } catch (e) {}
      return;
    }
    if (cmd === 'matrix') { setMatrixOn(true); setTimeout(()=>setMatrixOn(false), 6000); return; }
    if (cmd === 'katana' || cmd === 'katakana' || cmd === 'screensaver' || cmd === 'saver') { startSaver(); return; }
    if (cmd === 'scandisk') { setScandiskDemo(true); return; }
    if (cmd === 'rover' || cmd === 'dog' || cmd === 'bevel') {
      try { window.dispatchEvent(new CustomEvent('atharv-os:rover-show')); } catch(e) {}
      return;
    }
    if (cmd === 'konami') { triggerKonami(); return; }
    if (cmd === 'sudo' || cmd === 'sudo su') { setSudoMode(true); openApp('terminal'); return; }
    if (cmd === 'whoami') { openApp('about'); return; }
    if (cmd === 'clear' || cmd === 'cls') { setTermHistory([]); return; }
    if (cmd === 'contact' || cmd === 'mail' || cmd === 'email') {
      openApp('ie');
      setTimeout(() => {
        try {
          window.dispatchEvent(new CustomEvent('atharv-os:ie-navigate', {
            detail: { url: 'http://www.atharv.os/contact' }
          }));
        } catch(e) {}
      }, 50);
      return;
    }
    if (cmd === 'bsod') { if (window.sfx) window.sfx.ding(); if (window.AOS_FX) window.AOS_FX.channelFlip(1.6); setPhase('bsod'); setTimeout(()=>{ if (window.AOS_FX) window.AOS_FX.channelFlip(1); setPhase('desktop'); }, 4500); return; }
    if (cmd === 'shutdown') { handleShutdown(); return; }
    if (cmd === 'crt') { setCrt(c=>!c); return; }
    // Direct project lookup
    const proj = D.projects.find(p => p.slug === cmd || p.name.toLowerCase().includes(cmd));
    if (proj) { openProject(proj.slug); return; }
    // Open as URL
    if (cmd.startsWith('http')) { window.open(raw, '_blank'); return; }
    // Default
    openApp('terminal');
  };

  const handleShutdown = () => {
    if (window.sfx) window.sfx.shutdown();
    if (window.AOS_FX) window.AOS_FX.channelFlip(1);
    setPhase('shutdown');
    setTimeout(() => {
      if (window.AOS_FX) window.AOS_FX.channelFlip(1);
      setPhase('boot');
      setWindows([]);
    }, 2200);
  };

  const addTermLine = (l) => setTermHistory(h => [...h, l]);

  /* ========== Desktop icons ========== */
  const desktopIcons = [
    { key:'mycomputer', label:'My Computer', icon:'myComputer' },
    { key:'about', label:'About', icon:'about' },
    { key:'hiring', label:'HIRING.TXT', icon:'briefcase' },
    { key:'systems', label:'Systems Status', icon:'systems' },
    { key:'network', label:'Network Neighborhood', icon:'network' },
    { key:'ie', label:'Internet Explorer', icon:'ie' },
    { key:'mediaplayer', label:'Media Player', icon:'mediaPlayer' },
    { key:'photos', label:'Photos', icon:'photos' },
    { key:'notepad', label:'Notes.txt', icon:'notes' },
    { key:'experience', label:'Experience.log', icon:'experience' },
    { key:'skills', label:'Skills', icon:'skillsCpl' },
    { key:'awards', label:'Awards', icon:'awards' },
    { key:'resume', label:'Resume.pdf', icon:'pdf' },
    { key:'readme', label:'README.txt', icon:'readme' },
    { key:'chat', label:'devarv.ai', icon:'chat' },
    { key:'solitaire', label:'Solitaire', icon:'solitaire' },
    { key:'minesweeper', label:'Minesweeper', icon:'mines' },
    { key:'doom', label:'DOOM', icon:'doom' },
    { key:'recycle', label:'Recycle Bin', icon:'recycle' },
  ];
  const [selectedIcon, setSelectedIcon] = React.useState(null);
  const [isMobile, setIsMobile] = React.useState(() =>
    typeof window !== 'undefined' && window.matchMedia
      ? window.matchMedia('(max-width: 900px)').matches : false);
  React.useEffect(() => {
    if (!window.matchMedia) return;
    const mql = window.matchMedia('(max-width: 900px)');
    const handler = (e) => setIsMobile(e.matches);
    if (mql.addEventListener) mql.addEventListener('change', handler);
    else mql.addListener(handler);
    return () => {
      if (mql.removeEventListener) mql.removeEventListener('change', handler);
      else mql.removeListener(handler);
    };
  }, []);

  /* ========== Screensaver — katakana matrix rain (2 min idle, or manual) ========== */
  const [saverOn, setSaverOn] = React.useState(false);
  const [scandiskDemo, setScandiskDemo] = React.useState(false);
  const saverRef = React.useRef(false);
  saverRef.current = saverOn;
  const saverStartedAt = React.useRef(0);
  const startSaver = () => { saverStartedAt.current = Date.now(); setSaverOn(true); };
  React.useEffect(() => {
    if (phase !== 'desktop') { setSaverOn(false); return; }
    const reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    const mobile = window.matchMedia && window.matchMedia('(max-width: 900px)').matches;
    let t;
    // The visitor can be actively engaged with embedded content that does NOT
    // emit window-level events — playing DOOM or driving a Spotify embed sends
    // focus/mouse/keys to the iframe, not to `window`. Firing the screensaver
    // over that reads as "it starts while I'm using it", so re-arm instead.
    const busyWithEmbed = () => {
      if (document.fullscreenElement) return true;               // anything in real browser fullscreen (e.g. DOOM)
      const ae = document.activeElement;
      if (ae && ae.tagName === 'IFRAME') return true;           // an embed (DOOM / Spotify) has focus
      if (document.querySelector('.win.maxed iframe')) return true; // DOOM/embed maximized & filling the screen, even if focus drifted
      const media = document.querySelectorAll('audio, video');
      for (const m of media) if (!m.paused && !m.ended && m.readyState > 2) return true; // something is playing
      return false;
    };
    const idleFire = () => {
      if (busyWithEmbed()) { arm(); return; }
      startSaver();
    };
    const arm = () => {
      clearTimeout(t);
      if (reducedMotion || mobile) return; // no idle auto-start, manual launch still allowed
      t = setTimeout(idleFire, 120000);
    };
    const activity = () => {
      // Grace period so the click/mouse-move that launches it manually doesn't wake it.
      if (saverRef.current && Date.now() - saverStartedAt.current > 800) setSaverOn(false);
      arm();
    };
    const evs = ['mousemove', 'mousedown', 'keydown', 'touchstart', 'wheel'];
    evs.forEach(e => window.addEventListener(e, activity, { passive: true }));
    // Moving focus into/out of an iframe (DOOM/Spotify) fires window focus/blur
    // rather than mouse/key events — count those as activity too so the timer
    // keeps resetting while the visitor works inside an embed.
    const focusEvs = ['blur', 'focus', 'visibilitychange'];
    focusEvs.forEach(e => window.addEventListener(e, activity));
    arm();
    return () => {
      clearTimeout(t);
      evs.forEach(e => window.removeEventListener(e, activity));
      focusEvs.forEach(e => window.removeEventListener(e, activity));
    };
  }, [phase]);

  /* ========== Welcome on first boot (no startup sound) ========== */
  React.useEffect(() => {
    if (phase === 'desktop' && windows.length === 0) {
      setTimeout(() => openApp('welcome', { silent: true }), 400);
    }
  }, [phase]);

  /* ========== DEL-during-boot easter egg → open Tweaks panel ========== */
  React.useEffect(() => {
    if (phase === 'desktop' && window.__aosBootDel) {
      window.__aosBootDel = false;
      setTimeout(() => {
        try { window.postMessage({ type: '__activate_edit_mode' }, '*'); } catch (e) {}
      }, 900);
    }
  }, [phase]);

  /* ========== Render content ========== */
  const renderContent = (win) => {
    const c = win.content;
    if (c === 'about') return <AboutContent/>;
    if (c === 'mycomputer') return <MyComputerContent openApp={openApp} openProject={openProject} iconOverrides={t.iconOverrides || {}} folderIcons={t.folderIcons || {}}/>;
    if (c === 'mediaplayer') return <MediaPlayerContent
      playing={mp.playing} setPlaying={(v)=>setMp(m=>({...m, playing: typeof v==='function'?v(m.playing):v}))}
      currentTrack={mp.current} setCurrentTrack={(v)=>setMp(m=>({...m, current:v}))}
    />;
    if (c === 'photos') return <PhotosContent/>;
    if (c === 'notepad') return <NotesContent notepadText={notepadText} setNotepadText={setNotepadText}/>;
    if (c === 'network') return <NetworkContent/>;
    if (c === 'ie') return <IEContent/>;
    if (c === 'readme') return <ReadmeContent/>;
    if (c === 'resume') return <ResumeContent/>;
    if (c === 'experience') return <ExperienceContent/>;
    if (c === 'skills') return <SkillsContent/>;
    if (c === 'awards') return <AwardsContent/>;
    if (c === 'recycle') return <RecycleBinContent/>;
    if (c === 'terminal') return <TerminalContent openApp={openApp} openProject={openProject} lineHistory={termHistory} addLine={addTermLine} sudoMode={sudoMode}/>;
    if (c === 'run') return <RunDialog onRun={handleRun} onClose={()=>closeWindow(win.id)}/>;
    if (c === 'project') return <ProjectContent proj={win.proj}/>;
    if (c === 'welcome') return <WelcomeContent openApp={openApp} onClose={()=>closeWindow(win.id)}/>;
    if (c === 'tip') return <TipOfTheDay onClose={()=>closeWindow(win.id)}/>;
    if (c === 'solitaire') return <Solitaire onClick={(window.sfx && window.sfx.click) || (()=>{})}/>;
    if (c === 'chat') return <ChatContent/>;
    if (c === 'minesweeper') return <Minesweeper/>;
    if (c === 'hiring') return <HiringContent/>;
    if (c === 'doom') return <DoomContent/>;
    if (c === 'systems') return <SystemsContent/>;
    return <div className="wbody">Unknown content</div>;
  };

  /* ========== Phases ========== */
  if (phase === 'standby') return null;   // tube dark — crt-fx.js owns the gate
  if (phase === 'boot') return <BootScreen onDone={()=>{
    // Guaranteed on the first-ever boot, 20% chance afterwards.
    let firstBoot = false;
    try {
      firstBoot = !localStorage.getItem('aos-scandisk-seen');
      if (firstBoot) localStorage.setItem('aos-scandisk-seen', '1');
    } catch (e) {}
    if (window.AOS_FX) window.AOS_FX.channelFlip(1);
    setPhase(firstBoot || Math.random() < 0.2 ? 'scandisk' : 'splash');
  }}/>;
  if (phase === 'scandisk') return <ScanDisk onDone={()=>{ if (window.AOS_FX) window.AOS_FX.channelFlip(0.5); setPhase('splash'); }}/>;
  if (phase === 'splash') return <SplashScreen onDone={()=>{ if (window.AOS_FX) window.AOS_FX.channelFlip(1); setPhase('desktop'); }}/>;
  if (phase === 'shutdown') return (
    <div className="shutdown-screen">It is now safe to turn off your computer.</div>
  );
  if (phase === 'bsod') return (
    <div className="bsod">
      <h2>ATHARV.OS</h2>
      A fatal exception 0x420B has occurred at 0028:devarv. The current
      application will be terminated.{'\n\n'}
      *  Press any key to terminate the current application.{'\n'}
      *  Press CTRL+ALT+DEL again to restart your computer. You will{'\n'}
         lose any unsaved information in all applications.{'\n\n'}
      (just kidding — desktop will return in a moment)
    </div>
  );

  /* ========== Desktop ========== */
  return (
    <>
      <div className="scene" ref={desktopRef}>
        <div className="stage3d">
          <div className={`desktop ${crt?'crt':''}`}>
            {/* Desktop icons */}
            <div className="dicons">
              {desktopIcons.map(it => (
                <div
                  key={it.key}
                  className={`dicon ${selectedIcon===it.key?'selected':''}`}
                  onClick={()=> {
                    if (isMobile) { it.action ? it.action() : openApp(it.key); }
                    else { setSelectedIcon(it.key); }
                  }}
                  onDoubleClick={()=> it.action ? it.action() : openApp(it.key)}
                >
                  <div dangerouslySetInnerHTML={{__html: ICO[it.icon] ? ICO[it.icon](32) : ''}}/>
                  <div className="lbl">{it.label}</div>
                </div>
              ))}
            </div>
            {/* Now playing widget */}
            <NowPlayingWidget
              playing={mp.playing}
              currentTrack={mp.current}
              onOpen={()=>openApp('mediaplayer')}
              onToggle={()=>setMp(m=>({...m, playing:!m.playing}))}
            />
            {/* Windows */}
            {windows.filter(w => !w.minimized).map(w => (
              <Window
                key={w.id} win={w}
                focused={focused === w.id}
                zIndex={w.z}
                onFocus={focusWindow}
                onClose={closeWindow}
                onMinimize={minimizeWindow}
                onMaximize={()=>{}}
                onUpdate={updateWindow}
              >
                {renderContent(w)}
              </Window>
            ))}
            {/* Click on empty desktop deselects */}
            <div
              style={{position:'absolute', inset:0, zIndex:1}}
              onClick={(e)=>{
                if (e.target === e.currentTarget) setSelectedIcon(null);
              }}
            />
          </div>
        </div>
      </div>

      {/* Matrix overlay */}
      {matrixOn && <MatrixRain onDone={()=>setMatrixOn(false)}/>}
      {saverOn && <MatrixRain screensaver/>}
      {scandiskDemo && <ScanDisk onDone={()=>setScandiskDemo(false)}/>}
      {/* Confetti */}
      {confetti && <Confetti/>}

      {/* Taskbar (NOT inside 3D — flat for usability) */}
      <div className="taskbar slide-in">
        <button
          className={`startbtn ${startOpen?'open':''}`}
          onMouseDown={(e)=>{ e.stopPropagation(); setStartOpen(o=>!o); }}
        >
          <span className="logo" dangerouslySetInnerHTML={{__html: ICO.startWindowsLogo(16)}}/>
          <span>Start</span>
        </button>
        <div className="tbsep-v"/>
        <div className="tasks">
          {windows.map(w => (
            <button
              key={w.id}
              className={`taskitem ${focused===w.id && !w.minimized ? 'active':''}`}
              onClick={()=>{
                if (w.minimized) restoreWindow(w.id);
                else if (focused === w.id) minimizeWindow(w.id);
                else focusWindow(w.id);
              }}
            >
              <span className="ico16" dangerouslySetInnerHTML={{__html: ICO[w.iconKey] ? ICO[w.iconKey](16, w.iconColor) : ICO.myComputer(16)}}/>
              <span className="lbl">{w.title.split('—')[0].trim()}</span>
            </button>
          ))}
        </div>
        <SystemTray
          muted={muted} setMuted={setMuted}
          volume={volume} setVolume={setVolume}
          mp={mp}
          onOpenMedia={()=>openApp('mediaplayer')}
          onOpenNetwork={()=>openApp('network')}
        />
      </div>

      {startOpen && <StartMenu onSelect={(k)=>{ setStartOpen(false); if (k==='shutdown') handleShutdown(); else if (k==='screensaver') startSaver(); else openApp(k); }} onClose={()=>setStartOpen(false)}/>}

      {/* === BEVEL — your low-poly mountain pup guide === */}
      {typeof BevelMascot === 'function' && (
        <BevelMascot
          phase={phase}
          windows={windows}
          mpPlaying={mp.playing}
          currentTrack={mp.current}
          visible={t.showBevel !== false}
        />
      )}

      {typeof TweaksPanel === 'function' && (
        <TweaksPanel title="Tweaks — Project Icons">
          {typeof TweakSection === 'function' && typeof TweakToggle === 'function' && (
            <TweakSection title="Rover — your guide">
              <div style={{fontSize:11, color:'#555', marginBottom:8}}>The Windows XP search pup, adopted by this OS. <i>Click to pet. Right-click for tricks.</i></div>
              <TweakToggle
                label="Show Rover"
                value={t.showBevel !== false}
                onChange={(v) => setTweak('showBevel', v)}
              />
            </TweakSection>
          )}
          <TweakSection title="My Computer › Folders (C:\)">
            <div style={{fontSize:11, color:'#555', marginBottom:8}}>Swap the folder icons for Projects / Awards / Photos / Notes.</div>
            {[
              {key:'projects', label:'Projects'},
              {key:'awards', label:'Awards'},
              {key:'photos', label:'Photos'},
              {key:'notes', label:'Notes'},
            ].map(f => {
              const cur = (t.folderIcons||{})[f.key] || 'folder';
              return (
                <div key={f.key} style={{display:'flex', alignItems:'center', gap:8, marginBottom:6, padding:'4px 6px', background:'#f4f4f4', border:'1px solid #ddd'}}>
                  <div style={{width:24, height:24, flexShrink:0}}
                    dangerouslySetInnerHTML={{__html: (window.ATHARV_ICONS[cur] || window.ATHARV_ICONS.folder)(24)}}/>
                  <div style={{fontSize:11, flex:1, fontWeight:600}}>{f.label}</div>
                  <select
                    value={cur}
                    onChange={e => setTweak('folderIcons', { ...(t.folderIcons||{}), [f.key]: e.target.value })}
                    style={{fontSize:11, fontFamily:'inherit'}}>
                    {['folder','myComputer','network','recycle','ie','pdf','readme','notes','mediaPlayer','photos','about','skillsCpl','experience','awards','myDocs','shield','server','graph','whale','lock','wrench','brain','key','users','doc','iot','terminal','wp','car','floppy'].map(k =>
                      <option key={k} value={k}>{k}</option>
                    )}
                  </select>
                </div>
              );
            })}
            <button
              onClick={() => setTweak('folderIcons', {})}
              style={{marginTop:6, padding:'4px 10px', fontSize:11, fontFamily:'inherit', cursor:'pointer'}}>
              Reset folders
            </button>
          </TweakSection>
          <TweakSection title="My Computer › Projects">
            <div style={{fontSize:11, color:'#555', marginBottom:8}}>Pick a Win95 icon for each project shown inside My Computer.</div>
            {D.projects.map(p => (
              <div key={p.slug} style={{display:'flex', alignItems:'center', gap:8, marginBottom:6, padding:'4px 6px', background:'#f4f4f4', border:'1px solid #ddd'}}>
                <div style={{width:24, height:24, flexShrink:0}}
                  dangerouslySetInnerHTML={{__html: (window.ATHARV_ICONS[(t.iconOverrides||{})[p.slug] || p.iconKey] || (()=>''))(24, p.themeColor)}}/>
                <div style={{fontSize:11, flex:1, fontWeight:600, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{p.name}</div>
                <select
                  value={(t.iconOverrides||{})[p.slug] || p.iconKey}
                  onChange={e => setTweak('iconOverrides', { ...(t.iconOverrides||{}), [p.slug]: e.target.value })}
                  style={{fontSize:11, fontFamily:'inherit'}}>
                  {['shield','server','graph','whale','lock','wrench','brain','key','users','doc','iot','terminal','wp','car','floppy','folder','myComputer','network','recycle','ie','pdf','readme','notes','mediaPlayer','photos','about','skillsCpl','experience','awards'].map(k =>
                    <option key={k} value={k}>{k}</option>
                  )}
                </select>
              </div>
            ))}
            <button
              onClick={() => setTweak('iconOverrides', {})}
              style={{marginTop:6, padding:'4px 10px', fontSize:11, fontFamily:'inherit', cursor:'pointer'}}>
              Reset all to defaults
            </button>
          </TweakSection>
        </TweaksPanel>
      )}
    </>
  );
}

/* ===== ScanDisk — rare boot easter egg (any key skips) ===== */
function ScanDisk({ onDone }) {
  const [pct, setPct] = React.useState(0);
  const [done, setDone] = React.useState(false);
  React.useEffect(() => {
    const t = setInterval(() => setPct(p => {
      if (p >= 100) { clearInterval(t); setDone(true); return 100; }
      return p + 2 + Math.floor(Math.random() * 4);
    }), 50);
    return () => clearInterval(t);
  }, []);
  React.useEffect(() => {
    if (!done) return;
    const t = setTimeout(onDone, 900);
    return () => clearTimeout(t);
  }, [done]);
  React.useEffect(() => {
    const skip = () => onDone();
    // Attach a beat later so the keypress/click that LAUNCHED the screen
    // (still bubbling to window) can't instantly dismiss it.
    const t = setTimeout(() => {
      window.addEventListener('keydown', skip);
      window.addEventListener('mousedown', skip);
      window.addEventListener('touchstart', skip);
    }, 200);
    return () => {
      clearTimeout(t);
      window.removeEventListener('keydown', skip);
      window.removeEventListener('mousedown', skip);
      window.removeEventListener('touchstart', skip);
    };
  }, []);
  const blocks = Math.round(Math.min(pct, 100) / 4);
  return (
    <div style={{
      position:'fixed', inset:0, background:'#0000AA', color:'#C0C0C0',
      fontFamily:'Fixedsys, Consolas, monospace', fontSize:15, zIndex:400000,
      display:'flex', alignItems:'center', justifyContent:'center', cursor:'default',
    }}>
      <div style={{width:'min(560px, 92vw)'}}>
        <div style={{background:'#C0C0C0', color:'#0000AA', textAlign:'center', fontWeight:700, padding:'2px 0', marginBottom:18}}>
          Microsoft ScanDisk
        </div>
        <p style={{marginBottom:14}}>ScanDisk is now checking drive C: for errors…</p>
        <div style={{border:'1px solid #C0C0C0', padding:3, marginBottom:8}}>
          <div style={{display:'flex', gap:2}}>
            {Array.from({length:25}, (_, i) => (
              <div key={i} style={{flex:1, height:14, background: i < blocks ? '#FFFF00' : 'transparent'}}/>
            ))}
          </div>
        </div>
        <p style={{marginBottom:18}}>{Math.min(pct, 100)}% complete</p>
        {done && <p style={{color:'#FFFF00'}}>1 hidden easter egg found. No errors on this drive.</p>}
        <p style={{marginTop:22, fontSize:12, opacity:0.7}}>Press any key to skip…</p>
      </div>
    </div>
  );
}

/* ===== Matrix Rain (easter egg + screensaver) ===== */
function MatrixRain({ onDone, screensaver }) {
  const canvasRef = React.useRef();
  React.useEffect(() => {
    const c = canvasRef.current;
    if (!c) return;
    c.width = window.innerWidth; c.height = window.innerHeight;
    const ctx = c.getContext('2d');
    const size = screensaver ? 20 : 14;          // bigger glyphs for the screensaver
    const stepMs = screensaver ? 85 : 0;         // ~4x slower fall for the screensaver
    const fade = screensaver ? 0.07 : 0.05;
    const cols = Math.floor(c.width / size);
    const drops = Array(cols).fill(1);
    const chars = (screensaver
      ? 'ｱｲｳｴｵｶｷｸｹｺｻｼｽｾｿﾀﾁﾂﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖﾗﾘﾙﾚﾛﾜﾝ0123456789'
      : '01ATHARVDEVARVMOUNTAIN日本語').split('');
    if (screensaver) { ctx.fillStyle = '#000'; ctx.fillRect(0, 0, c.width, c.height); }
    let raf, last = 0;
    const draw = (ts) => {
      raf = requestAnimationFrame(draw);
      if (stepMs && ts - last < stepMs) return;
      last = ts;
      ctx.fillStyle = `rgba(0,0,0,${fade})`;
      ctx.fillRect(0,0,c.width,c.height);
      ctx.fillStyle = '#5cd35c';
      ctx.font = `${size}px "MS Gothic", Fixedsys, monospace`;
      drops.forEach((y, i) => {
        const ch = chars[Math.floor(Math.random()*chars.length)];
        ctx.fillText(ch, i*size, y*size);
        if (y*size > c.height && Math.random() > 0.96) drops[i] = 0;
        drops[i]++;
      });
    };
    raf = requestAnimationFrame(draw);
    return () => cancelAnimationFrame(raf);
  }, []);
  return (
    <canvas ref={canvasRef} style={{
      position:'fixed', inset:0, pointerEvents:'none',
      zIndex: screensaver ? 500000 : 9999,
      background: screensaver ? '#000' : 'transparent',
    }}/>
  );
}

/* ===== Confetti ===== */
function Confetti() {
  const canvasRef = React.useRef();
  React.useEffect(() => {
    const c = canvasRef.current;
    if (!c) return;
    c.width = window.innerWidth; c.height = window.innerHeight;
    const ctx = c.getContext('2d');
    const colors = ['#FF6B6B','#FFD700','#5BC0EB','#7E5BB5','#5cd35c','#D69E2E'];
    const parts = Array(180).fill(0).map(()=>({
      x: Math.random()*c.width,
      y: -20 - Math.random()*200,
      vy: 2 + Math.random()*5,
      vx: -2 + Math.random()*4,
      r: 3 + Math.random()*5,
      c: colors[Math.floor(Math.random()*colors.length)],
      a: Math.random()*Math.PI*2,
      va: -0.2 + Math.random()*0.4,
    }));
    let raf;
    const draw = () => {
      ctx.clearRect(0,0,c.width,c.height);
      parts.forEach(p => {
        p.x += p.vx; p.y += p.vy; p.a += p.va;
        ctx.save(); ctx.translate(p.x, p.y); ctx.rotate(p.a);
        ctx.fillStyle = p.c;
        ctx.fillRect(-p.r, -p.r/2, p.r*2, p.r);
        ctx.restore();
      });
      raf = requestAnimationFrame(draw);
    };
    draw();
    return () => cancelAnimationFrame(raf);
  }, []);
  return (
    <canvas ref={canvasRef} className="fx-canvas"/>
  );
}

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