/* global React,
   FolderIcon, DocIcon, TrashIcon, BrowserIcon,
   ContactsIcon, NotesIcon, AboutIcon, WorkIcon, GearIcon,
   SearchIcon, ControlCenterIcon, WifiIcon, BatteryIcon,
   ChevronLeftIcon, ChevronRightIcon,
   WORK, SIDE, POSTS,
   MacWindow, FinderShell, SIDEBAR_SECTIONS,
   WorkFolderView, WorkDetail,
   SideFolderView, SideDetail, BlogsListView, BlogPostView,
   BrowserResume, AddressBookApp, AboutApp, ControlPanel,
   MusicApp, MusicIosIcon,
   CassetteStage, MusicIndicator,
   PhoneShell */

const { useState, useEffect, useRef, useCallback, useMemo } = React;

/* Viewport width below which the iPhone peek overlay is suppressed — there's
   no longer enough room for the Mac to read alongside it. */
const PHONE_OVERLAY_MIN_W = 1024;
/* Drag distance (in CSS px) past which a release dismisses focus and snaps
   the phone back to its peek slot. */
const PHONE_DISMISS_THRESHOLD = 180;
/* Native iPhone 3GS chassis dimensions PhoneShell renders at. The overlay
   wraps PhoneShell at this size and applies an outer transform for placement. */
const PHONE_W = 320;
const PHONE_H = 620;

/* Mac chassis geometry — kept in sync with .chassis-wrap / .crt-bezel /
   .crt-screen in styles.css. The CRT screen sits inside the bezel (78% × 56%
   of the chassis) with a 14px inset, and transform-origin (50%, 35%) puts the
   scale pivot at the bezel center, which is 123px above the chassis-wrap's
   geometric center. We use these to compute a zoom that exactly covers the
   viewport at the end of the power-on sequence. */
const CHASSIS_W = 720;
const CHASSIS_H = 820;
const CRT_SCREEN_W = 0.78 * CHASSIS_W - 28;     // 533.6
const CRT_SCREEN_H = 0.56 * CHASSIS_H - 28;     // 431.2
const CRT_ORIGIN_OFFSET_Y = (0.5 - 0.35) * CHASSIS_H;  // 123 — bezel above chassis center

function coverScale() {
  if (typeof window === 'undefined') return 2.4;
  return Math.max(window.innerWidth / CRT_SCREEN_W, window.innerHeight / CRT_SCREEN_H);
}

/* ============== Power on flow =======================================
   Phases: intro → booting → zooming → desktop.
   - intro:    small chassis idling on the screensaver (looping video + clock).
   - booting:  small chassis still, makOS splash + progress bar in CRT.
   - zooming:  chassis scales up while CRT TV-on warmup runs.
   - desktop:  regular makOS desktop. */
function usePowerOn(skip) {
  const [phase, setPhase] = useState(skip ? 'desktop' : 'intro');
  const [warmState, setWarmState] = useState(null);
  const timers = useRef([]);

  useEffect(() => {
    document.body.style.overflow = 'hidden';
    return () => {
      document.body.style.overflow = '';
      timers.current.forEach(clearTimeout);
    };
  }, []);

  useEffect(() => { if (skip) setPhase('desktop'); }, [skip]);

  function clearAll() {
    timers.current.forEach(clearTimeout);
    timers.current = [];
  }

  const powerOn = useCallback(() => {
    setPhase(p => {
      if (p !== 'intro') return p;
      clearAll();
      // Step 1: linger on the makOS boot splash inside the small chassis.
      timers.current.push(setTimeout(() => {
        // Step 2: zoom the chassis up and run the CRT TV-on warmup.
        setPhase('zooming');
        setWarmState('dot');
        timers.current.push(setTimeout(() => setWarmState('line'), 60));
        timers.current.push(setTimeout(() => setWarmState('full'), 420));
        timers.current.push(setTimeout(() => setWarmState('fade'), 920));
        timers.current.push(setTimeout(() => {
          setPhase('desktop');
          setWarmState(null);
        }, 1180));
      }, 1700));
      return 'booting';
    });
  }, []);

  const powerOff = useCallback(() => {
    clearAll();
    setWarmState(null);
    setPhase('intro');
  }, []);

  // Chassis sits small while we're at intro/booting, then zooms up.
  const progress = (phase === 'intro' || phase === 'booting') ? 0 : 1;
  return { phase, progress, warmState, powerOn, powerOff };
}

/* ============== Live clock (also exported for iOS status bar) ========= */
function useClock() {
  const [now, setNow] = useState(new Date());
  useEffect(() => {
    const t = setInterval(() => setNow(new Date()), 30 * 1000);
    return () => clearInterval(t);
  }, []);
  return now;
}
function formatClock(d) {
  const day = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][d.getDay()];
  let h = d.getHours();
  const m = d.getMinutes().toString().padStart(2, '0');
  const ampm = h >= 12 ? 'PM' : 'AM';
  h = h % 12; if (h === 0) h = 12;
  return `${day} ${h}:${m} ${ampm}`;
}
const SAVER_DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
const SAVER_MONTHS = ['January','February','March','April','May','June','July','August',
                      'September','October','November','December'];
function formatSaverDate(d) {
  return `${SAVER_DAYS[d.getDay()]}, ${SAVER_MONTHS[d.getMonth()]} ${d.getDate()}`;
}
function formatSaverTime(d) {
  let h = d.getHours();
  const m = d.getMinutes().toString().padStart(2, '0');
  h = h % 12; if (h === 0) h = 12;
  return `${h}:${m}`;
}

