// explorer.jsx — Red-pill destination, rebuilt as a Matrix-style file system.
// No text input. Folder list → click to drill in → back/breadcrumb to return.
// Always reads green; pure click navigation. Bottom strip = social links.

const { useState: useEx, useEffect: useExFX, useRef: useExR, useCallback: useExCB } = React;

// All paths root at ~/oleksiukstepan. Each "folder" renders its own view.
// Keeping the tree flat (one level deep) keeps interaction obvious — every
// node either is a folder you open or a link you launch.

function FileExplorer({ theme, onBack, backdrop = 'cinema', skillsLayout = 'compact', initialPath = ['~'], titleBarVariant = 'terminal' }) {
  const c = theme;
  const [path, setPath] = useEx(initialPath);          // breadcrumb stack
  const [selected, setSelected] = useEx(0);      // keyboard focus row
  // Fade-in on mount so the explorer doesn't slap onscreen the instant the
  // glitch transition hands off. Sits behind the fading-out glitch overlay,
  // so the two crossfade naturally.
  const [appeared, setAppeared] = useEx(false);
  useExFX(() => {
    const t = setTimeout(() => setAppeared(true), 30);
    return () => clearTimeout(t);
  }, []);

  const cwd = path[path.length - 1];

  // ── Folder catalogue ────────────────────────────────────────────────
  const ROOT = [
    { kind: 'dir', name: 'about',     hint: 'identity & bio',           open: 'about'    },
    { kind: 'dir', name: 'skills',    hint: 'tech stack',               open: 'skills'   },
    { kind: 'dir', name: 'projects',  hint: 'shipped work',             open: 'projects' },
    { kind: 'dir', name: 'contacts',  hint: 'reach out',                open: 'contacts' },
    { kind: 'file', name: 'README.md',  hint: 'a note from the operator', open: 'readme' },
  ];

  const rows = cwd === '~' ? ROOT : [];

  // Keyboard nav at root.
  useExFX(() => {
    const onKey = (e) => {
      if (cwd !== '~') return;
      if (e.key === 'ArrowDown') { e.preventDefault(); setSelected((i) => Math.min(rows.length - 1, i + 1)); }
      if (e.key === 'ArrowUp')   { e.preventDefault(); setSelected((i) => Math.max(0, i - 1)); }
      if (e.key === 'Enter')     {
        e.preventDefault();
        const row = rows[selected];
        if (!row) return;
        if (row.href) window.open(row.href, '_blank', 'noopener');
        else if (row.open) setPath((p) => [...p, row.open]);
      }
      if (e.key === 'Escape') { e.preventDefault(); onBack(); }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [cwd, selected, rows.length, onBack]);

  const open = (row) => {
    if (row.href) window.open(row.href, '_blank', 'noopener');
    else if (row.open) setPath((p) => [...p, row.open]);
  };

  const goUp = () => {
    if (path.length > 1) setPath((p) => p.slice(0, -1));
    else onBack();
  };

  const breadcrumb = path.join('/').replace('~/', '~ / ').replace(/^~$/, '~');

  // ── Backdrop variants ──────────────────────────────────────────────
  // 'cinema' — solid dark gradient, rain hidden (most readable)
  // 'dim'    — semi-transparent dark, rain peeks through ~15%
  // 'sides'  — centered window with side margins, rain visible on sides
  // 'glass'  — backdrop-filter blur, rain visible but blurred behind glass
  const baseStyle = {
    position: 'absolute',
    color: c.fg,
    fontFamily: c.fontMono,
    fontSize: 14,
    lineHeight: 1.55,
    display: 'flex',
    flexDirection: 'column',
    zIndex: 30,
    overflow: 'hidden',
    opacity: appeared ? 1 : 0,
    transform: appeared ? 'scale(1)' : 'scale(0.985)',
    transition: 'opacity 0.55s ease-out, transform 0.55s ease-out',
  };
  let panelStyle;
  if (backdrop === 'sides') {
    panelStyle = {
      ...baseStyle,
      top: 0, bottom: 0, left: 120, right: 120,
      background: `linear-gradient(180deg, ${c.bgDeep} 0%, ${c.bg} 100%)`,
      borderLeft: `1px solid ${c.fgDim}`,
      borderRight: `1px solid ${c.fgDim}`,
      boxShadow: `0 0 40px rgba(0,255,65,0.15), inset 0 0 60px rgba(0,0,0,0.5)`,
    };
  } else if (backdrop === 'dim') {
    panelStyle = {
      ...baseStyle, inset: 0,
      background: `linear-gradient(180deg, rgba(0,15,5,0.88) 0%, rgba(0,8,3,0.92) 100%)`,
    };
  } else if (backdrop === 'glass') {
    panelStyle = {
      ...baseStyle, inset: 0,
      background: `linear-gradient(180deg, rgba(0,15,5,0.55) 0%, rgba(0,8,3,0.45) 100%)`,
      backdropFilter: 'blur(6px)',
      WebkitBackdropFilter: 'blur(6px)',
    };
  } else {
    // cinema (default)
    panelStyle = {
      ...baseStyle, inset: 0,
      background: `linear-gradient(180deg, ${c.bgDeep} 0%, ${c.bg} 100%)`,
    };
  }

  return (
    <div style={panelStyle}>
      {/* Title bar — looks like a window chrome but in green ASCII */}
      <ExTitleBar theme={c} breadcrumb={breadcrumb} onBack={onBack} variant={titleBarVariant} />

      {/* Main viewing area */}
      <div style={{ flex: 1, overflowY: 'auto', padding: '22px 30px 12px', position: 'relative' }}>
        {cwd === '~'        && <DirView theme={c} rows={rows} selected={selected} setSelected={setSelected} onOpen={open} />}
        {cwd === 'about'    && <AboutView    theme={c} />}
        {cwd === 'skills'   && <SkillsView   theme={c} layout={skillsLayout} />}
        {cwd === 'projects' && <ProjectsView theme={c} />}
        {cwd === 'contacts' && <ContactsView theme={c} />}
        {cwd === 'readme'   && <ReadmeView   theme={c} />}

        {/* Up/back row inside content for non-root */}
        {cwd !== '~' && (
          <div style={{
            marginTop: 24,
            paddingTop: 14,
            borderTop: `1px dashed ${c.fgDim}`,
            display: 'flex',
            gap: 14,
            color: c.fgDim,
          }}>
            <button onClick={goUp} style={asciiBtnStyle(c, true)}>← cd ..</button>
            <button onClick={() => setPath(['~'])} style={asciiBtnStyle(c)}>~ home</button>
          </div>
        )}
      </div>

      {/* Bottom strip — socials, always visible */}
      <ExSocials theme={c} />
    </div>
  );
}

// ── Title bar ───────────────────────────────────────────────────────────

// ── Title bar variants ─────────────────────────────────────────────────
function ExTitleBar({ theme, breadcrumb, onBack, variant = 'terminal' }) {
  if (variant === 'macos')    return <ExTitleBarMacOS    theme={theme} breadcrumb={breadcrumb} onBack={onBack} />;
  if (variant === 'tui')      return <ExTitleBarTUI      theme={theme} breadcrumb={breadcrumb} onBack={onBack} />;
  if (variant === 'bios')     return <ExTitleBarBIOS     theme={theme} breadcrumb={breadcrumb} onBack={onBack} />;
  if (variant === 'norton')   return <ExTitleBarNorton   theme={theme} breadcrumb={breadcrumb} onBack={onBack} />;
  return <ExTitleBarTerminal theme={theme} breadcrumb={breadcrumb} onBack={onBack} />;
}

// Variant A · Terminal — the current/default. Flat single-row prompt.
function ExTitleBarTerminal({ theme, breadcrumb, onBack }) {
  const c = theme;
  return (
    <div style={{
      padding: '10px 18px',
      borderBottom: `1px solid ${c.accentSoft}`,
      display: 'flex',
      alignItems: 'center',
      gap: 14,
      color: c.fgDim,
      fontSize: 12,
      letterSpacing: 1.5,
      textTransform: 'uppercase',
      background: 'rgba(0,0,0,0.45)',
    }}>
      <span style={{ color: c.accent, textShadow: `0 0 6px ${c.accent}` }}>▮ matrix.fs</span>
      <span style={{ flex: 1, color: c.fg, textShadow: `0 0 4px ${c.fg}`, textTransform: 'none', letterSpacing: 0.5 }}>
        {PROFILE.handle}@matrix:<span style={{ color: c.fgGlow }}>{breadcrumb}</span>
      </span>
      <span style={{ color: c.fgDim }}>red_pill // online</span>
      <button
        onClick={onBack}
        style={{
          border: `1px solid ${c.fgDim}`,
          background: 'transparent',
          color: c.fg,
          fontFamily: c.fontMono,
          fontSize: 11,
          padding: '3px 10px',
          cursor: 'pointer',
          letterSpacing: 1.5,
        }}
      >× exit</button>
    </div>
  );
}

// Variant B · Classic chrome — Win95 / DOS-window feel. Title text on the
// left with a small app glyph, a single beveled [×] close button on the
// right glowing red. No traffic lights.
function ExTitleBarMacOS({ theme, breadcrumb, onBack }) {
  return <ExTitleBarClassic theme={theme} breadcrumb={breadcrumb} onBack={onBack} />;
}

function ExTitleBarClassic({ theme, breadcrumb, onBack }) {
  const c = theme;
  return (
    <div style={{
      padding: '6px 10px 6px 14px',
      borderBottom: `1px solid ${c.fgDim}`,
      background: `linear-gradient(180deg, rgba(0,255,65,0.12), rgba(0,0,0,0.55))`,
      display: 'flex',
      alignItems: 'center',
      gap: 12,
      minHeight: 30,
    }}>
      <span style={{
        color: c.fgGlow, fontFamily: c.fontMono,
        textShadow: `0 0 6px ${c.fg}`,
        fontSize: 14,
      }}>■</span>
      <span style={{
        flex: 1,
        color: c.fg,
        fontFamily: c.fontMono,
        fontSize: 12,
        letterSpacing: 1.5,
        textShadow: `0 0 4px ${c.fg}`,
        whiteSpace: 'nowrap',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
      }}>
        matrix.fs <span style={{ color: c.fgDim }}>—</span> {breadcrumb}
      </span>
      <button
        onClick={onBack}
        aria-label="close"
        title="close"
        style={{
          width: 22, height: 22, padding: 0,
          background: `linear-gradient(180deg, #401015 0%, #200508 100%)`,
          border: `1px solid ${c.redPill}`,
          color: c.redPill,
          fontFamily: c.fontMono,
          fontSize: 14,
          lineHeight: 1,
          cursor: 'pointer',
          boxShadow: `0 0 8px rgba(255,34,64,0.45), inset 0 1px 0 rgba(255,255,255,0.15), inset 0 -1px 0 rgba(0,0,0,0.5)`,
          textShadow: `0 0 6px ${c.redPill}`,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}
      >×</button>
    </div>
  );
}

// Variant C · TUI box — single-row ASCII box top with bracketed sections.
function ExTitleBarTUI({ theme, breadcrumb, onBack }) {
  const c = theme;
  return (
    <div style={{
      padding: '6px 14px',
      borderBottom: `1px solid ${c.fgDim}`,
      fontFamily: c.fontMono, fontSize: 12,
      color: c.fgDim,
      background: 'rgba(0,0,0,0.5)',
      display: 'flex', alignItems: 'center',
      whiteSpace: 'nowrap', overflow: 'hidden',
    }}>
      <span>{'╭──'}</span>
      <span style={{ color: c.accent, textShadow: `0 0 6px ${c.accent}`, margin: '0 6px' }}>[ matrix.fs ]</span>
      <span>{'──'}</span>
      <span style={{ color: c.fg, margin: '0 6px', textShadow: `0 0 4px ${c.fg}` }}>[ {PROFILE.handle}@host ]</span>
      <span>{'──'}</span>
      <span style={{ color: c.fgGlow, margin: '0 6px', textShadow: `0 0 4px ${c.fg}` }}>[ {breadcrumb} ]</span>
      <span style={{ flex: 1, overflow: 'hidden' }}>{'─'.repeat(80)}</span>
      <button onClick={onBack} style={{
        color: c.fgGlow, background: 'transparent', border: 'none',
        fontFamily: c.fontMono, fontSize: 12, cursor: 'pointer',
        padding: 0, textShadow: `0 0 4px ${c.fg}`,
      }}>──[ × ]</button>
      <span>{'──╮'}</span>
    </div>
  );
}

// Variant D · BIOS — ALL-CAPS status line with live uptime counter.
function ExTitleBarBIOS({ theme, breadcrumb, onBack }) {
  const c = theme;
  const [uptime, setUptime] = useEx(0);
  useExFX(() => {
    const start = Date.now();
    const iv = setInterval(() => setUptime(Math.floor((Date.now() - start) / 1000)), 1000);
    return () => clearInterval(iv);
  }, []);
  const h = String(Math.floor(uptime / 3600)).padStart(2, '0');
  const m = String(Math.floor((uptime % 3600) / 60)).padStart(2, '0');
  const s = String(uptime % 60).padStart(2, '0');
  return (
    <div style={{
      padding: '8px 16px',
      borderBottom: `1px solid ${c.fgDim}`,
      fontFamily: c.fontMono, fontSize: 11,
      color: c.fg, letterSpacing: 1.5,
      textTransform: 'uppercase',
      background: 'rgba(0,0,0,0.55)',
      display: 'flex', alignItems: 'center', gap: 10,
    }}>
      <span style={{ color: c.fgDim }}>::</span>
      <span style={{ color: c.accent, textShadow: `0 0 6px ${c.accent}` }}>matrix.fs v0.4.2</span>
      <span style={{ color: c.fgDim }}>::</span>
      <span>{PROFILE.handle}@host</span>
      <span style={{ color: c.fgDim }}>::</span>
      <span style={{ color: c.fgGlow, textShadow: `0 0 4px ${c.fg}` }}>{breadcrumb}</span>
      <span style={{ color: c.fgDim }}>::</span>
      <span>uptime {h}:{m}:{s}</span>
      <span style={{ flex: 1 }} />
      <span style={{ color: c.fgDim }}>::</span>
      <button onClick={onBack} style={{
        color: c.fg, background: 'transparent', border: 'none',
        fontFamily: c.fontMono, fontSize: 11, cursor: 'pointer',
        padding: 0, letterSpacing: 1.5, textTransform: 'uppercase',
      }}>[× exit]</button>
      <span style={{ color: c.fgDim }}>::</span>
    </div>
  );
}

// Variant E · Norton Commander — heavy double-line border, F-key exit.
function ExTitleBarNorton({ theme, breadcrumb, onBack }) {
  const c = theme;
  return (
    <div style={{
      padding: '6px 14px',
      borderBottom: `1px solid ${c.fgDim}`,
      fontFamily: c.fontMono, fontSize: 12,
      color: c.fgDim,
      background: 'rgba(0,0,0,0.6)',
      display: 'flex', alignItems: 'center',
      whiteSpace: 'nowrap', overflow: 'hidden',
    }}>
      <span>{'╔══'}</span>
      <span style={{
        color: c.fgGlow, margin: '0 6px',
        textShadow: `0 0 8px ${c.fg}, 0 0 16px ${c.fg}`,
        fontWeight: 700,
      }}>[ matrix.fs ]</span>
      <span>{'══'}</span>
      <span style={{ color: c.fg, margin: '0 6px', textShadow: `0 0 4px ${c.fg}` }}>[ {PROFILE.handle}@host ]</span>
      <span>{'══'}</span>
      <span style={{ color: c.accent, margin: '0 6px', textShadow: `0 0 4px ${c.accent}` }}>[ {breadcrumb} ]</span>
      <span style={{ flex: 1, overflow: 'hidden' }}>{'═'.repeat(80)}</span>
      <button onClick={onBack} style={{
        color: c.fgGlow, background: 'transparent', border: 'none',
        fontFamily: c.fontMono, fontSize: 12, cursor: 'pointer',
        padding: 0, textShadow: `0 0 4px ${c.fg}`,
      }}>══[ F10 exit ]</button>
      <span>{'══╗'}</span>
    </div>
  );
}

// ── Root directory listing ──────────────────────────────────────────────
// Renders an ls -la-style table. Click a row to open; arrow-keys focus.

function DirView({ theme, rows, selected, setSelected, onOpen }) {
  const c = theme;
  return (
    <div>
      <PromptLine theme={c} text="ls -la ~/oleksiukstepan" />
      <div style={{ color: c.fgDim, marginBottom: 12 }}>
        total {rows.length} · use ↑↓ Enter to navigate, or click a row.
      </div>
      <div style={{
        border: `1px solid ${c.fgDim}`,
        boxShadow: `0 0 18px ${c.accentSoft}, inset 0 0 30px rgba(0,0,0,0.5)`,
        padding: '10px 14px 12px',
        background: 'rgba(0,0,0,0.4)',
      }}>
        <div style={{
          display: 'grid',
          gridTemplateColumns: '110px 60px 28px 1fr 1.4fr',
          gap: 12,
          color: c.fgDim,
          fontSize: 11,
          textTransform: 'uppercase',
          letterSpacing: 2,
          padding: '4px 6px',
          borderBottom: `1px dashed ${c.fgDim}`,
          marginBottom: 4,
        }}>
          <div>perms</div>
          <div>size</div>
          <div></div>
          <div>name</div>
          <div>note</div>
        </div>
        {rows.map((r, i) => (
          <DirRow
            key={r.name}
            theme={c}
            row={r}
            active={i === selected}
            onHover={() => setSelected(i)}
            onClick={() => onOpen(r)}
          />
        ))}
      </div>
      <div style={{ marginTop: 18, color: c.fgDim, fontSize: 12 }}>
        ── tip: press <span style={{ color: c.fgGlow }}>Esc</span> to leave the matrix.
      </div>
    </div>
  );
}

function DirRow({ theme, row, active, onHover, onClick }) {
  const c = theme;
  const isDir = row.kind === 'dir';
  const perms = isDir ? 'drwxr-xr-x' : '-rw-r--r--';
  const size  = isDir ? '4.0K' : (row.name.endsWith('.pdf') ? '212K' : '1.2K');
  const icon  = isDir ? '▸' : '·';
  const trail = isDir ? '/' : '';
  return (
    <button
      onClick={onClick}
      onMouseEnter={onHover}
      style={{
        all: 'unset',
        cursor: 'pointer',
        display: 'grid',
        gridTemplateColumns: '110px 60px 28px 1fr 1.4fr',
        gap: 12,
        padding: '6px 6px',
        fontFamily: c.fontMono,
        fontSize: 14,
        color: c.fg,
        background: active ? c.accentSoft : 'transparent',
        boxShadow: active ? `inset 0 0 0 1px ${c.accent}, 0 0 14px ${c.accentSoft}` : 'none',
        transition: 'background 0.12s, box-shadow 0.12s',
        width: '100%',
        boxSizing: 'border-box',
      }}
    >
      <span style={{ color: c.fgDim }}>{perms}</span>
      <span style={{ color: c.fgDim }}>{size}</span>
      <span style={{ color: c.accent, textShadow: `0 0 8px ${c.accent}` }}>{icon}</span>
      <span style={{ color: isDir ? c.fgGlow : c.fg, textShadow: isDir ? `0 0 6px ${c.fg}` : 'none' }}>
        {row.name}<span style={{ color: c.fgDim }}>{trail}</span>
      </span>
      <span style={{ color: c.fgDim, fontSize: 13 }}>{row.hint}</span>
    </button>
  );
}

// ── Sub-views ──────────────────────────────────────────────────────────

function PromptLine({ theme, text }) {
  const c = theme;
  return (
    <div style={{ marginBottom: 10 }}>
      <span style={{ color: c.accent, textShadow: `0 0 6px ${c.accent}` }}>{PROFILE.handle}@matrix</span>
      <span style={{ color: c.fgDim }}>:~$ </span>
      <span style={{ color: c.fgGlow, textShadow: `0 0 6px ${c.fg}` }}>{text}</span>
    </div>
  );
}

function SectionHeader({ theme, text }) {
  const c = theme;
  return (
    <div style={{
      color: c.fgGlow,
      textShadow: `0 0 8px ${c.fg}`,
      textTransform: 'uppercase',
      letterSpacing: 5,
      fontSize: 13,
      margin: '4px 0 14px',
    }}>── {text} ──────────</div>
  );
}

function AboutView({ theme }) {
  const c = theme;
  return (
    <div>
      <PromptLine theme={c} text="cat about/bio.txt" />
      <SectionHeader theme={c} text="about" />
      {/* Two columns: identity + bio on the left, education on the right. */}
      <div style={{
        display: 'grid',
        gridTemplateColumns: '1fr 1fr',
        gap: 28,
        alignItems: 'start',
      }}>
        {/* Left column: identity card + bio paragraph */}
        <div style={{ color: c.fg, lineHeight: 1.75 }}>
          <div style={{
            display: 'grid',
            gridTemplateColumns: '90px 1fr',
            rowGap: 4, columnGap: 14,
            marginBottom: 16,
          }}>
            <div style={{ color: c.fgDim }}>name</div>     <div>{PROFILE.name}</div>
            <div style={{ color: c.fgDim }}>title</div>    <div>{PROFILE.title}</div>
            <div style={{ color: c.fgDim }}>location</div> <div>{PROFILE.location}</div>
          </div>
          <div style={{ color: c.fg, fontSize: 13.5 }}>
            {PROFILE.longBio}
          </div>
        </div>

        {/* Right column: Education and Qualifications */}
        <div>
          <div style={{
            color: c.accent, fontSize: 12, letterSpacing: 3,
            textTransform: 'uppercase', textShadow: `0 0 6px ${c.accent}`,
            marginBottom: 8,
          }}>▸ education and qualifications</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            {PROFILE.education.map((e) => (
              <div key={e.title} style={{
                border: `1px solid ${c.fgDim}`,
                padding: '8px 12px',
                background: 'rgba(0,0,0,0.35)',
              }}>
                <div style={{
                  display: 'flex', justifyContent: 'space-between',
                  gap: 12, alignItems: 'baseline',
                  marginBottom: 3,
                  flexWrap: 'wrap',
                }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <span style={{
                      color: c.fgGlow, fontWeight: 600,
                      textShadow: `0 0 6px ${c.fg}`,
                      fontSize: 13.5,
                    }}>{e.title}</span>
                    <span style={{ color: c.fgDim, marginLeft: 8 }}>@</span>
                    <span style={{ color: c.fg, marginLeft: 4 }}>{e.place}</span>
                  </div>
                  <div style={{
                    color: c.fgDim, fontSize: 11,
                    whiteSpace: 'nowrap',
                  }}>{e.year} · {e.mode}</div>
                </div>
                <div style={{ color: c.fg, fontSize: 12.5, opacity: 0.9, lineHeight: 1.55 }}>
                  {e.desc}
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

function SkillsView({ theme, layout = 'compact' }) {
  if (layout === 'tags')              return <SkillsTags        theme={theme} />;
  if (layout === 'tiers')             return <SkillsTiers       theme={theme} />;
  if (layout === 'dots')              return <SkillsDots        theme={theme} />;
  if (layout === 'tags-glow')         return <SkillsTagsGlow    theme={theme} />;
  if (layout === 'tags-fill')         return <SkillsTagsFill    theme={theme} />;
  if (layout === 'mini-bar')          return <SkillsMiniBar     theme={theme} />;
  if (layout === 'tree')              return <SkillsTree        theme={theme} />;
  if (layout === 'tree-hex')          return <SkillsTreeHex     theme={theme} />;
  if (layout === 'tree-status')       return <SkillsTreeStatus  theme={theme} />;
  if (layout === 'tree-glitch')       return <SkillsTreeGlitch  theme={theme} cols={2} />;
  if (layout === 'tree-glitch-3col')  return <SkillsTreeGlitch  theme={theme} cols={3} />;
  if (layout === 'tree-scan')         return <SkillsTreeScan    theme={theme} />;
  if (layout === 'stars')             return <SkillsStars       theme={theme} />;
  return <SkillsCompact theme={theme} />;
}

// Variant A · Compact bars — closest to the original. 3 columns instead of
// 2, smaller bars, no numerical percentages.
function SkillsCompact({ theme }) {
  const c = theme;
  return (
    <div>
      <PromptLine theme={c} text="ls skills/" />
      <SectionHeader theme={c} text="tech stack" />
      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(3, 1fr)',
        gap: '12px 18px',
      }}>
        {SKILLS.map((cat) => (
          <div key={cat.label} style={{
            border: `1px solid ${c.fgDim}`,
            padding: '8px 12px',
            background: 'rgba(0,0,0,0.35)',
          }}>
            <div style={{
              color: c.accent, fontSize: 11, letterSpacing: 1.5,
              textTransform: 'uppercase', textShadow: `0 0 6px ${c.accent}`,
              marginBottom: 6,
            }}>▸ {cat.label}</div>
            {cat.items.map((item, i) => {
              const pct = cat.lvl?.[i] ?? 70;
              const blocks = 12;
              const filled = Math.round((pct / 100) * blocks);
              return (
                <div key={item} style={{
                  display: 'flex', gap: 8, fontSize: 12, padding: '1px 0',
                  alignItems: 'baseline',
                }}>
                  <div style={{
                    flex: 1, color: c.fg,
                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                  }}>{item}</div>
                  <div style={{ letterSpacing: 0, fontSize: 10, fontFamily: c.fontMono }}>
                    <span style={{ color: c.fgGlow, textShadow: `0 0 4px ${c.fg}` }}>{'█'.repeat(filled)}</span>
                    <span style={{ color: c.fgDim, opacity: 0.5 }}>{'░'.repeat(blocks - filled)}</span>
                  </div>
                </div>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
}

// Variant B · Tags — just bordered tag-chips per category. No proficiency.
function SkillsTags({ theme }) {
  const c = theme;
  return (
    <div>
      <PromptLine theme={c} text="ls skills/" />
      <SectionHeader theme={c} text="tech stack" />
      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(3, 1fr)',
        gap: '16px 20px',
      }}>
        {SKILLS.map((cat) => (
          <div key={cat.label}>
            <div style={{
              color: c.accent, fontSize: 11, letterSpacing: 2,
              textTransform: 'uppercase', textShadow: `0 0 6px ${c.accent}`,
              marginBottom: 8,
            }}>▸ {cat.label}</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: '5px 6px' }}>
              {cat.items.map((item) => (
                <span key={item} style={{
                  border: `1px solid ${c.fgDim}`,
                  padding: '2px 8px',
                  fontSize: 12,
                  color: c.fg,
                  background: 'rgba(0,0,0,0.4)',
                  letterSpacing: 0.5,
                }}>{item}</span>
              ))}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// Variant C · Tiers — flatten all skills, bucket by proficiency. Skills
// grouped not by category but by how well you know them. Recruiter sees
// what you actually own.
function SkillsTiers({ theme }) {
  const c = theme;
  const all = [];
  SKILLS.forEach((cat) => {
    cat.items.forEach((item, i) => {
      const lvl = cat.lvl?.[i] ?? 70;
      all.push({ item, lvl });
    });
  });
  const core     = all.filter((s) => s.lvl >= 90).sort((a, b) => b.lvl - a.lvl);
  const strong   = all.filter((s) => s.lvl >= 75 && s.lvl < 90).sort((a, b) => b.lvl - a.lvl);
  const familiar = all.filter((s) => s.lvl < 75).sort((a, b) => b.lvl - a.lvl);
  return (
    <div>
      <PromptLine theme={c} text="cat skills/by-proficiency.txt" />
      <SectionHeader theme={c} text="tech stack" />
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 920 }}>
        {[
          { label: 'core',     items: core,     accent: c.fgGlow },
          { label: 'strong',   items: strong,   accent: c.fg },
          { label: 'familiar', items: familiar, accent: c.fgDim },
        ].map((tier) => (
          <div key={tier.label} style={{
            border: `1px solid ${c.fgDim}`,
            padding: '10px 16px',
            background: 'rgba(0,0,0,0.4)',
          }}>
            <div style={{
              display: 'flex', alignItems: 'baseline', gap: 12,
              marginBottom: 8,
            }}>
              <div style={{
                color: tier.accent,
                fontSize: 12, letterSpacing: 3, textTransform: 'uppercase',
                textShadow: `0 0 6px ${c.fg}`,
              }}>▸ {tier.label}</div>
              <div style={{ color: c.fgDim, fontSize: 11 }}>
                {tier.items.length} items
              </div>
            </div>
            <div style={{
              display: 'flex', flexWrap: 'wrap', gap: '4px 0',
              fontSize: 13, color: c.fg, lineHeight: 1.6,
            }}>
              {tier.items.map((s, i) => (
                <span key={s.item}>
                  {s.item}
                  {i < tier.items.length - 1 && (
                    <span style={{ color: c.fgDim, margin: '0 10px' }}>·</span>
                  )}
                </span>
              ))}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// Variant D · Dots — 5-dot proficiency indicator. Most visually compact
// way to convey "how well" without numbers.
function SkillsDots({ theme }) {
  const c = theme;
  return (
    <div>
      <PromptLine theme={c} text="ls skills/" />
      <SectionHeader theme={c} text="tech stack" />
      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(3, 1fr)',
        gap: '12px 18px',
      }}>
        {SKILLS.map((cat) => (
          <div key={cat.label} style={{
            border: `1px solid ${c.fgDim}`,
            padding: '8px 12px',
            background: 'rgba(0,0,0,0.35)',
          }}>
            <div style={{
              color: c.accent, fontSize: 11, letterSpacing: 1.5,
              textTransform: 'uppercase', textShadow: `0 0 6px ${c.accent}`,
              marginBottom: 6,
            }}>▸ {cat.label}</div>
            {cat.items.map((item, i) => {
              const pct = cat.lvl?.[i] ?? 70;
              const dots = 5;
              const filled = Math.round((pct / 100) * dots);
              return (
                <div key={item} style={{
                  display: 'flex', gap: 8, fontSize: 12, padding: '1px 0',
                  alignItems: 'center',
                }}>
                  <div style={{
                    letterSpacing: 1, fontSize: 11,
                    fontFamily: c.fontMono, width: 50,
                  }}>
                    <span style={{ color: c.fgGlow, textShadow: `0 0 6px ${c.fg}` }}>
                      {'●'.repeat(filled)}
                    </span>
                    <span style={{ color: c.fgDim, opacity: 0.45 }}>
                      {'○'.repeat(dots - filled)}
                    </span>
                  </div>
                  <div style={{
                    flex: 1, color: c.fg,
                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                  }}>{item}</div>
                </div>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
}

// ── Five new skills variants (next round of iteration) ─────────────────

// Variant E · Tags + glow — refines B. Same chip layout, but proficiency
// is conveyed by the text intensity (glow + colour temperature). No
// percentage numbers, no dots, no bars — just visual hierarchy.
function SkillsTagsGlow({ theme }) {
  const c = theme;
  const tagStyle = (lvl) => {
    const t = lvl / 100;
    // Three tiers: core ≥ 0.88, strong ≥ 0.72, familiar < 0.72
    if (t >= 0.88) return {
      color: c.fgGlow, textShadow: `0 0 8px ${c.fg}, 0 0 16px ${c.fg}`,
      borderColor: c.fg, opacity: 1,
    };
    if (t >= 0.72) return {
      color: c.fg, textShadow: `0 0 4px ${c.fg}`,
      borderColor: c.fgDim, opacity: 0.92,
    };
    return {
      color: c.fgDim, textShadow: 'none',
      borderColor: c.fgDim, opacity: 0.7,
    };
  };
  return (
    <div>
      <PromptLine theme={c} text="ls skills/" />
      <SectionHeader theme={c} text="tech stack" />
      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)',
        gap: '16px 20px',
      }}>
        {SKILLS.map((cat) => (
          <div key={cat.label}>
            <div style={{
              color: c.accent, fontSize: 11, letterSpacing: 2,
              textTransform: 'uppercase', textShadow: `0 0 6px ${c.accent}`,
              marginBottom: 8,
            }}>▸ {cat.label}</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: '5px 6px' }}>
              {cat.items.map((item, i) => {
                const s = tagStyle(cat.lvl?.[i] ?? 70);
                return (
                  <span key={item} style={{
                    border: `1px solid ${s.borderColor}`,
                    padding: '2px 8px', fontSize: 12,
                    background: 'rgba(0,0,0,0.4)',
                    letterSpacing: 0.5,
                    color: s.color, textShadow: s.textShadow, opacity: s.opacity,
                  }}>{item}</span>
                );
              })}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// Variant F · Tags + fill — chips have an internal gradient fill that
// represents proficiency (% width). Looks like a progress bar inside the
// chip itself. One signal: shape + horizontal fill amount.
function SkillsTagsFill({ theme }) {
  const c = theme;
  return (
    <div>
      <PromptLine theme={c} text="ls skills/" />
      <SectionHeader theme={c} text="tech stack" />
      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)',
        gap: '16px 20px',
      }}>
        {SKILLS.map((cat) => (
          <div key={cat.label}>
            <div style={{
              color: c.accent, fontSize: 11, letterSpacing: 2,
              textTransform: 'uppercase', textShadow: `0 0 6px ${c.accent}`,
              marginBottom: 8,
            }}>▸ {cat.label}</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: '5px 6px' }}>
              {cat.items.map((item, i) => {
                const lvl = cat.lvl?.[i] ?? 70;
                return (
                  <span key={item} style={{
                    border: `1px solid ${c.fgDim}`,
                    padding: '2px 8px', fontSize: 12,
                    color: c.fg, letterSpacing: 0.5,
                    background: `linear-gradient(90deg, rgba(0,255,65,0.22) 0%, rgba(0,255,65,0.22) ${lvl}%, rgba(0,0,0,0.5) ${lvl}%)`,
                  }}>{item}</span>
                );
              })}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// Variant G · Mini bar — refines D direction. Continuous CSS bar instead
// of discrete dots; sits right under the skill name. More precise.
function SkillsMiniBar({ theme }) {
  const c = theme;
  return (
    <div>
      <PromptLine theme={c} text="ls skills/" />
      <SectionHeader theme={c} text="tech stack" />
      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)',
        gap: '12px 18px',
      }}>
        {SKILLS.map((cat) => (
          <div key={cat.label} style={{
            border: `1px solid ${c.fgDim}`,
            padding: '8px 12px',
            background: 'rgba(0,0,0,0.35)',
          }}>
            <div style={{
              color: c.accent, fontSize: 11, letterSpacing: 1.5,
              textTransform: 'uppercase', textShadow: `0 0 6px ${c.accent}`,
              marginBottom: 6,
            }}>▸ {cat.label}</div>
            {cat.items.map((item, i) => {
              const lvl = cat.lvl?.[i] ?? 70;
              return (
                <div key={item} style={{ padding: '2px 0' }}>
                  <div style={{
                    color: c.fg, fontSize: 12,
                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                  }}>{item}</div>
                  <div style={{
                    height: 2, background: 'rgba(255,255,255,0.07)',
                    position: 'relative', marginTop: 1,
                  }}>
                    <div style={{
                      position: 'absolute', top: 0, left: 0, bottom: 0,
                      width: `${lvl}%`,
                      background: c.fgGlow,
                      boxShadow: `0 0 4px ${c.fg}`,
                    }} />
                  </div>
                </div>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
}

// Variant H · Tree (pure) — ASCII filesystem tree. Cleaned up: tier tags
// removed per user pref, the tree carries itself.
function SkillsTree({ theme }) {
  const c = theme;
  return <SkillsTreeBase theme={c} prompt="tree skills/" renderLeaf={({ item }) => (
    <span style={{ color: c.fg }}>{item}</span>
  )} />;
}

// Shared chrome for all tree variants. Takes a renderLeaf({item, idx, catIdx})
// callback that returns the leaf row's inner JSX (after the connector glyph).
function SkillsTreeBase({ theme, prompt, sectionLabel, renderLeaf, extraOverlay, cols = 2 }) {
  const c = theme;
  return (
    <div style={{ position: 'relative' }}>
      <PromptLine theme={c} text={prompt} />
      <SectionHeader theme={c} text={sectionLabel || 'tech stack'} />
      {/* CSS multi-column flow so categories pack tightly within each
          column without grid-row gaps. `break-inside: avoid` on each
          category keeps a label + its leaves together. */}
      <div style={{
        position: 'relative',
        fontFamily: c.fontMono, fontSize: 12.5,
        color: c.fg, lineHeight: 1.55,
        columnCount: cols,
        columnGap: cols === 3 ? 22 : 38,
      }}>
        {SKILLS.map((cat, ci) => (
          <div key={cat.label} style={{
            breakInside: 'avoid',
            WebkitColumnBreakInside: 'avoid',
            pageBreakInside: 'avoid',
            marginBottom: 6,
          }}>
            <div style={{
              color: c.accent, textShadow: `0 0 6px ${c.accent}`,
              fontWeight: 600,
            }}>
              ├── {cat.label}/
            </div>
            {cat.items.map((item, ii) => {
              const last = ii === cat.items.length - 1;
              return (
                <div key={item} style={{ whiteSpace: 'pre', position: 'relative' }}>
                  <span style={{ color: c.fgDim }}>{`│   ${last ? '└──' : '├──'} `}</span>
                  {renderLeaf({ item, ii, ci, cat, last })}
                </div>
              );
            })}
          </div>
        ))}
        {extraOverlay}
      </div>
    </div>
  );
}

// Variant I · Tree + hex addresses — memory-dump look
function SkillsTreeHex({ theme }) {
  const c = theme;
  // Build a stable address index across all leaves at render time.
  let addr = 0x1F2A;
  const addrFor = {};
  SKILLS.forEach((cat) => {
    cat.items.forEach((item) => {
      addr += 0x10;
      addrFor[`${cat.label}::${item}`] = '0x' + addr.toString(16).toUpperCase().padStart(4, '0');
    });
  });
  return (
    <SkillsTreeBase
      theme={c}
      prompt="hexdump skills/"
      sectionLabel="memory map"
      renderLeaf={({ item, cat }) => (
        <>
          <span style={{ color: c.fgDim, opacity: 0.7 }}>{addrFor[`${cat.label}::${item}`]}</span>
          <span style={{ color: c.fgDim }}>{'  '}</span>
          <span style={{ color: c.fg }}>{item}</span>
        </>
      )}
    />
  );
}

// Variant J · Tree + systemd status — service-status vibe
function SkillsTreeStatus({ theme }) {
  const c = theme;
  return (
    <SkillsTreeBase
      theme={c}
      prompt="systemctl status skills/*"
      sectionLabel="active services"
      renderLeaf={({ item }) => (
        <>
          <span style={{ color: c.fg }}>{item}</span>
          <span style={{
            color: c.fgGlow, textShadow: `0 0 6px ${c.fg}`, marginLeft: 8,
          }}>[ OK ]</span>
        </>
      )}
    />
  );
}

// Variant K · Tree + live glitch — at most one char on one item flickers
// to katakana ~every 1.3s, holds for 250ms, then clears. Subtle "alive"
// feel without crowding the screen.
function SkillsTreeGlitch({ theme, cols = 2 }) {
  const c = theme;
  const charset = (RAIN_CHARS[c.chars] || RAIN_CHARS.classic).split('');
  const [glitchMap, setGlitchMap] = useEx({});
  useExFX(() => {
    // Flatten all leaf strings once; pick one at random per tick.
    const allItems = [];
    SKILLS.forEach((cat) => cat.items.forEach((item) => allItems.push(item)));
    let clearTimer;
    const iv = setInterval(() => {
      const item = allItems[Math.floor(Math.random() * allItems.length)];
      const charIdx = Math.floor(Math.random() * item.length);
      const ch = charset[Math.floor(Math.random() * charset.length)];
      setGlitchMap({ [item]: { charIdx, ch } });
      clearTimeout(clearTimer);
      clearTimer = setTimeout(() => setGlitchMap({}), 250);
    }, 1300);
    return () => { clearInterval(iv); clearTimeout(clearTimer); };
  }, []);
  return (
    <SkillsTreeBase
      theme={c}
      prompt="tree --watch skills/"
      renderLeaf={({ item }) => {
        const g = glitchMap[item];
        if (!g) return <span style={{ color: c.fg }}>{item}</span>;
        return (
          <span style={{ color: c.fg }}>
            {item.slice(0, g.charIdx)}
            <span style={{
              color: c.fgGlow,
              textShadow: `0 0 10px ${c.fgGlow}, 0 0 18px ${c.fg}`,
            }}>{g.ch}</span>
            {item.slice(g.charIdx + 1)}
          </span>
        );
      }}
      cols={cols}
    />
  );
}

// Variant L · Tree + scanning cursor — single highlighted row traverses
// through all leaves automatically.
function SkillsTreeScan({ theme }) {
  const c = theme;
  // Build a flat list of (catIdx, itemIdx) for the cursor to step through.
  const allKeys = [];
  SKILLS.forEach((cat, ci) => {
    cat.items.forEach((_, ii) => allKeys.push(`${ci}-${ii}`));
  });
  const [cursorKey, setCursorKey] = useEx(allKeys[0]);
  useExFX(() => {
    let idx = 0;
    const iv = setInterval(() => {
      idx = (idx + 1) % allKeys.length;
      setCursorKey(allKeys[idx]);
    }, 320);
    return () => clearInterval(iv);
  }, []);
  return (
    <SkillsTreeBase
      theme={c}
      prompt="scan --recurse skills/"
      renderLeaf={({ item, ii, ci }) => {
        const isCursor = `${ci}-${ii}` === cursorKey;
        return (
          <>
            <span style={{
              color: isCursor ? c.fgGlow : c.fg,
              textShadow: isCursor ? `0 0 8px ${c.fg}` : 'none',
              background: isCursor ? 'rgba(0,255,65,0.18)' : 'transparent',
              padding: isCursor ? '0 4px' : 0,
              transition: 'background 0.15s, color 0.15s, padding 0.05s',
            }}>{item}</span>
            {isCursor && (
              <span style={{
                color: c.accent, marginLeft: 8,
                textShadow: `0 0 6px ${c.accent}`,
              }}>◀</span>
            )}
          </>
        );
      }}
    />
  );
}

// Variant I · Stars — familiar ★★★★☆ pattern. Reads instantly, no
// learning curve. Pairs well with the terminal aesthetic via mono glyphs.
function SkillsStars({ theme }) {
  const c = theme;
  return (
    <div>
      <PromptLine theme={c} text="ls skills/" />
      <SectionHeader theme={c} text="tech stack" />
      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)',
        gap: '12px 18px',
      }}>
        {SKILLS.map((cat) => (
          <div key={cat.label} style={{
            border: `1px solid ${c.fgDim}`,
            padding: '8px 12px',
            background: 'rgba(0,0,0,0.35)',
          }}>
            <div style={{
              color: c.accent, fontSize: 11, letterSpacing: 1.5,
              textTransform: 'uppercase', textShadow: `0 0 6px ${c.accent}`,
              marginBottom: 6,
            }}>▸ {cat.label}</div>
            {cat.items.map((item, i) => {
              const lvl = cat.lvl?.[i] ?? 70;
              const total = 5;
              const filled = Math.round((lvl / 100) * total);
              return (
                <div key={item} style={{
                  display: 'flex', justifyContent: 'space-between',
                  gap: 8, fontSize: 12, padding: '1px 0',
                }}>
                  <div style={{
                    color: c.fg, flex: 1,
                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                  }}>{item}</div>
                  <div style={{ letterSpacing: 1, fontSize: 11, whiteSpace: 'nowrap' }}>
                    <span style={{ color: c.fgGlow, textShadow: `0 0 4px ${c.fg}` }}>{'★'.repeat(filled)}</span>
                    <span style={{ color: c.fgDim, opacity: 0.5 }}>{'☆'.repeat(total - filled)}</span>
                  </div>
                </div>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
}

// Original single-row helper kept for reference but unused.
function SkillRow_unused({ label, pct, theme }) {
  const c = theme;
  const blocks = 18;
  const filled = Math.round((pct / 100) * blocks);
  return (
    <div style={{ display: 'flex', gap: 10, alignItems: 'baseline', fontSize: 13, padding: '2px 0' }}>
      <div style={{ width: 160, color: c.fg }}>{label}</div>
      <div style={{ flex: 1, letterSpacing: 1 }}>
        <span style={{ color: c.fgGlow, textShadow: `0 0 6px ${c.fg}` }}>{'█'.repeat(filled)}</span>
        <span style={{ color: c.fgDim }}>{'░'.repeat(blocks - filled)}</span>
      </div>
      <div style={{ width: 32, color: c.fgDim, textAlign: 'right' }}>{pct}%</div>
    </div>
  );
}

function ProjectsView({ theme }) {
  const c = theme;
  const [open, setOpen] = useEx(null);
  const [selected, setSelected] = useEx(0);

  // Same up/down/Enter navigation as the root file listing. Up/Down change
  // the highlighted row; Enter expands or collapses it; Esc-style exit is
  // handled higher up in the explorer. We intentionally don't trap focus —
  // the input listener is window-scoped and only consumes keys when this
  // view is mounted.
  useExFX(() => {
    const onKey = (e) => {
      if (e.key === 'ArrowDown') {
        e.preventDefault();
        setSelected((i) => Math.min(PROJECTS.length - 1, i + 1));
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        setSelected((i) => Math.max(0, i - 1));
      } else if (e.key === 'Enter') {
        e.preventDefault();
        const id = PROJECTS[selected]?.id;
        if (id != null) setOpen((o) => (o === id ? null : id));
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [selected]);

  return (
    <div>
      <PromptLine theme={c} text="ls projects/" />
      <SectionHeader theme={c} text="shipped" />
      <div style={{ color: c.fgDim, marginBottom: 12, fontSize: 12 }}>
        total {PROJECTS.length} · use ↑↓ Enter to navigate, or click a row.
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8, maxWidth: 980 }}>
        {PROJECTS.map((p, i) => {
          const isOpen = open === p.id;
          const isFocus = i === selected;
          return (
            <div key={p.id} style={{
              border: `1px solid ${isOpen ? c.accent : (isFocus ? c.fg : c.fgDim)}`,
              background: isOpen
                ? 'rgba(0,255,65,0.06)'
                : (isFocus ? 'rgba(0,255,65,0.04)' : 'rgba(0,0,0,0.35)'),
              boxShadow: isOpen
                ? `0 0 18px ${c.accentSoft}`
                : (isFocus ? `inset 0 0 0 1px ${c.accent}, 0 0 12px ${c.accentSoft}` : 'none'),
              transition: 'all 0.16s',
            }}>
              <button
                onClick={() => { setSelected(i); setOpen(isOpen ? null : p.id); }}
                onMouseEnter={() => setSelected(i)}
                style={{
                  all: 'unset',
                  cursor: 'pointer',
                  display: 'flex',
                  alignItems: 'center',
                  gap: 14,
                  width: '100%',
                  padding: '10px 14px',
                  boxSizing: 'border-box',
                }}
              >
                <span style={{ color: c.accent, textShadow: `0 0 8px ${c.accent}` }}>{isOpen ? '▾' : '▸'}</span>
                <span style={{ color: c.fgDim, width: 30 }}>[{p.id}]</span>
                <span style={{ color: c.fgGlow, flex: 1, textShadow: `0 0 6px ${c.fg}` }}>{p.name}</span>
                <span style={{ color: c.fgDim, fontSize: 12 }}>{p.stack.length} deps</span>
              </button>
              {isOpen && (
                <div style={{ padding: '0 14px 14px 48px', color: c.fg, fontSize: 13, lineHeight: 1.7 }}>
                  <div style={{ color: c.fgDim, marginBottom: 6 }}>
                    <span style={{ color: c.fg }}>{p.role}</span>
                    {p.period && (
                      <span style={{ color: c.fgDim, marginLeft: 10 }}>· {p.period}</span>
                    )}
                  </div>
                  <div style={{ color: c.fgDim, marginBottom: 10 }}>
                    stack: <span style={{ color: c.fg }}>{p.stack.join(' · ')}</span>
                  </div>
                  {p.bullets.map((b, bi) => (
                    <div key={bi} style={{ display: 'flex', gap: 8, marginBottom: 2 }}>
                      <span style={{ color: c.accent, flexShrink: 0 }}>·</span>
                      <span>{b}</span>
                    </div>
                  ))}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

function ContactsView({ theme }) {
  const c = theme;
  const rows = [
    { k: 'email',    v: PROFILE.email,         href: `mailto:${PROFILE.email}` },
    { k: 'github',   v: PROFILE.githubLabel,   href: PROFILE.github   },
    { k: 'linkedin', v: PROFILE.linkedinLabel, href: PROFILE.linkedin },
    { k: 'telegram', v: PROFILE.telegramLabel, href: PROFILE.telegram },
    { k: 'phone',    v: PROFILE.phone,         href: `tel:${PROFILE.phone.replace(/\s|\(|\)/g, '')}` },
    { k: 'location', v: PROFILE.location,      href: null },
  ];
  return (
    <div>
      <PromptLine theme={c} text="cat contacts/*" />
      <SectionHeader theme={c} text="open channels" />
      <div style={{
        border: `1px solid ${c.fgDim}`,
        background: 'rgba(0,0,0,0.35)',
        padding: '14px 18px',
        maxWidth: 720,
      }}>
        {rows.map((r) => (
          <div key={r.k} style={{ display: 'grid', gridTemplateColumns: '120px 1fr', padding: '5px 0', alignItems: 'baseline' }}>
            <div style={{ color: c.fgDim, textTransform: 'uppercase', letterSpacing: 2, fontSize: 12 }}>{r.k}</div>
            {r.href ? (
              <a href={r.href} target="_blank" rel="noopener" style={{
                color: c.accent,
                textShadow: `0 0 8px ${c.accent}`,
                textDecoration: 'underline',
                textUnderlineOffset: 3,
                fontSize: 14,
              }}>{r.v}</a>
            ) : (
              <div style={{ color: c.fg }}>{r.v}</div>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}

function ReadmeView({ theme }) {
  const c = theme;
  return (
    <div style={{ maxWidth: 720 }}>
      <PromptLine theme={c} text="less README.md" />
      <SectionHeader theme={c} text="readme" />
      <div style={{ color: c.fg, lineHeight: 1.75 }}>
        <p style={{ marginTop: 0 }}>
          Click a folder above to explore. Everything is pulled live from
          the same source as my CV.
        </p>
        <p style={{ color: c.fgDim }}>
          press <span style={{ color: c.fgGlow }}>Esc</span> any time to go back to the choice.
        </p>
      </div>
    </div>
  );
}

// ── Socials strip ──────────────────────────────────────────────────────

function ExSocials({ theme }) {
  const c = theme;
  const items = [
    { label: 'github',   href: PROFILE.github,   sub: PROFILE.githubLabel   },
    { label: 'linkedin', href: PROFILE.linkedin, sub: PROFILE.linkedinLabel },
    { label: 'telegram', href: PROFILE.telegram, sub: PROFILE.telegramLabel },
    { label: 'email',    href: `mailto:${PROFILE.email}`, sub: PROFILE.email },
  ];
  return (
    <div style={{
      padding: '10px 18px',
      borderTop: `1px solid ${c.accentSoft}`,
      display: 'flex',
      gap: 10,
      flexWrap: 'wrap',
      background: 'rgba(0,0,0,0.55)',
      fontSize: 12,
    }}>
      {items.map((it) => (
        <a key={it.label} href={it.href} target="_blank" rel="noopener"
           style={{
             display: 'flex',
             flexDirection: 'column',
             border: `1px solid ${c.fgDim}`,
             background: 'rgba(0,0,0,0.4)',
             color: c.fg,
             fontFamily: c.fontMono,
             padding: '5px 12px',
             textDecoration: 'none',
             letterSpacing: 1.2,
             transition: 'all 0.18s',
           }}
           onMouseEnter={(e) => {
             e.currentTarget.style.background = c.accentSoft;
             e.currentTarget.style.borderColor = c.accent;
             e.currentTarget.style.boxShadow = `0 0 14px ${c.accentSoft}`;
           }}
           onMouseLeave={(e) => {
             e.currentTarget.style.background = 'rgba(0,0,0,0.4)';
             e.currentTarget.style.borderColor = c.fgDim;
             e.currentTarget.style.boxShadow = 'none';
           }}
        >
          <span style={{ color: c.fgGlow, textTransform: 'uppercase', fontSize: 11, letterSpacing: 2, textShadow: `0 0 6px ${c.fg}` }}>
            ▸ {it.label}
          </span>
          <span style={{ color: c.fgDim, fontSize: 11 }}>{it.sub}</span>
        </a>
      ))}
    </div>
  );
}

// little ASCII-style button helper
function asciiBtnStyle(theme, primary = false) {
  const c = theme;
  return {
    border: `1px solid ${primary ? c.accent : c.fgDim}`,
    background: primary ? c.accentSoft : 'rgba(0,0,0,0.4)',
    color: c.fg,
    fontFamily: c.fontMono,
    fontSize: 12,
    padding: '4px 12px',
    cursor: 'pointer',
    letterSpacing: 1.2,
    textTransform: 'uppercase',
    boxShadow: primary ? `0 0 10px ${c.accentSoft}` : 'none',
  };
}

Object.assign(window, { FileExplorer });
