/* global React, Sticker, Star, Smiley, Flower, Butterfly, Heart, Squiggle, Arrow, Burst, PaperClip, PortraitGlyph, ProjThumb */
const { useState: useState_S, useEffect: useEffect_S } = React;

/* =========================================================
   HERO + ID CARD
========================================================= */
window.Hero = function Hero({ pal }) {
  return (
    <section id="top" className="hero">
      <div className="hero-left">
        <div className="hero-tag hero-tag-quirky">
          <span className="ht-blink">▮</span>
          <span>caffeinated · semi-sentient · v4.0.4</span>
          <span className="ht-chip">[ ✱ pets cats for free ]</span>
        </div>
        <h1>
          RAHUL
          <br />
          <span className="hero-t">T</span><span className="reg-mark">®</span>
          <span className="emoji-burst">
            <span className="eb e1">😎</span>
            <span className="eb e2">🫪</span>
            <span className="eb e3">👾</span>
            <span className="eb e4">🔥</span>
            <span className="eb e5">⚡</span>
          </span>
          <span className="swirl">404 chill not found</span>
        </h1>
        <div className="hero-handnote">
          A <span className="hilite">full-stack &amp; AI developer</span> who writes code on purpose,
          ships the <span className="underline">boring 80%</span> first, and absolutely will argue about <code style={{fontFamily:'JetBrains Mono', fontSize:'.85em', background:'var(--ink)', color:'var(--paper)', padding:'0 4px'}}>tabs vs spaces</code> if provoked. 🌶️
        </div>

        <div className="hero-poster-row">
          <div className="poster poster-now">
            <div className="poster-eyebrow">● NOW PLAYING</div>
            <div className="poster-big">deploy<br />friday<br /><span className="strike">never</span> always</div>
            <div className="poster-foot">side A · 33 ⅓ RPM</div>
          </div>

          <div className="stamp-circle coffee-mug" aria-hidden="true">
            <svg viewBox="0 0 200 200" width="100%" height="100%">
              {/* steam */}
              <path d="M 78 36 q -8 -10 0 -22 q 8 -12 0 -22" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" />
              <path d="M 100 32 q -8 -10 0 -22 q 8 -12 0 -22" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" />
              <path d="M 122 36 q -8 -10 0 -22 q 8 -12 0 -22" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" />
              {/* mug body */}
              <rect x="38" y="62" width="108" height="100" rx="6" fill="#fff8e7" stroke="currentColor" strokeWidth="4" />
              {/* coffee surface */}
              <ellipse cx="92" cy="68" rx="50" ry="9" fill="#5a3a1f" stroke="currentColor" strokeWidth="3" />
              <ellipse cx="92" cy="66" rx="40" ry="5" fill="#8b5a2b" />
              {/* handle */}
              <path d="M 146 84 q 28 0 28 28 q 0 28 -28 28" fill="none" stroke="currentColor" strokeWidth="4" />
              <path d="M 146 96 q 16 0 16 16 q 0 16 -16 16" fill="none" stroke="currentColor" strokeWidth="3" />
              {/* face on mug */}
              <circle cx="74" cy="108" r="3.5" fill="currentColor" />
              <circle cx="110" cy="108" r="3.5" fill="currentColor" />
              <path d="M 74 124 q 18 12 36 0" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" />
              {/* base saucer */}
              <ellipse cx="92" cy="170" rx="74" ry="8" fill="none" stroke="currentColor" strokeWidth="4" />
              <line x1="22" y1="170" x2="22" y2="178" stroke="currentColor" strokeWidth="4" strokeLinecap="round" />
              <line x1="162" y1="170" x2="162" y2="178" stroke="currentColor" strokeWidth="4" strokeLinecap="round" />
              <path d="M 22 178 q 70 12 140 0" fill="none" stroke="currentColor" strokeWidth="4" />
              {/* tag tied to handle */}
              <line x1="170" y1="112" x2="186" y2="124" stroke="currentColor" strokeWidth="2" />
              <rect x="178" y="120" width="22" height="14" fill="#ffd34a" stroke="currentColor" strokeWidth="2" transform="rotate(8 189 127)" />
              <text x="189" y="131" textAnchor="middle" fontFamily="JetBrains Mono" fontSize="7" fontWeight="700" fill="currentColor" transform="rotate(8 189 127)">FUEL</text>
            </svg>
          </div>

          <div className="poster poster-tape">
            <div className="poster-eyebrow">// status.txt</div>
            <ul className="poster-list">
              <li><b>building</b> chaos with intent</li>
              <li><b>reading</b> DDIA, ch. 9 (consensus)</li>
              <li><b>fixing</b> a bug from oct '24</li>
              <li><b>avoiding</b> the dishes</li>
            </ul>
            <div className="poster-foot">last updated: just now</div>
          </div>
        </div>
      </div>

      <IdCard />
      <IpodMini />
      <img className="floating-sticker fs-monster" src="assets/sticker-monster.png" alt="" aria-hidden="true" />
      <img className="floating-sticker fs-byte" src="assets/sticker-byte.png" alt="" aria-hidden="true" />
      <img className="floating-sticker fs-nosleep" src="assets/sticker-nosleep.png" alt="" aria-hidden="true" />
    </section>
  );
};