/* ============== Screensaver — looping video + macOS-style clock =======
   Lives inside the small chassis CRT during the intro phase. Click bubbles
   up to the .crt-screen, which fires powerOn and starts the boot sequence. */
function Screensaver() {
  const now = useClock();
  return (
    <div className="screensaver">
      <video className="screensaver-video"
             src="img/screensaver-loop.mp4"
             poster="img/screensaver-poster.png"
             autoPlay loop muted playsInline preload="auto"/>
      <div className="screensaver-vignette"/>
      <div className="screensaver-overlay">
        <div className="screensaver-date">{formatSaverDate(now)}</div>
        <div className="screensaver-time">{formatSaverTime(now)}</div>
      </div>
    </div>
  );
}

/* ============== Apple-style menu bar ================================= */
function MenuBar({ openWindow, onShutdown, onRestart, onSpotlight, activeAppName, musicNode }) {
  const [open, setOpen] = useState(null);
  const now = useClock();

  const menus = {
    apple: [
      { label: 'About this Portfolio', action: () => openWindow('about') },
      { sep: true },
      { label: 'System Settings…', shortcut: '⌘,', action: () => openWindow('control') },
      { label: 'App Store…', disabled: true },
      { sep: true },
      { label: 'Restart…', action: onRestart },
      { label: 'Shut Down…', action: onShutdown },
    ],
    file: [
      { label: 'New Window', shortcut: '⌘N', action: () => openWindow('work') },
      { label: 'Open…', shortcut: '⌘O', action: () => openWindow('blogs') },
      { label: 'Open Resume', shortcut: '⌘R', action: () => openWindow('browser') },
      { sep: true },
      { label: 'Print…', shortcut: '⌘P', action: () => window.print() },
      { label: 'Close Window', shortcut: '⌘W', disabled: true },
    ],
    edit: [
      { label: 'Undo', shortcut: '⌘Z', disabled: true },
      { label: 'Redo', shortcut: '⇧⌘Z', disabled: true },
      { sep: true },
      { label: 'Cut', shortcut: '⌘X', disabled: true },
      { label: 'Copy', shortcut: '⌘C', disabled: true },
      { label: 'Paste Email', shortcut: '⌘V',
        action: () => navigator.clipboard?.writeText('tmakhlay2@gmail.com') },
    ],
    view: [
      { label: 'as Icons', shortcut: '⌘1' },
      { label: 'as List',  shortcut: '⌘2' },
      { label: 'as Columns', shortcut: '⌘3' },
      { sep: true },
      { label: 'Show Sidebar', shortcut: '⌃⌘S' },
      { label: 'Hide Toolbar', disabled: true },
    ],
    go: [
      { label: 'Work Folder', action: () => openWindow('work') },
      { label: 'Side Projects', action: () => openWindow('side') },
      { label: 'Writing', action: () => openWindow('blogs') },
      { sep: true },
      { label: 'Address Book', action: () => openWindow('address') },
      { label: 'Music', action: () => openWindow('music') },
    ],
    help: [
      { label: 'Search', shortcut: '⌘ Space', action: onSpotlight },
      { sep: true },
      { label: 'About this Portfolio', action: () => openWindow('about') },
    ],
  };

  function close() { setOpen(null); }
  useEffect(() => {
    if (!open) return;
    const handler = (e) => { if (!e.target.closest('.menubar')) close(); };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [open]);

  function renderMenu(key, items, x) {
    if (open !== key) return null;
    return (
      <div className="menu-dropdown" style={{ left: x }}>
        {items.map((it, i) => it.sep ? <div className="sep" key={i}/> : (
          <div key={i}
               className={`item ${it.disabled ? 'disabled' : ''}`}
               onClick={() => { if (!it.disabled) { it.action?.(); close(); } }}>
            <span>{it.label}</span>
            {it.shortcut && <span className="shortcut">{it.shortcut}</span>}
          </div>
        ))}
      </div>
    );
  }

  const appName = activeAppName || 'Finder';

  return (
    <div className="menubar">
      <div className={`mb-apple ${open==='apple'?'open':''}`}
           onMouseDown={(e)=>{e.preventDefault(); setOpen(open==='apple'?null:'apple');}}>
        <svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true">
          <path d="M8 14s-5.5-3.4-5.5-7.6c0-1.9 1.5-3.4 3.4-3.4 1.1 0 2.1.5 2.7 1.3.6-.8 1.6-1.3 2.7-1.3 1.9 0 3.4 1.5 3.4 3.4C13.5 10.6 8 14 8 14z" fill="currentColor"/>
        </svg>
      </div>
      <div className={`mb-app ${open==='file'?'open':''}`}
           onMouseDown={(e)=>{e.preventDefault(); setOpen(open==='file'?null:'file');}}>{appName}</div>
      <div className={`mb-item ${open==='edit'?'open':''}`}
           onMouseDown={(e)=>{e.preventDefault(); setOpen(open==='edit'?null:'edit');}}>Edit</div>
      <div className={`mb-item ${open==='view'?'open':''}`}
           onMouseDown={(e)=>{e.preventDefault(); setOpen(open==='view'?null:'view');}}>View</div>
      <div className={`mb-item ${open==='go'?'open':''}`}
           onMouseDown={(e)=>{e.preventDefault(); setOpen(open==='go'?null:'go');}}>Go</div>
      <div className={`mb-item ${open==='help'?'open':''}`}
           onMouseDown={(e)=>{e.preventDefault(); setOpen(open==='help'?null:'help');}}>Help</div>

      <div className="mb-spacer"/>

      <div className="mb-right">
        {musicNode}
        <div className="mb-icon" title="Battery"><BatteryIcon/></div>
        <div className="mb-icon" title="Wi-Fi"><WifiIcon/></div>
        <div className="mb-icon" title="Search" onClick={onSpotlight}><SearchIcon size={13}/></div>
        <div className="mb-icon" title="Control Center"><ControlCenterIcon/></div>
        <div className="mb-clock">{formatClock(now)}</div>
      </div>

      {renderMenu('apple',  menus.apple,  8)}
      {renderMenu('file',   menus.file,   30)}
      {renderMenu('edit',   menus.edit,   30 + 60)}
      {renderMenu('view',   menus.view,   30 + 60 + 38)}
      {renderMenu('go',     menus.go,     30 + 60 + 38 + 42)}
      {renderMenu('help',   menus.help,   30 + 60 + 38 + 42 + 32)}
    </div>
  );
}

/* ============== Desktop icons ======================================== */
function DesktopIcon({ id, icon, label, onOpen, isSelected, onSelect }) {
  return (
    <div className={`desktop-icon ${isSelected ? 'selected' : ''}`}
         data-icon-id={id}
         onMouseDown={(e) => e.stopPropagation()}
         onClick={(e) => { e.stopPropagation(); onSelect(id); }}
         onDoubleClick={onOpen}>
      <div className="pix-icon">{icon}</div>
      <div className="icon-label">{label}</div>
    </div>
  );
}

/* ============== Dock ================================================= */
function Dock({ open, runningKinds }) {
  const apps = [
    { id: 'browser', label: 'Browser',      icon: <BrowserIcon/> },
    { id: 'address', label: 'Address Book', icon: <ContactsIcon/> },
    { id: 'music',   label: 'Music',        icon: <MusicIosIcon/> },
    { id: 'about',   label: 'About Me',     icon: <AboutIcon/> },
    { id: 'control', label: 'System Settings', icon: <GearIcon/> },
  ];
  const folders = [
    { id: 'trash',   label: 'Trash',        icon: <TrashIcon/> },
  ];
  return (
    <div className="dock-wrap">
      {apps.map(it => (
        <div key={it.id} className="dock-item" onClick={() => open(it.id)}>
          <div className="dock-ico">{it.icon}</div>
          <div className="dock-tooltip">{it.label}</div>
          {runningKinds.has(it.id) && <div className="dock-running-dot"/>}
        </div>
      ))}
      <div className="dock-sep"/>
      {folders.map(it => (
        <div key={it.id} className="dock-item" onClick={() => open(it.id)}>
          <div className="dock-ico">{it.icon}</div>
          <div className="dock-tooltip">{it.label}</div>
        </div>
      ))}
    </div>
  );
}

/* ============== Spotlight =========================================== */
function Spotlight({ open, onClose, openWindow }) {
  const [q, setQ] = useState('');
  const inputRef = useRef(null);

  useEffect(() => {
    if (open) { setQ(''); setTimeout(() => inputRef.current?.focus(), 30); }
  }, [open]);

  useEffect(() => {
    function onKey(e) { if (e.key === 'Escape' && open) onClose(); }
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  const sources = useMemo(() => [
    ...WORK.map(w => ({ kind:'work', id:w.id, title:w.name, sub:`${w.role} · ${w.when}`, ico:<WorkIcon size={24}/>, openId:'workItem', payload:w })),
    ...SIDE.map(s => ({ kind:'side', id:s.id, title:s.name, sub:`Side project · ${s.when}`, ico:<FolderIcon size={24}/>, openId:'sideItem', payload:s })),
    ...POSTS.map(p => ({ kind:'post', id:p.id, title:p.title, sub:`${p.kind} · ${p.date}`, ico:<NotesIcon size={24}/>, openId:'post', payload:p })),
    { kind:'app', id:'browser', title:'Resume', sub:'Browser', ico:<BrowserIcon size={24}/>, openId:'browser' },
    { kind:'app', id:'address', title:'Address Book', sub:'Application', ico:<ContactsIcon size={24}/>, openId:'address' },
    { kind:'app', id:'music',   title:'Music', sub:'Application', ico:<MusicIosIcon size={24}/>, openId:'music' },
    { kind:'app', id:'about',   title:'About Me', sub:'Application', ico:<AboutIcon size={24}/>, openId:'about' },
    { kind:'app', id:'control', title:'System Settings', sub:'Application', ico:<GearIcon size={24}/>, openId:'control' },
  ], []);

  const results = useMemo(() => {
    if (!q.trim()) return sources.slice(0, 8);
    const Q = q.toLowerCase();
    return sources.filter(s => s.title.toLowerCase().includes(Q) || s.sub.toLowerCase().includes(Q)).slice(0, 10);
  }, [q, sources]);

  if (!open) return null;
  return (
    <div className="spotlight-overlay" onClick={onClose}>
      <div className="spotlight" onClick={(e) => e.stopPropagation()}>
        <div className="spotlight-input">
          <SearchIcon size={20}/>
          <input
            ref={inputRef}
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="Spotlight Search"
            onKeyDown={(e) => {
              if (e.key === 'Enter' && results[0]) {
                openWindow(results[0].openId, results[0].payload);
                onClose();
              }
            }}
          />
        </div>
        <div className="spotlight-results">
          {results.length === 0 && (
            <div style={{padding:'24px 16px', color:'var(--mac-text-3)', fontSize:13}}>No results</div>
          )}
          {results.length > 0 && <div className="spotlight-section">Top Hits</div>}
          {results.map((r, i) => (
            <div key={r.kind+r.id}
                 className={`spotlight-row ${i === 0 ? 'active' : ''}`}
                 onClick={() => { openWindow(r.openId, r.payload); onClose(); }}>
              <div className="sp-ico">{r.ico}</div>
              <div>
                <div className="sp-title">{r.title}</div>
                <div className="sp-sub">{r.sub}</div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ============== Notification Center ================================== */
function NotificationCenter({ openWindow, onDismiss }) {
  return (
    <div className="nc-overlay">
      <div className="nc-card" onClick={() => { openWindow('address'); onDismiss(); }}>
        <div className="nc-head"><span>📅</span> CALENDAR · NOW</div>
        <div className="nc-title">Booking Q3 2026 — one slot left</div>
        <div className="nc-body">Reply with the shape of the problem. I'll tell you whether I'm the right person for it.</div>
      </div>
      <div className="nc-card" onClick={() => { openWindow('post', POSTS[0]); onDismiss(); }}>
        <div className="nc-head"><span>📝</span> WRITING · 2D AGO</div>
        <div className="nc-title">New essay: {POSTS[0].title}</div>
        <div className="nc-body">{POSTS[0].body[0].slice(0, 90)}…</div>
      </div>
      <div className="nc-card" onClick={() => { openWindow('workItem', WORK[0]); onDismiss(); }}>
        <div className="nc-head"><span>🛠</span> WORK · NOW</div>
        <div className="nc-title">{WORK[0].name} — {WORK[0].role}</div>
        <div className="nc-body">{WORK[0].summary.slice(0, 100)}…</div>
      </div>
    </div>
  );
}

/* ============== Boot splash + window helpers =========================== */
function BootSplash({ hidden }) {
  return (
    <div className="boot-splash" style={{ opacity: hidden ? 0 : 1, transition: 'opacity 200ms' }}>
      <div>
        <div className="boot-mark">makOS</div>
        <div className="boot-progress"/>
      </div>
    </div>
  );
}

function toolbarFor(w, onBack, onForward) {
  const isFinder = ['work','side','blogs','workItem','sideItem','post','about'].includes(w.kind);
  if (!isFinder) return null;
  const canBack = (w.history?.length || 0) > 0;
  const canForward = (w.future?.length || 0) > 0;
  return {
    left: (
      <>
        <div className={`tb-btn ${canBack ? '' : 'disabled'}`} onClick={canBack ? onBack : undefined}><ChevronLeftIcon/></div>
        <div className={`tb-btn ${canForward ? '' : 'disabled'}`} onClick={canForward ? onForward : undefined}><ChevronRightIcon/></div>
      </>
    ),
    right: (
      <div className="tb-search">
        <SearchIcon size={12}/>
        <input placeholder="Search" readOnly/>
      </div>
    ),
  };
}

function renderWindowBody(w, openWindow, tweaks, setTweak, navigate, audioApi) {
  switch (w.kind) {
    case 'work':
      return (
        <FinderShell sections={SIDEBAR_SECTIONS('work')} activeId="work"
                     onPickSection={(id) => {
                       if (id === 'work' || id === 'side' || id === 'blogs' || id === 'about') navigate(id);
                     }}>
          <WorkFolderView openFile={(it) => navigate('workItem', it)}/>
        </FinderShell>
      );
    case 'side':
      return (
        <FinderShell sections={SIDEBAR_SECTIONS('side')} activeId="side"
                     onPickSection={(id) => {
                       if (id === 'work' || id === 'side' || id === 'blogs' || id === 'about') navigate(id);
                     }}>
          <SideFolderView openFile={(it) => navigate('sideItem', it)}/>
        </FinderShell>
      );
    case 'blogs':
      return (
        <FinderShell sections={SIDEBAR_SECTIONS('blogs')} activeId="blogs"
                     onPickSection={(id) => {
                       if (id === 'work' || id === 'side' || id === 'blogs' || id === 'about') navigate(id);
                     }}>
          <BlogsListView openFile={(it) => navigate('post', it)}/>
        </FinderShell>
      );
    case 'workItem':return <div className="finder-main-scroll" style={{flex:1, width:'100%'}}><WorkDetail item={w.payload}/></div>;
    case 'sideItem':return <div className="finder-main-scroll" style={{flex:1, width:'100%'}}><SideDetail item={w.payload}/></div>;
    case 'post':    return <div className="finder-main-scroll" style={{flex:1, width:'100%'}}><BlogPostView item={w.payload}/></div>;
    case 'browser': return <BrowserResume/>;
    case 'address': return <AddressBookApp/>;
    case 'about':   return <div className="finder-main-scroll" style={{flex:1, width:'100%'}}><AboutApp/></div>;
    case 'control': return <ControlPanel tweaks={tweaks} setTweak={setTweak}/>;
    case 'music':   return <MusicApp audioApi={audioApi}/>;
    default: return null;
  }
}

/* ============== iPhone peek overlay (desktop only) ====================
   Renders the same PhoneShell used on the mobile route, but as a floating
   chassis that lives on top of the homepage. Visual states:
     hidden  — well off-screen-left. Used for the pre-entry initial commit
               and any time macOS is up (intro phase ended).
     peek    — chassis tucked into the left edge, partially off-screen,
               leaning clockwise into the viewport. Click to focus.
     focused — chassis slides to dead center over a dark backdrop. The Mac
               stays put underneath; the backdrop covers it. Click the
               backdrop OR drag the top/bottom bezel past the threshold to
               dismiss back to peek.
   All transforms are computed in JS and applied as inline styles so we never
   hit the "scale(min(calc(...)))" parsing edge case some browsers choke on
   (the symptom: the focused state would silently invalidate, leaving the
   phone visually pinned at top-left). PhoneShell stays mounted across these
   transitions so its internal state (which app, scroll, audio) survives. */
function PhoneOverlay({ tweaks, setTweak, audioApi, macPhase }) {
  const [focused, setFocused] = useState(false);
  const [drag, setDrag] = useState({ dx: 0, dy: 0, dragging: false });
  const [entered, setEntered] = useState(false);
  // True for one tick after a drag-release so the synthesized click that
  // follows pointer-up doesn't re-focus the phone we just dismissed.
  const justReleasedRef = useRef(false);

  // Live viewport so transforms reflow on resize.
  const [vp, setVp] = useState(() => ({
    w: typeof window !== 'undefined' ? window.innerWidth  : 1280,
    h: typeof window !== 'undefined' ? window.innerHeight : 800,
  }));
  useEffect(() => {
    const onResize = () => setVp({ w: window.innerWidth, h: window.innerHeight });
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  // Two rAFs ensure the initial hidden transform is committed before we flip
  // `entered` to true — the next render then hands a real "from → to" delta
  // to the CSS transition, so the phone slides in instead of snapping.
  useEffect(() => {
    let f2;
    const f1 = requestAnimationFrame(() => {
      f2 = requestAnimationFrame(() => setEntered(true));
    });
    return () => {
      cancelAnimationFrame(f1);
      if (f2) cancelAnimationFrame(f2);
    };
  }, []);

  // Slide out when the user opens macOS, slide back in when they close it.
  // Also reset focus on the way out so the slide-out targets the peek slot,
  // not the centered focused position (avoids a weird jog mid-transition).
  useEffect(() => {
    if (macPhase !== 'intro') setFocused(false);
  }, [macPhase]);

  const overlayTweaks = useMemo(
    () => ({ ...tweaks, skipIntro: false }),
    [tweaks]
  );

  const onPeekClick = useCallback(() => {
    if (justReleasedRef.current) return;
    if (focused) return;
    if (!entered) return;
    if (macPhase !== 'intro') return;
    setFocused(true);
  }, [focused, entered, macPhase]);

  const onBackdropClick = useCallback(() => {
    if (focused) setFocused(false);
  }, [focused]);

  const startDrag = useCallback((e) => {
    if (!focused) return;
    e.preventDefault();
    e.stopPropagation();
    const startX = e.clientX, startY = e.clientY;
    setDrag({ dx: 0, dy: 0, dragging: true });

    const onMove = (ev) => {
      setDrag({ dx: ev.clientX - startX, dy: ev.clientY - startY, dragging: true });
    };
    const onUp = (ev) => {
      window.removeEventListener('pointermove', onMove);
      window.removeEventListener('pointerup', onUp);
      window.removeEventListener('pointercancel', onUp);
      const dist = Math.hypot(ev.clientX - startX, ev.clientY - startY);
      if (dist >= PHONE_DISMISS_THRESHOLD) {
        justReleasedRef.current = true;
        setTimeout(() => { justReleasedRef.current = false; }, 50);
        setFocused(false);
      }
      setDrag({ dx: 0, dy: 0, dragging: false });
    };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerup', onUp);
    window.addEventListener('pointercancel', onUp);
  }, [focused]);

  // Compute the transform per state. Because transform-origin is 50% 50% of
  // the 320×620 box, the visual center of the phone after `translate(tx, ty)
  // scale(s) rotate(r)` lands at (tx + 160, ty + 310) — the scale and
  // rotation pivot around the original center, so they don't shift it.
  const cy = vp.h / 2 - PHONE_H / 2;

  // Peek slot — phone tucked into the LEFT edge, tilted clockwise so the
  // top leans into the viewport (the cassette arc lives on the right; we
  // stay clear of it on purpose).
  const peekTx    = -150;
  const peekRot   = 32;
  const peekScale = 0.55;

  // Hidden — well past the left edge. Used both for the first paint (entry
  // slides in from off-screen-left) and any time macOS is up.
  const hideTx    = -PHONE_W - 220;
  const hideRot   = 44;
  const hideScale = 0.42;

  // Focused — dead center, no rotation, fits the viewport with margin so the
  // dark backdrop is visible around the chassis.
  const focusScale = Math.min((vp.h - 80) / PHONE_H, (vp.w - 100) / PHONE_W);
  const focusTx    = vp.w / 2 - PHONE_W / 2 + drag.dx;
  const focusTy    = vp.h / 2 - PHONE_H / 2 + drag.dy;

  const visible = entered && macPhase === 'intro';
  let transform, opacity;
  if (!visible) {
    transform = `translate(${hideTx}px, ${cy}px) rotate(${hideRot}deg) scale(${hideScale})`;
    opacity   = entered ? 1 : 0;
  } else if (focused) {
    transform = `translate(${focusTx}px, ${focusTy}px) rotate(0deg) scale(${focusScale})`;
    opacity   = 1;
  } else {
    transform = `translate(${peekTx}px, ${cy}px) rotate(${peekRot}deg) scale(${peekScale})`;
    opacity   = 1;
  }

  const hostStyle = {
    transform,
    opacity,
    transition: drag.dragging ? 'none' : undefined,
  };

  const hostClasses = [
    'phone-overlay-host',
    focused ? 'focused' : 'peek',
    drag.dragging ? 'dragging' : '',
  ].filter(Boolean).join(' ');

  return (
    <div className="phone-overlay-root" data-platform="ios">
      <div className="phone-overlay-backdrop"
           data-active={focused ? 'true' : 'false'}
           onClick={onBackdropClick}/>
      <div className={hostClasses}
           style={hostStyle}
           onClick={!focused ? onPeekClick : undefined}>
        <div className="phone-drag-handle phone-drag-top"    onPointerDown={startDrag}/>
        <div className="phone-drag-handle phone-drag-bottom" onPointerDown={startDrag}/>
        <PhoneShell tweaks={overlayTweaks} setTweak={setTweak} audioApi={audioApi}/>
      </div>
    </div>
  );
}

/* ============== MacShell — the desktop, chassis stage, windows, dock ====== */
function MacShell({ tweaks, setTweak, audioApi }) {
  const { phase, progress, warmState, powerOn, powerOff } = usePowerOn(tweaks.skipIntro);

  const [windows, setWindows] = useState([]);
  const [zCounter, setZ] = useState(10);
  const [poweringOff, setPoweringOff] = useState(false);
  const [bootKey, setBootKey] = useState(0);
  const [spotlightOpen, setSpotlightOpen] = useState(false);
  const [ncOpen, setNcOpen] = useState(false);
  const [selectedDesktopIcons, setSelectedDesktopIcons] = useState(() => new Set());
  const [dragCircle, setDragCircle] = useState(null);
  const [hasRoomForPhone, setHasRoomForPhone] = useState(
    typeof window !== 'undefined' && window.innerWidth >= PHONE_OVERLAY_MIN_W
  );

  useEffect(() => {
    const onResize = () => setHasRoomForPhone(window.innerWidth >= PHONE_OVERLAY_MIN_W);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  const screenRef = useRef(null);
  const desktopAreaRef = useRef(null);
  const dragInfoRef = useRef(null);

  function selectOnly(id) {
    setSelectedDesktopIcons(new Set([id]));
  }

  function handleDesktopMouseDown(e) {
    if (e.button !== 0) return;
    if (e.target.closest('.desktop-icon')) return;
    const rect = desktopAreaRef.current?.getBoundingClientRect();
    if (!rect) return;
    dragInfoRef.current = {
      startX: e.clientX - rect.left,
      startY: e.clientY - rect.top,
      clientX: e.clientX,
      clientY: e.clientY,
      dragging: false,
    };
  }

  useEffect(() => {
    function onMove(e) {
      const info = dragInfoRef.current;
      if (!info) return;
      const rect = desktopAreaRef.current?.getBoundingClientRect();
      if (!rect) return;

      if (!info.dragging) {
        const dx = e.clientX - info.clientX;
        const dy = e.clientY - info.clientY;
        if (Math.hypot(dx, dy) < 5) return;
        info.dragging = true;
      }

      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      const cx = info.startX;
      const cy = info.startY;
      const radius = Math.hypot(x - cx, y - cy);
      setDragCircle({ cx, cy, r: radius });

      const next = new Set();
      const nodes = desktopAreaRef.current.querySelectorAll('.desktop-icon');
      nodes.forEach(node => {
        const r = node.getBoundingClientRect();
        const ix = r.left - rect.left;
        const iy = r.top - rect.top;
        const nx = Math.max(ix, Math.min(cx, ix + r.width));
        const ny = Math.max(iy, Math.min(cy, iy + r.height));
        if (Math.hypot(cx - nx, cy - ny) <= radius) {
          next.add(node.dataset.iconId);
        }
      });
      setSelectedDesktopIcons(next);
    }
    function onUp() {
      const info = dragInfoRef.current;
      if (!info) return;
      const wasDragging = info.dragging;
      dragInfoRef.current = null;
      setDragCircle(null);
      if (!wasDragging) setSelectedDesktopIcons(new Set());
    }
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
    };
  }, []);

  // Cmd+K / Cmd+Space → spotlight
  useEffect(() => {
    function onKey(e) {
      if ((e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'K')) {
        e.preventDefault();
        if (phase === 'desktop') setSpotlightOpen(o => !o);
      }
    }
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [phase]);

  // Exiting fullscreen from the desktop → power off back to the screensaver.
  useEffect(() => {
    function onFsChange() {
      const fsEl = document.fullscreenElement || document.webkitFullscreenElement;
      if (!fsEl && phase === 'desktop') shutdown();
    }
    document.addEventListener('fullscreenchange', onFsChange);
    document.addEventListener('webkitfullscreenchange', onFsChange);
    return () => {
      document.removeEventListener('fullscreenchange', onFsChange);
      document.removeEventListener('webkitfullscreenchange', onFsChange);
    };
  }, [phase]);

  const stagger = useRef(0);
  function nextPos(w, h) {
    const i = stagger.current++ % 6;
    const screen = screenRef.current?.getBoundingClientRect();
    const sw = screen?.width  ?? 800;
    const sh = screen?.height ?? 600;
    return {
      x: Math.max(20, Math.min(sw - w - 20, 60 + i * 32)),
      y: Math.max(40, Math.min(sh - h - 30, 60 + i * 26)),
    };
  }

  function focusWin(id) {
    setZ(z => {
      const nz = z + 1;
      setWindows(ws => ws.map(w => w.id === id ? { ...w, z: nz } : w));
      return nz;
    });
  }
  function closeWin(id) { setWindows(ws => ws.filter(w => w.id !== id)); }
  function moveWin(id, x, y) { setWindows(ws => ws.map(w => w.id === id ? { ...w, x, y } : w)); }
  function resizeWin(id, ww, hh) { setWindows(ws => ws.map(w => w.id === id ? { ...w, w: ww, h: hh } : w)); }

  function navigateWin(id, kind, payload) {
    const titleMap = {
      work:    'Work',
      side:    'Side Projects',
      blogs:   'Writing',
      about:   'About Me',
      browser: 'Resume — Safari',
      address: 'Contacts',
      music:   'Music',
    };
    const newId = payload ? `${kind}:${payload.id}` : kind;
    setWindows(ws => ws.map(w => {
      if (w.id !== id) return w;
      const history = [...(w.history || []), { id: w.id, kind: w.kind, payload: w.payload, title: w.title }];
      return {
        ...w,
        id: newId, kind, payload,
        title: payload?.title || payload?.name || titleMap[kind] || w.title,
        history,
        future: [],
      };
    }));
  }
  function navigateBack(id) {
    setWindows(ws => ws.map(w => {
      if (w.id !== id) return w;
      const history = [...(w.history || [])];
      const prev = history.pop();
      if (!prev) return w;
      const future = [{ id: w.id, kind: w.kind, payload: w.payload, title: w.title }, ...(w.future || [])];
      return { ...w, ...prev, history, future };
    }));
  }
  function navigateForward(id) {
    setWindows(ws => ws.map(w => {
      if (w.id !== id) return w;
      const future = [...(w.future || [])];
      const next = future.shift();
      if (!next) return w;
      const history = [...(w.history || []), { id: w.id, kind: w.kind, payload: w.payload, title: w.title }];
      return { ...w, ...next, history, future };
    }));
  }
  function zoomWin(id) {
    setWindows(ws => ws.map(w => {
      if (w.id !== id) return w;
      const screen = screenRef.current?.getBoundingClientRect();
      const sw = screen?.width  ?? 800;
      const sh = screen?.height ?? 600;
      if (w._zoomed) return { ...w, ...w._prev, _zoomed: false, _prev: null };
      return {
        ...w,
        _prev: { x: w.x, y: w.y, w: w.w, h: w.h },
        _zoomed: true,
        x: 12, y: 36,
        w: sw - 24, h: sh - 80,
      };
    }));
  }

  function openWindow(kind, payload) {
    if (kind === 'trash') { shutdown(); return; }
    const id = payload ? `${kind}:${payload.id}` : kind;
    const existing = windows.find(w => w.id === id);
    if (existing) { focusWin(id); return; }

    const defs = {
      work:    { title: 'Work',            w: 720, h: 440 },
      side:    { title: 'Side Projects',   w: 720, h: 440 },
      blogs:   { title: 'Writing',         w: 720, h: 440 },
      browser: { title: 'Resume — Safari', w: 760, h: 540 },
      address: { title: 'Contacts',        w: 660, h: 540 },
      about:   { title: 'About Me',        w: 580, h: 540 },
      control: { title: 'System Settings', w: 640, h: 480 },
      music:   { title: 'Music',           w: 740, h: 520 },
      workItem:{ title: payload?.name,     w: 580, h: 480 },
      sideItem:{ title: payload?.name,     w: 540, h: 420 },
      post:    { title: payload?.title,    w: 600, h: 500 },
    };
    const def = defs[kind];
    if (!def) return;
    const pos = nextPos(def.w, def.h);
    const nz = zCounter + 1;
    setZ(nz);
    setWindows(ws => [...ws, { id, kind, payload, ...def, ...pos, z: nz }]);
  }

  function handleChassisClick(e) {
    e.stopPropagation();
    if (phase === 'intro') {
      const el = document.documentElement;
      const fn = el.requestFullscreen || el.webkitRequestFullscreen;
      if (fn) fn.call(el).catch(() => {});
    }
    powerOn();
  }

  function shutdown() {
    setPoweringOff(true);
    setTimeout(() => {
      setWindows([]);
      setPoweringOff(false);
      stagger.current = 0;
      setBootKey(k => k + 1);
      powerOff();
    }, 750);
  }
  function restart() { shutdown(); }

  // Chassis transform — at progress=1 the CRT screen should exactly cover the
  // viewport, so the fade from chassis to .crt-screen.full has no visible jump.
  // endScale is recomputed on resize; ty(1) compensates for the bezel-centered
  // origin so the screen's final center lands at the viewport center.
  const [endScale, setEndScale] = useState(() => coverScale());
  useEffect(() => {
    const onResize = () => setEndScale(coverScale());
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);
  const startScale = 0.62;
  const scale = startScale + (endScale - startScale) * progress;
  const ty = CRT_ORIGIN_OFFSET_Y * progress;

  const fullScreen = phase === 'desktop';
  const activeWin = windows.length ? windows.reduce((a, b) => a.z > b.z ? a : b) : null;
  const activeId = activeWin?.id ?? null;
  const runningKinds = new Set(windows.map(w => w.kind === 'workItem' ? 'work'
                                  : w.kind === 'sideItem' ? 'side'
                                  : w.kind === 'post' ? 'blogs'
                                  : w.kind));

  const appNameMap = {
    work:'Finder', side:'Finder', blogs:'Finder', workItem:'Preview', sideItem:'Preview',
    post:'Pages', browser:'Safari', address:'Contacts', about:'About', control:'System Settings',
    music:'Music',
  };
  const activeAppName = activeWin ? appNameMap[activeWin.kind] : 'Finder';

  return (
    <>
      {/* Chassis stage */}
      <div className="stage" style={{ opacity: fullScreen ? 0 : 1, transition: 'opacity 250ms', pointerEvents: fullScreen ? 'none' : 'auto' }}>
        <div className="chassis-wrap"
             style={{
               transform: `translateY(${ty}px) scale(${scale})`,
               cursor: phase === 'intro' ? 'pointer' : 'default',
             }}>
          <div className="chassis" onClick={handleChassisClick}/>
          <div className="chassis-face" onClick={handleChassisClick}/>
          <div className="crt-bezel">
            <div className="crt-screen" onClick={handleChassisClick}>
              {phase !== 'desktop' && <Screensaver/>}
              <BootSplash key={bootKey} hidden={phase !== 'booting'}/>
              {warmState && (
                <div className={`crt-poweron state-${warmState}`}>
                  <div className="beam"/>
                  <div className="scanlines"/>
                </div>
              )}
            </div>
          </div>
          <div className="chassis-nameplate">
            <span>Makhlay™ · Personal Computer</span>
            <span className="led"/>
          </div>
          <div className="chassis-vents"><div/><div/></div>
          <div className={`chassis-floppy ${audioApi.active ? 'loaded' : ''}`}
               onClick={(e) => { e.stopPropagation(); if (audioApi.active) audioApi.eject(); }}
               title={audioApi.active ? 'Click to eject' : ''}/>
        </div>

        {phase === 'intro' && (
          <CassetteStage
            activeCassetteId={audioApi.active?.id}
            onPick={audioApi.pick}
          />
        )}
      </div>

      {phase === 'desktop' && (
        <div className="version-pill">makOS Makhlay 15.2 · build 26.04.26</div>
      )}

      {fullScreen && (
        <div className="crt-screen full" style={{
          position: 'fixed', inset: 0, borderRadius: 0, boxShadow: 'none', zIndex: 50,
        }}>
          <div id="screen-root" className="screen-root" ref={screenRef}>
            <div className="wallpaper"/>

            <MenuBar
              openWindow={openWindow}
              onShutdown={shutdown}
              onRestart={restart}
              onSpotlight={() => setSpotlightOpen(o => !o)}
              activeAppName={activeAppName}
              musicNode={audioApi.active && (
                <MusicIndicator
                  data={audioApi.active}
                  paused={audioApi.paused}
                  progress={audioApi.progress}
                  duration={audioApi.duration}
                  onTogglePlay={audioApi.togglePlay}
                  onEject={audioApi.eject}
                />
              )}
            />

            <div className="desktop-area" ref={desktopAreaRef} onMouseDown={handleDesktopMouseDown}>
              <div className="desktop-icons">
                <DesktopIcon id="work" icon={<WorkIcon/>} label="Work"
                             isSelected={selectedDesktopIcons.has('work')} onSelect={selectOnly}
                             onOpen={() => openWindow('work')}/>
                <DesktopIcon id="side" icon={<FolderIcon/>} label="Side Projects"
                             isSelected={selectedDesktopIcons.has('side')} onSelect={selectOnly}
                             onOpen={() => openWindow('side')}/>
                <DesktopIcon id="blogs" icon={<NotesIcon/>} label="Writing"
                             isSelected={selectedDesktopIcons.has('blogs')} onSelect={selectOnly}
                             onOpen={() => openWindow('blogs')}/>
                <DesktopIcon id="about" icon={<AboutIcon/>} label="About Me"
                             isSelected={selectedDesktopIcons.has('about')} onSelect={selectOnly}
                             onOpen={() => openWindow('about')}/>
              </div>
              {dragCircle && (
                <div className="desktop-drag-circle"
                     style={{
                       left: dragCircle.cx - dragCircle.r,
                       top: dragCircle.cy - dragCircle.r,
                       width: dragCircle.r * 2,
                       height: dragCircle.r * 2,
                     }}/>
              )}
            </div>

            {ncOpen && <NotificationCenter openWindow={openWindow} onDismiss={() => setNcOpen(false)}/>}

            {windows.map(w => (
              <MacWindow key={w.id} win={w} active={w.id === activeId}
                onFocus={() => focusWin(w.id)}
                onClose={() => closeWin(w.id)}
                onMove={(x,y) => moveWin(w.id, x, y)}
                onResize={(ww,hh) => resizeWin(w.id, ww, hh)}
                onZoom={() => zoomWin(w.id)}
                toolbar={toolbarFor(w, () => navigateBack(w.id), () => navigateForward(w.id))}
                noBorder={['workItem','sideItem','post','about'].includes(w.kind)}>
                {renderWindowBody(w, openWindow, tweaks, setTweak, (kind, payload) => navigateWin(w.id, kind, payload), audioApi)}
              </MacWindow>
            ))}

            <Dock open={openWindow} runningKinds={runningKinds}/>

            <Spotlight open={spotlightOpen} onClose={() => setSpotlightOpen(false)} openWindow={openWindow}/>

            {poweringOff && (
              <div className="crt-poweroff"><div className="collapse"/></div>
            )}
          </div>
        </div>
      )}

      {/* iPhone peek overlay sits above both the intro chassis and the booted
          desktop. Lives in the top-level fragment (not inside the crt-screen
          full block) so it stays mounted across the homepage → desktop
          transition. macPhase drives slide-out: visible while in 'intro',
          tucked off-screen-left while macOS is up, slides back when the
          user shuts down. */}
      {hasRoomForPhone && (
        <PhoneOverlay tweaks={tweaks} setTweak={setTweak} audioApi={audioApi}
                      macPhase={phase}/>
      )}
    </>
  );
}

Object.assign(window, { MacShell, useClock, formatClock });