window.IpodMini = function IpodMini() {
  const audioRef = React.useRef(null);
  const [playing, setPlaying] = React.useState(false);
  const [cur, setCur] = React.useState(0);
  const [dur, setDur] = React.useState(0);
  const track = 'Stateside';
  const artist = 'PinkPantheress · Zara Larsson';

  React.useEffect(() => {
    const a = audioRef.current;
    if (!a) return;
    const onTime = () => setCur(a.currentTime || 0);
    const onMeta = () => setDur(a.duration || 0);
    const onPlay = () => setPlaying(true);
    const onPause = () => setPlaying(false);
    a.addEventListener('timeupdate', onTime);
    a.addEventListener('loadedmetadata', onMeta);
    a.addEventListener('play', onPlay);
    a.addEventListener('pause', onPause);
    a.addEventListener('ended', onPause);
    return () => {
      a.removeEventListener('timeupdate', onTime);
      a.removeEventListener('loadedmetadata', onMeta);
      a.removeEventListener('play', onPlay);
      a.removeEventListener('pause', onPause);
      a.removeEventListener('ended', onPause);
    };
  }, []);

  const toggle = () => {
    const a = audioRef.current;
    if (!a) return;
    if (a.paused) a.play().catch(() => {}); else a.pause();
  };
  const seekBy = (delta) => {
    const a = audioRef.current;
    if (!a) return;
    a.currentTime = Math.max(0, Math.min((a.duration || 0), (a.currentTime || 0) + delta));
  };

  const fmt = (s) => {
    if (!isFinite(s)) s = 0;
    const m = Math.floor(s / 60);
    const sec = Math.floor(s % 60).toString().padStart(2, '0');
    return `${m}:${sec}`;
  };
  const pct = dur > 0 ? Math.min(100, (cur / dur) * 100) : 0;

  return (
    <div className="ipod-mini" aria-label="now playing">
      <audio ref={audioRef} src="assets/song.mp3" preload="metadata" />
      <div className="ipod-screen">
        <div className="ipod-eyebrow">{playing ? '▶ NOW PLAYING' : '▮▮ PAUSED'}</div>
        <div className="ipod-track">{track}</div>
        <div className="ipod-artist">{artist}</div>
        <div className="ipod-bar"><span style={{ width: pct + '%' }} /></div>
        <div className="ipod-time"><span>{fmt(cur)}</span><span>−{fmt(Math.max(0, dur - cur))}</span></div>
      </div>
      <div className="ipod-wheel">
        <span className="ipod-wheel-menu">MENU</span>
        <span className="ipod-wheel-prev" onClick={() => seekBy(-10)}>⏮</span>
        <span className="ipod-wheel-next" onClick={() => seekBy(10)}>⏭</span>
        <span className="ipod-wheel-play" onClick={toggle}>▶︎▮▮</span>
        <button className="ipod-wheel-center" aria-label="play/pause" onClick={toggle} />
      </div>
    </div>
  );
};

window.IdCard = function IdCard() {
  return (
    <div className="id-card">
      <div className="id-strip">
        <span>VISITOR PASS</span>
        <span>№ 0421</span>
      </div>

      <div className="id-photo">
        <img src="assets/profile.jpeg" alt="Rahul" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
        <div className="photo-label">DEV ON DUTY · CHENNAI</div>
      </div>

      <div className="id-name">
        Rahul <span className="accent">T.</span>
      </div>
      <div className="id-blurb">
        Full-stack &amp; AI dev. Runs on coffee, curiosity, and the occasional <i>Stack Overflow</i> miracle.
      </div>

      <dl className="id-grid">
        <dt>Class</dt><dd>Dev / Lvl 22</dd>
        <dt>Stack</dt><dd>Full + AI</dd>
        <dt>Spawn</dt><dd>Chennai · IN</dd>
        <dt>Mode</dt><dd>online ⚫</dd>
      </dl>

      <div className="id-foot">
        <div className="qr" aria-hidden="true" />
        <div className="id-contact">
          <div>rahulnilvan43</div>
          <div>@gmail.com</div>
          <a href="https://github.com/therahul-yo" target="_blank" rel="noreferrer">@therahul-yo</a>
        </div>
      </div>

      <div className="barcode" aria-hidden="true">
        {[2,4,1,3,2,5,1,2,4,2,3,1,5,2,3,1,2,4,1,2,3,2].map((w,i) =>
          <b key={i} style={{ width: w + 'px' }} />
        )}
      </div>
    </div>
  );
};

/* =========================================================
   WORK EXPERIENCE
========================================================= */
const WORK = [
  {
    company: 'Next Level · Singapore',
    role: 'Team Lead',
    period: '2024 → now',
    learned: [
      'Bossed around 4 humans + 1 very patient Redis instance',
      'Shipped Redis-backed job queues that no longer cry on Tuesdays',
      'Replaced the team\'s tribal-lore deploy doc with an actual checklist',
      'Survived $ git push origin main --force (will not elaborate)',
    ],
    tools: ['Node', 'TS', 'Redis', 'Docker', 'vim'],
    color: 'lime',
  },
  {
    company: 'Freelance & open-source',
    role: 'Solo Dev',
    period: '2022 → now',
    learned: [
      'Tiny CLIs, weirder browser extensions, occasional menu-bar apps',
      '~80 GitHub stars, mostly from people who star-then-vanish, hi',
      'Stack Overflow karma: enough to feed a small family',
    ],
    tools: ['Whatever the bug needs'],
    color: 'sun',
  },
];

window.WorkSection = function WorkSection({ pal }) {
  return (
    <section id="work" className="work-section">
      <Sticker top={-10} right={60} rotate={10}><Smiley size={68} fill={pal.sun} /></Sticker>
      <Sticker top={300} left={-40} rotate={-18}><Heart size={64} fill={pal.tomato} /></Sticker>
      <Sticker bottom={-40} right={-20} rotate={-8}><Butterfly size={88} fill={pal.blush} /></Sticker>

      <div className="paper work-paper">
        <div className="work-title">
          Work<br />History <em>(abridged)</em>
        </div>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 11, letterSpacing: '0.12em', color: '#666', marginBottom: 18 }}>
          $ git log --author=rahul --oneline | head -2
        </div>

        {WORK.map((job, i) => (
          <div key={i} className="job">
            <div className="job-head">
              <h3>
                {job.company}
                <span className="role" style={{ background: `var(--${job.color}, var(--lime))` }}>
                  {job.role}
                </span>
              </h3>
              <div className="job-date">{job.period}</div>
            </div>
            <div className="job-q">What did I learn here?</div>
            <ul>
              {job.learned.map((l, j) => <li key={j}>{l}</li>)}
            </ul>
            <div className="job-tools">
              {job.tools.map(t => <span key={t} className="tool-chip">{t}</span>)}
            </div>
          </div>
        ))}
      </div>
    </section>
  );
};

/* =========================================================
   SKILLS
========================================================= */
const SKILL_GROUPS = [
  {
    label: 'Frontend',
    items: [
      { glyph: 'Re', label: 'React', lvl: 90 },
      { glyph: 'Vj', label: 'Vanilla JS', lvl: 88 },
      { glyph: 'Hc', label: 'HTML/CSS', lvl: 90 },
      { glyph: 'Fg', label: 'Figma', lvl: 75 },
    ],
  },
  {
    label: 'Backend',
    items: [
      { glyph: 'Nd', label: 'Node', lvl: 90 },
      { glyph: 'Ex', label: 'Express', lvl: 88 },
      { glyph: 'Py', label: 'Python', lvl: 86 },
      { glyph: 'Bu', label: 'Bull/Redis', lvl: 78 },
    ],
  },
  {
    label: 'AI / ML / Native',
    items: [
      { glyph: 'Sk', label: 'scikit-learn', lvl: 80 },
      { glyph: 'Ai', label: 'AI Tools', lvl: 92, accent: true },
      { glyph: 'Sw', label: 'Swift', lvl: 70 },
      { glyph: 'Dk', label: 'Docker', lvl: 82 },
    ],
  },
];

window.SkillsSection = function SkillsSection({ pal }) {
  return (
    <section id="skills" className="skills-section">

      <div className="skills-card">
        <div className="price-tag">EST. 2021</div>
        <div className="skills-eyebrow">─ Stack &amp; tools</div>
        <div className="skills-title">
          SKILLS<span className="tm">™</span>
          <div className="skills-burst" aria-hidden="true">
            <Burst size={96} fill="#ff5a3c" spikes={12} />
          </div>
        </div>

        {SKILL_GROUPS.map(g => (
          <div key={g.label} className="skills-group">
            <h4>// {g.label}</h4>
            <div className="skill-grid">
              {g.items.map(s => (
                <div key={s.label} className={`skill-tile lvl${s.accent ? ' skill-tile-accent' : ''}`} style={{ '--lvl': s.lvl + '%' }}>
                  <div className="glyph">{s.glyph}</div>
                  <div className="label">{s.label}</div>
                </div>
              ))}
            </div>
          </div>
        ))}
      </div>

      <AboutCard />
    </section>
  );
};

window.AboutCard = function AboutCard() {
  return (
    <div className="about-card">
      <h3>$ whoami</h3>
      <p>
        Hi, I'm Rahul. I write code, occasionally on purpose. I read changelogs for fun, name my
        branches in haiku, and have <i>strong opinions</i> about trailing commas (pro).
      </p>
      <p>
        I like full-stack things, sharply-scoped AI tools, and making the boring screens really good
        instead of the cool screens fine. I've broken prod exactly enough times to be useful.
      </p>
      <p style={{ fontFamily: 'JetBrains Mono', fontSize: 11, lineHeight: 1.7, marginTop: 10, color: '#444' }}>
        $ uname -a<br />
        Rahul 4.0.4 #2026 SMP <i>caffeinated</i> Chennai/UTC+5:30 GNU/Curious
      </p>
      <div className="signoff">— rt &lt;3</div>
      <div className="stamp">EXIT 0 · 2026</div>

      <div className="whoami-note">
        <div className="whoami-note-pin" aria-hidden="true">
          <svg viewBox="0 0 40 60" width="22" height="34">
            <path d="M 20 4 C 12 4 8 12 8 22 L 8 50 C 8 54 12 56 16 56 L 16 30 C 16 24 20 22 24 22 C 28 22 32 24 32 30 L 32 38 C 32 42 28 42 28 38 L 28 32" fill="none" stroke="#888" strokeWidth="3" strokeLinecap="round" />
          </svg>
        </div>
        <div className="whoami-note-eyebrow">// post-it · do not remove</div>
        <p>P.S. — if you've read this far, you've already passed the vibe check. Bring snacks. ✦</p>
      </div>

      <div className="ruled-note">
        <div className="ruled-note-tape" aria-hidden="true" />
        <div className="ruled-note-eyebrow">// torn from notebook · ruled, slightly cursed</div>
        <p>
          dear future employer,<br />
          if you're reading this, please note: i debug with <i>console.log</i> and i'm <b>not</b> sorry.
          i once fixed a bug by renaming the file. it worked. don't ask.
        </p>
        <div className="ruled-note-foot">— rt, probably caffeinated ☕</div>
      </div>
    </div>
  );
};

/* =========================================================
   EDUCATION + RIGHT COLLAGE
========================================================= */
window.EduSection = function EduSection({ pal }) {
  return (
    <section id="education" className="edu-section">

      <div className="edu-ticket">
        <div className="edu-title">education</div>
        <div className="edu-sub">─ Admit one · do not bend</div>

        <div className="edu-entry">
          <div className="yr">2021–2025</div>
          <div>
            <h4>B.E. Computer Science</h4>
            <p>
              Jeppiaar Engineering College · Chennai<br />
              CGPA 8.1 — slept through DBMS, stayed awake for compilers. Net positive.
            </p>
          </div>
        </div>

        <div className="edu-entry">
          <div className="yr">∞</div>
          <div>
            <h4>Self-taught, ongoing</h4>
            <p>
              PyTorch on weekends · distributed-systems papers in the bathtub ·
              Swift/SwiftUI when iOS dev twitter goads me into it.
            </p>
          </div>
        </div>

        <div className="edu-foot">
          <span>SEC. A&nbsp;&nbsp;ROW 12</span>
          <span>·····</span>
          <span>VALID 2026</span>
        </div>

        <div className="edu-barcode" aria-hidden="true">
          <div className="edu-barcode-bars">
            {Array.from({ length: 38 }).map((_, i) => (
              <span key={i} style={{ width: [1,2,3,1,2,1,3,2][i % 8] + 'px' }} />
            ))}
          </div>
          <div className="edu-barcode-num">★ 0 4042021 \u00b7 2025 ★ JEC \u00b7 CHN</div>
        </div>
      </div>

      {/* right side: stat-receipts + currently */}
      <div style={{ position: 'relative', minHeight: 520 }}>
        <Sticker top={-8} right={120} rotate={-6}><PaperClip size={48} /></Sticker>

        <div className="paper" style={{ padding: '20px 22px', transform: 'rotate(-3deg)', marginBottom: 28 }}>
          <div style={{ fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#777', marginBottom: 8 }}>// now playing</div>
          <div style={{ fontFamily: 'Archivo Black', fontSize: 30, lineHeight: 0.95, marginBottom: 8, textTransform: 'uppercase' }}>
            Building <em style={{ fontStyle: 'normal', color: 'var(--tomato)' }}>AllKitty</em>
          </div>
          <div style={{ fontFamily: 'Kalam', fontSize: 17, lineHeight: 1.4 }}>
            A media-extraction queue thing. Redis + Node + TS + Docker — a thousand tiny services pretending to be one big one.
          </div>
          <div style={{ marginTop: 12, fontFamily: 'JetBrains Mono', fontSize: 10, color: '#444' }}>
            uptime: 99.94% &nbsp;·&nbsp; last deploy: 6h ago &nbsp;·&nbsp; vibes: cautiously optimistic
          </div>
          <div style={{ marginTop: 10, height: 6, background: 'repeating-linear-gradient(45deg, var(--ink) 0 4px, transparent 4px 8px)' }} />
        </div>

        <div style={{ display: 'flex', gap: 14, alignItems: 'flex-start', flexWrap: 'wrap' }}>
          <div className="paper" style={{ padding: '18px 22px', transform: 'rotate(2.5deg)', background: 'var(--cyan)', flex: '1.4 1 260px', minWidth: 240 }}>
            <div style={{ fontFamily: 'JetBrains Mono', fontSize: 11, letterSpacing: '0.2em', textTransform: 'uppercase', marginBottom: 10 }}>// stats.json</div>
            <ul style={{ listStyle: 'none', fontFamily: 'JetBrains Mono', fontSize: 13, lineHeight: 1.85, margin: 0, padding: 0 }}>
              <li>☕ coffees → code: <b>1:1</b></li>
              <li>⌨ keystrokes/day: <b>~28k</b></li>
              <li>🐛 bugs filed: <b>404</b></li>
              <li>📑 tabs open: <b>71</b></li>
            </ul>
          </div>

          <div className="typing-gif" style={{ transform: 'rotate(-3deg)', flex: '1 1 200px', minWidth: 180 }}>
            <img src="assets/typing.gif" alt="typing" />
            <div className="typing-gif-cap">// dev.exe — currently running</div>
          </div>
        </div>

        <div className="card-sticker-row" style={{ marginTop: 18 }}>
          <span className="cs-item" style={{ transform: 'rotate(-12deg)' }}><Star size={64} fill="#d8ff3a" /></span>
          <span className="cs-item" style={{ transform: 'rotate(8deg)' }}><Heart size={56} fill="#ff5a3c" /></span>
          <span className="cs-item" style={{ transform: 'rotate(-6deg)' }}><Smiley size={58} fill="#ffd34a" /></span>
          <span className="cs-item" style={{ transform: 'rotate(14deg)' }}><Burst size={70} fill="#ff8fb0" spikes={10} /></span>
        </div>

        <div style={{ position: 'absolute', bottom: -10, left: -20, transform: 'rotate(-12deg)' }}>
          <Squiggle width={140} color="#0c0c0c" />
        </div>
      </div>
    </section>
  );
};

/* =========================================================
   PROJECTS
========================================================= */
const PROJECTS = [
  { num: '01', name: 'AllKitty', tag: 'Backend', kind: 'queue', caption: 'Redis-backed media queues. Handles nonsense at scale so you don\'t have to.', stack: ['Node', 'TS', 'Bull/Redis', 'SQLite', 'Docker', 'Jest'], palette: ['#d8ff3a', '#ff5a3c', '#5ec6ff'], url: 'https://all-kitty.onrender.com/' },
  { num: '02', name: 'SmithWorks', tag: 'Freelance', kind: 'browser', caption: 'A freelance project board — clean, fast, no bloat. Ship quote, scope, deliver.', stack: ['React', 'Node', 'Mongo', 'Socket.IO', 'JWT', 'AWS EC2'], palette: ['#ff5a3c', '#ffd34a', '#0c0c0c'], url: 'https://freelance-application-coral.vercel.app/' },
  { num: '03', name: 'TopX', tag: 'AI', kind: 'resume', caption: 'AI resume tool. Scores you, rewrites you, then quietly judges your bullet points.', stack: ['Python', 'Flask', 'scikit-learn', 'Socket.IO', 'JS'], palette: ['#ff8fb0', '#5ec6ff', '#d8ff3a'], url: 'https://topx-ai-resume-analyzer.onrender.com/' },
  { num: '04', name: 'PIXS', tag: 'Native', kind: 'menubar', caption: 'macOS menu-bar notes. ⌘⌥N → type → gone. Built for shower-thoughts and 2am ideas.', stack: ['Swift', 'SwiftUI', 'AppKit', 'Gemini API'], palette: ['#5ec6ff', '#d8ff3a', '#ff5a3c'], url: 'https://pixs-xi.vercel.app/' },
  { num: '05', name: 'Arcks', tag: 'Browser', kind: 'browser', caption: 'Hover any link → AI preview before you click. Saves ≈3 doomscroll-tabs/day.', stack: ['Chrome', 'TS', 'Vite'], palette: ['#ffd34a', '#ff5a3c', '#0c0c0c'], url: 'https://therahul-yo.github.io/Arcks/' },
  { num: '06', name: 'Spam Classifier', tag: 'ML', kind: 'ml', caption: 'NLP pipeline that decides if you should read the email. Spoiler: usually no.', stack: ['Python', 'PyTorch', 'FastAPI'], palette: ['#b07cff', '#d8ff3a', '#ff8fb0'] },
];

window.ProjectsSection = function ProjectsSection({ pal }) {
  return (
    <section id="projects" className="projects-section">
      <div className="section-head">
        <div className="num">// 03</div>
        <h2>Selected<br />Projects<span className="swirl">/* side-quests &amp; weekend hacks */</span></h2>
        <div className="meta">{PROJECTS.length} repos · 0 outages · ∞ regrets</div>
      </div>

      <Sticker bottom={20} right={-20} rotate={8}><Smiley size={62} fill={pal.tomato} /></Sticker>

      <div className="polaroid-grid">
        {PROJECTS.map((p, i) => {
          const inner = (
            <>
              <div className="polaroid-thumb">
                <ProjThumb kind={p.kind} palette={p.palette} />
                <div className="polaroid-num">№ {p.num}</div>
                <div className="polaroid-tag">{p.tag}</div>
              </div>
              <h3>{p.name}</h3>
              <div className="caption">{p.caption}</div>
              <div className="stack">
                {p.stack.map(s => <span key={s}>{s}</span>)}
              </div>
            </>
          );
          return p.url ? (
            <a key={p.num} className="polaroid" href={p.url} target="_blank" rel="noreferrer">{inner}</a>
          ) : (
            <div key={p.num} className="polaroid">{inner}</div>
          );
        })}

        <div className="polaroid polaroid-wip">
          <div className="wip-stamp">WIP</div>
          <div className="wip-tape" aria-hidden="true" />
          <div className="wip-eyebrow">// № 07 · status: brewing</div>
          <h3 className="wip-title">work in progress</h3>
          <div className="wip-caption">
            future side-quest loading… probably involves AI, definitely involves coffee.<br />
            <span className="wip-blink">▮</span> your idea here?
          </div>
          <div className="wip-foot">
            <span>ETA: soon™</span>
            <span>·</span>
            <span>vibes: caffeinated</span>
          </div>
        </div>
      </div>
    </section>
  );
};

/* =========================================================
   CONTACT
========================================================= */
window.ContactSection = function ContactSection() {
  const [copied, setCopied] = useState_S(false);
  const copy = (e) => {
    e.preventDefault();
    navigator.clipboard?.writeText('rahulnilvan43@gmail.com');
    setCopied(true);
    setTimeout(() => setCopied(false), 1600);
  };
  return (
    <section id="contact" className="contact-section">
      <div className="contact-marquee" aria-hidden="true">
        {Array.from({ length: 2 }).map((_, n) => (
          <React.Fragment key={n}>
            <span>★ hire me · i bring snacks</span>
            <span>★ chennai-based · happily remote</span>
            <span>★ currently shipping AllKitty</span>
            <span>★ ./hello @ rahulnilvan43.gmail.com</span>
            <span>★ reply &lt; 24h · SLA: vibes</span>
          </React.Fragment>
        ))}
      </div>

      <div className="contact-grid">
        <div>
          <h2>Let's<br />make<br /><span className="acc">something</span> weird.</h2>
          <p className="lead">
            Email is the move. I read every one. Reply is usually a same-day <code style={{fontFamily:'JetBrains Mono'}}>200 OK</code> or a thoughtful <code style={{fontFamily:'JetBrains Mono'}}>410 Gone</code>.
          </p>

          <div className="contact-links">
            <a href="mailto:rahulnilvan43@gmail.com" onClick={copy}>
              <span className="label">Email</span>
              <span>{copied ? 'Copied to clipboard' : 'rahulnilvan43@gmail.com'}</span>
              <span className="arrow">→</span>
            </a>
            <a href="https://github.com/therahul-yo" target="_blank" rel="noreferrer">
              <span className="label">Github</span>
              <span>github.com/therahul-yo</span>
              <span className="arrow">↗</span>
            </a>
            <a href="https://www.linkedin.com/in/rahul-t-rn56/" target="_blank" rel="noreferrer">
              <span className="label">LinkedIn</span>
              <span>linkedin.com/in/rahul-t-rn56</span>
              <span className="arrow">↗</span>
            </a>
            <a href="assets/Rahul-Resume.pdf" target="_blank" rel="noreferrer">
              <span className="label">Resume</span>
              <span>The whole thing as a PDF</span>
              <span className="arrow">↓</span>
            </a>
          </div>
        </div>

        <div className="contact-side">
          <div className="post-stamp">
            <b>RT</b>
            <span>est<br />2004</span>
          </div>
          <div className="stamp-card">
            Currently in
            <span className="big">Chennai, IN</span>
            UTC+05:30 · Replying within 24h
          </div>
        </div>
      </div>

      <div className="foot">
        <span>© 2026 Rahul T · Built by hand, kept on file</span>
        <span>Made with HTML, sweat, and a lot of paper</span>
      </div>

    </section>
  );
};

window.PixelVillage = function PixelVillage() {
  const canvasRef = React.useRef(null);

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const W = 240, H = 90;
    canvas.width = W; canvas.height = H;
    ctx.imageSmoothingEnabled = false;

    const PALETTE = {
      sky: '#f0e6c8',
      grassDark: '#5a7a2a',
      grassLight: '#7fa83a',
      dirt: '#6a4a2a',
      wall: '#ffd58a',
      wallShade: '#e0a44a',
      roof: '#c4422a',
      roofShade: '#8a2814',
      door: '#5a3414',
      window: '#7fc4ff',
      chimney: '#8a2814',
      smoke: 'rgba(120,120,120,0.55)',
      sun: '#ffd24a',
      char: '#1a1a1a',
      charSkin: '#ffcc88',
      charShirt: '#3a78d8',
      charPants: '#2a2a4a',
    };

    // House layout: list of {x,y,c}. x,y in canvas pixel coords.
    const HX = 130, HY = 42;
    const houseTarget = [];
    // Walls 18 wide x 14 tall starting at HX,HY
    const wallW = 18, wallH = 14;
    // bottom-up build order
    for (let row = wallH - 1; row >= 0; row--) {
      for (let col = 0; col < wallW; col++) {
        const x = HX + col, y = HY + row;
        // door (cols 8-9, rows 10-13)
        if (col >= 8 && col <= 9 && row >= 10) {
          houseTarget.push({ x, y, c: PALETTE.door, layer: 'door' });
        }
        // window (cols 3-4, rows 4-5)
        else if (col >= 3 && col <= 4 && row >= 4 && row <= 5) {
          houseTarget.push({ x, y, c: PALETTE.window, layer: 'window' });
        }
        // wall edges shaded
        else if (col === 0 || col === wallW - 1 || row === wallH - 1) {
          houseTarget.push({ x, y, c: PALETTE.wallShade, layer: 'wall' });
        } else {
          houseTarget.push({ x, y, c: PALETTE.wall, layer: 'wall' });
        }
      }
    }
    // Roof: triangle on top
    const roofRows = 7;
    for (let r = roofRows - 1; r >= 0; r--) {
      const span = wallW + 4 - r * 2; // start wider, narrow up
      const startX = HX - 2 + r;
      const y = HY - 1 - r;
      for (let c = 0; c < span; c++) {
        const isEdge = c === 0 || c === span - 1 || r === 0;
        houseTarget.push({ x: startX + c, y, c: isEdge ? PALETTE.roofShade : PALETTE.roof, layer: 'roof' });
      }
    }
    // Chimney: 3x4 on roof, slightly left of center
    for (let cy = 0; cy < 4; cy++) {
      for (let cx = 0; cx < 3; cx++) {
        const x = HX + 3 + cx, y = HY - 5 - cy;
        houseTarget.push({ x, y, c: cx === 2 || cy === 3 ? PALETTE.roofShade : PALETTE.chimney, layer: 'chimney' });
      }
    }

    const groundY = HY + wallH; // top of ground line
    const spawnX = 10;
    const placed = [];
    let buildIdx = 0;
    let phase = 'fetch'; // fetch -> carry -> place -> return -> ...
    let charX = spawnX, charY = groundY - 8;
    let charFrame = 0;
    let carrying = null;
    let smokeParticles = [];
    let resetTimer = 0;
    let tick = 0;
    let sunY = H - 5;

    const drawChar = (x, y, dir, walking) => {
      x = Math.round(x); y = Math.round(y);
      const f = walking ? Math.floor(tick / 6) % 2 : 0;
      // hair
      ctx.fillStyle = PALETTE.char;
      ctx.fillRect(x + 1, y, 3, 1);
      ctx.fillRect(x, y + 1, 5, 1);
      // face
      ctx.fillStyle = PALETTE.charSkin;
      ctx.fillRect(x + 1, y + 2, 3, 1);
      // eyes
      ctx.fillStyle = PALETTE.char;
      ctx.fillRect(x + (dir > 0 ? 3 : 1), y + 2, 1, 1);
      // shirt
      ctx.fillStyle = PALETTE.charShirt;
      ctx.fillRect(x, y + 3, 5, 2);
      // pants
      ctx.fillStyle = PALETTE.charPants;
      ctx.fillRect(x + 1, y + 5, 3, 1);
      // legs
      if (f === 0) {
        ctx.fillRect(x + 1, y + 6, 1, 2);
        ctx.fillRect(x + 3, y + 6, 1, 2);
      } else {
        ctx.fillRect(x + 1, y + 6, 1, 1);
        ctx.fillRect(x + 1, y + 7, 1, 1);
        ctx.fillRect(x + 3, y + 6, 1, 1);
        ctx.fillRect(x + 3, y + 7, 1, 1);
      }
    };

    let raf;
    const loop = () => {
      tick++;
      // sky
      ctx.fillStyle = PALETTE.sky;
      ctx.fillRect(0, 0, W, H);
      // sun
      ctx.fillStyle = PALETTE.sun;
      const sx = 30, sy = 14 + Math.sin(tick / 80) * 2;
      ctx.fillRect(sx, sy, 5, 5);
      ctx.fillRect(sx - 1, sy + 1, 1, 3);
      ctx.fillRect(sx + 5, sy + 1, 1, 3);
      ctx.fillRect(sx + 1, sy - 1, 3, 1);
      ctx.fillRect(sx + 1, sy + 5, 3, 1);
      // distant clouds
      ctx.fillStyle = '#fffaf0';
      const cl = ((tick / 4) % (W + 30)) - 30;
      ctx.fillRect(cl, 10, 8, 2);
      ctx.fillRect(cl + 2, 9, 4, 1);
      const cl2 = ((tick / 3 + 80) % (W + 30)) - 30;
      ctx.fillRect(cl2, 22, 6, 2);

      // ground
      ctx.fillStyle = PALETTE.grassDark;
      ctx.fillRect(0, groundY, W, 1);
      ctx.fillStyle = PALETTE.grassLight;
      for (let i = 0; i < W; i += 2) ctx.fillRect(i, groundY, 1, 1);
      ctx.fillStyle = PALETTE.dirt;
      ctx.fillRect(0, groundY + 1, W, H - groundY - 1);
      // little grass tufts
      ctx.fillStyle = PALETTE.grassDark;
      for (let i = 0; i < W; i += 11) {
        ctx.fillRect((i + 3) % W, groundY - 1, 1, 1);
      }

      // placed blocks
      placed.forEach(b => {
        ctx.fillStyle = b.c;
        ctx.fillRect(b.x, b.y, 1, 1);
      });

      // logic per phase
      const target = houseTarget[buildIdx];
      const targetX = target ? target.x : spawnX;

      if (phase === 'fetch') {
        // walk toward spawn pile
        const goal = spawnX;
        if (charX > goal) charX -= 0.45;
        else { phase = 'pickup'; }
      } else if (phase === 'pickup') {
        carrying = target;
        phase = 'carry';
      } else if (phase === 'carry') {
        const goal = targetX - 2;
        if (charX < goal) charX += 0.55;
        else if (charX > goal) charX -= 0.55;
        else phase = 'place';
      } else if (phase === 'place') {
        if (carrying) placed.push(carrying);
        carrying = null;
        buildIdx++;
        if (buildIdx >= houseTarget.length) {
          phase = 'celebrate';
          resetTimer = 0;
        } else {
          phase = 'fetch';
        }
      } else if (phase === 'celebrate') {
        // chimney smoke
        if (tick % 14 === 0) {
          smokeParticles.push({ x: HX + 4, y: HY - 9, life: 0, drift: (Math.random() - 0.5) * 0.3 });
        }
        resetTimer++;
        if (resetTimer > 360) {
          // reset
          placed.length = 0;
          smokeParticles = [];
          buildIdx = 0;
          phase = 'fetch';
          charX = spawnX;
        }
      }

      // spawn block stash at left
      ctx.fillStyle = PALETTE.wall;
      ctx.fillRect(spawnX - 2, groundY - 1, 1, 1);
      ctx.fillRect(spawnX - 1, groundY - 1, 1, 1);
      ctx.fillRect(spawnX, groundY - 2, 1, 1);

      // smoke
      smokeParticles = smokeParticles.filter(p => p.life < 50);
      smokeParticles.forEach(p => {
        ctx.fillStyle = `rgba(140,140,140,${0.6 - p.life / 80})`;
        ctx.fillRect(Math.round(p.x), Math.round(p.y - p.life * 0.5), 2, 2);
        p.x += p.drift;
        p.life++;
      });

      // character
      const dir = phase === 'fetch' ? -1 : 1;
      const walking = phase === 'fetch' || phase === 'carry';
      drawChar(charX, charY, dir, walking);
      // carried block
      if (carrying) {
        ctx.fillStyle = carrying.c;
        ctx.fillRect(Math.round(charX) + 2, charY - 2, 1, 1);
      }

      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, []);

  return (
    <div className="pixel-village" aria-hidden="true">
      <div className="pixel-village-eyebrow">// rt-os :: world.tick — tiny dev building tiny house</div>
      <canvas ref={canvasRef} className="pixel-village-canvas" />
    </div>
  );
};

window.PageFire = function PageFire() {
  return (
    <div className="page-fire" aria-hidden="true">
      <svg viewBox="0 0 1600 280" preserveAspectRatio="none" width="100%" height="280">
        <defs>
          <filter id="fireblur" x="-10%" y="-10%" width="120%" height="120%">
            <feGaussianBlur stdDeviation="3" />
          </filter>
          <radialGradient id="hotcore" cx="50%" cy="100%" r="80%">
            <stop offset="0%" stopColor="#fff7c2" />
            <stop offset="35%" stopColor="#ffd24a" />
            <stop offset="70%" stopColor="#ff6a1a" />
            <stop offset="100%" stopColor="#a0140a" stopOpacity="0" />
          </radialGradient>
          <linearGradient id="fireRed" x1="50%" y1="100%" x2="50%" y2="0%">
            <stop offset="0%" stopColor="#fff2a8" />
            <stop offset="35%" stopColor="#ffb33a" />
            <stop offset="70%" stopColor="#ff4a14" />
            <stop offset="100%" stopColor="#7a0a0a" />
          </linearGradient>
          <linearGradient id="fireOrange" x1="50%" y1="100%" x2="50%" y2="0%">
            <stop offset="0%" stopColor="#fff5b0" />
            <stop offset="40%" stopColor="#ffc04a" />
            <stop offset="80%" stopColor="#ff5f1a" />
            <stop offset="100%" stopColor="#a0220a" />
          </linearGradient>
          <linearGradient id="fireYellow" x1="50%" y1="100%" x2="50%" y2="0%">
            <stop offset="0%" stopColor="#ffffd0" />
            <stop offset="50%" stopColor="#ffd852" />
            <stop offset="100%" stopColor="#ff8a1a" stopOpacity=".9" />
          </linearGradient>
        </defs>

        <rect x="0" y="220" width="1600" height="60" fill="#ff4a14" opacity=".55" />
        <ellipse cx="800" cy="280" rx="900" ry="80" fill="url(#hotcore)" opacity=".7" />

        <g filter="url(#fireblur)">
          <path d="M 0 280 C 20 220 60 200 40 150 C 30 110 70 100 60 60 C 90 90 110 130 95 175 C 110 155 130 175 125 215 C 130 240 100 280 0 280 Z" fill="url(#fireRed)" />
          <path d="M 130 280 C 110 230 150 210 125 160 C 115 130 160 110 150 80 C 195 115 210 165 185 210 C 205 195 220 235 215 270 C 220 280 175 280 130 280 Z" fill="url(#fireOrange)" />
          <path d="M 260 280 C 240 220 280 200 255 150 C 240 110 290 90 280 50 C 330 95 345 155 320 200 C 340 185 360 220 350 260 C 355 280 305 280 260 280 Z" fill="url(#fireRed)" />
          <path d="M 380 280 C 360 230 395 210 380 170 C 370 140 405 130 400 100 C 425 130 440 165 425 200 C 440 185 455 215 450 250 C 460 275 425 280 380 280 Z" fill="url(#fireOrange)" />
          <path d="M 510 280 C 480 215 530 195 500 140 C 485 105 545 80 530 40 C 590 90 605 160 575 215 C 600 195 625 240 615 270 C 625 280 555 280 510 280 Z" fill="url(#fireRed)" />
          <path d="M 650 280 C 625 220 665 195 645 145 C 630 110 680 95 670 60 C 705 95 720 145 700 190 C 720 175 740 215 730 255 C 740 278 695 280 650 280 Z" fill="url(#fireOrange)" />
          <path d="M 790 280 C 760 210 815 190 785 130 C 770 95 830 70 815 30 C 875 80 895 150 860 205 C 885 185 910 230 900 265 C 910 280 840 280 790 280 Z" fill="url(#fireRed)" />
          <path d="M 940 280 C 915 225 955 205 935 155 C 920 120 970 105 960 70 C 1000 105 1015 155 990 200 C 1015 180 1035 225 1025 260 C 1035 280 985 280 940 280 Z" fill="url(#fireOrange)" />
          <path d="M 1080 280 C 1050 215 1100 195 1070 140 C 1055 105 1115 80 1100 40 C 1160 90 1180 160 1145 215 C 1170 195 1195 240 1185 270 C 1195 280 1125 280 1080 280 Z" fill="url(#fireRed)" />
          <path d="M 1220 280 C 1195 220 1235 200 1215 150 C 1200 115 1250 100 1240 65 C 1280 100 1295 150 1270 195 C 1295 175 1315 220 1305 255 C 1315 278 1265 280 1220 280 Z" fill="url(#fireOrange)" />
          <path d="M 1360 280 C 1330 215 1380 195 1350 140 C 1335 105 1395 80 1380 40 C 1440 90 1460 160 1425 215 C 1450 195 1475 240 1465 270 C 1475 280 1405 280 1360 280 Z" fill="url(#fireRed)" />
          <path d="M 1500 280 C 1475 220 1515 200 1495 150 C 1480 115 1530 100 1520 65 C 1560 100 1575 150 1550 195 C 1575 175 1600 220 1585 280 L 1500 280 Z" fill="url(#fireOrange)" />
        </g>

        <g opacity=".95" filter="url(#fireblur)">
          <path d="M 50 280 C 40 245 70 235 60 200 C 55 180 80 175 75 155 C 90 175 100 200 90 225 C 100 215 110 235 105 255 C 110 275 90 280 50 280 Z" fill="url(#fireYellow)" />
          <path d="M 190 280 C 175 240 205 230 195 200 C 188 175 215 165 210 145 C 230 170 240 200 225 230 C 240 220 250 245 245 270 C 250 280 220 280 190 280 Z" fill="url(#fireYellow)" />
          <path d="M 320 280 C 305 240 335 225 320 185 C 313 160 345 150 340 125 C 365 155 375 195 355 225 C 370 215 385 245 380 270 C 385 280 350 280 320 280 Z" fill="url(#fireYellow)" />
          <path d="M 440 280 C 425 245 450 235 440 205 C 432 180 460 175 455 155 C 475 180 485 210 470 235 C 485 225 495 250 490 270 C 495 280 470 280 440 280 Z" fill="url(#fireYellow)" />
          <path d="M 570 280 C 550 230 590 215 570 170 C 560 145 605 130 595 100 C 635 140 650 195 625 235 C 650 215 670 255 660 275 C 670 280 615 280 570 280 Z" fill="url(#fireYellow)" />
          <path d="M 710 280 C 695 240 720 230 710 200 C 700 175 725 165 720 145 C 740 170 755 200 740 230 C 755 220 770 250 765 270 C 770 280 740 280 710 280 Z" fill="url(#fireYellow)" />
          <path d="M 850 280 C 830 230 870 215 845 165 C 835 140 880 125 870 95 C 915 140 935 195 905 240 C 930 220 950 260 940 275 C 950 280 895 280 850 280 Z" fill="url(#fireYellow)" />
          <path d="M 1000 280 C 985 240 1015 230 1005 200 C 998 175 1025 165 1020 145 C 1040 170 1055 200 1040 230 C 1055 220 1070 250 1065 270 C 1070 280 1030 280 1000 280 Z" fill="url(#fireYellow)" />
          <path d="M 1140 280 C 1120 235 1160 215 1135 170 C 1125 145 1170 130 1160 100 C 1200 140 1215 195 1190 235 C 1215 215 1235 255 1225 275 C 1235 280 1185 280 1140 280 Z" fill="url(#fireYellow)" />
          <path d="M 1280 280 C 1265 240 1290 230 1280 200 C 1273 175 1300 165 1295 145 C 1315 170 1330 200 1315 230 C 1330 220 1345 250 1340 270 C 1345 280 1310 280 1280 280 Z" fill="url(#fireYellow)" />
          <path d="M 1420 280 C 1400 235 1440 215 1415 170 C 1405 145 1450 130 1440 100 C 1480 140 1495 195 1470 235 C 1495 215 1515 255 1505 275 C 1515 280 1465 280 1420 280 Z" fill="url(#fireYellow)" />
        </g>

        <g opacity=".9">
          <circle cx="80" cy="120" r="2.5" fill="#ffd24a">
            <animate attributeName="cy" values="120;40;120" dur="2.4s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.9;0;0.9" dur="2.4s" repeatCount="indefinite" />
          </circle>
          <circle cx="280" cy="100" r="2" fill="#ff8a1a">
            <animate attributeName="cy" values="100;20;100" dur="2.8s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.9;0;0.9" dur="2.8s" repeatCount="indefinite" />
          </circle>
          <circle cx="520" cy="130" r="3" fill="#ffd24a">
            <animate attributeName="cy" values="130;30;130" dur="3.2s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.9;0;0.9" dur="3.2s" repeatCount="indefinite" />
          </circle>
          <circle cx="760" cy="110" r="2.2" fill="#ff5f1a">
            <animate attributeName="cy" values="110;25;110" dur="2.6s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.9;0;0.9" dur="2.6s" repeatCount="indefinite" />
          </circle>
          <circle cx="1000" cy="120" r="2.5" fill="#ffd24a">
            <animate attributeName="cy" values="120;30;120" dur="3s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.9;0;0.9" dur="3s" repeatCount="indefinite" />
          </circle>
          <circle cx="1240" cy="100" r="2" fill="#ff8a1a">
            <animate attributeName="cy" values="100;20;100" dur="2.7s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.9;0;0.9" dur="2.7s" repeatCount="indefinite" />
          </circle>
          <circle cx="1480" cy="115" r="2.4" fill="#ffd24a">
            <animate attributeName="cy" values="115;25;115" dur="2.5s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.9;0;0.9" dur="2.5s" repeatCount="indefinite" />
          </circle>
        </g>
      </svg>
    </div>
  );
};
