// Page sections — composed in app.jsx

const Nav = () => {
  const [scrolled, setScrolled] = React.useState(false);
  const [open, setOpen] = React.useState(false);
  React.useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    window.addEventListener('scroll', onScroll);
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  React.useEffect(() => {
    document.body.classList.toggle('nav-open', open);
    return () => document.body.classList.remove('nav-open');
  }, [open]);
  const smoothTo = (e, hash) => {
    e.preventDefault();
    setOpen(false);
    const id = hash.replace(/^#/, '');
    const el = id === 'top' ? null : document.getElementById(id);
    if (id !== 'top' && !el) return;
    const gsap = window.gsap;
    const endY = el ? el.getBoundingClientRect().top + window.scrollY : 0;
    if (gsap) {
      if (window.ScrollToPlugin) gsap.registerPlugin(window.ScrollToPlugin);
      const proxy = { y: window.scrollY };
      gsap.to(proxy, {
        y: endY,
        duration: 0.9,
        ease: 'power2.inOut',
        onUpdate: () => window.scrollTo(0, proxy.y),
      });
    } else {
      window.scrollTo({ top: endY, behavior: 'smooth' });
    }
    history.replaceState(null, '', location.pathname + location.search);
  };
  return (
    <nav className={`nav ${scrolled ? 'scrolled' : ''} ${open ? 'is-open' : ''}`}>
      <div className="shell nav-inner">
        <a href="#top" className="nav-logo" onClick={(e) => smoothTo(e, '#top')}>R<span className="dot">.</span></a>
        <div className="nav-menu">
          <a href="#top" onClick={(e) => smoothTo(e, '#top')}>About</a>
          <a href="#work" onClick={(e) => smoothTo(e, '#work')}>Portfolio</a>
          <a href="#contact" onClick={(e) => smoothTo(e, '#contact')}>Contact</a>
        </div>
        <Btn kind="primary" className="nav-cta" href="https://www.upwork.com/freelancers/mahamudhasanrashel" target="_blank" rel="noreferrer noopener" arrow>Work with me</Btn>
        <button
          type="button"
          className="nav-toggle"
          aria-label={open ? 'Close menu' : 'Open menu'}
          aria-expanded={open}
          onClick={() => setOpen(v => !v)}
        >
          <span /><span /><span />
        </button>
      </div>
      <div className="nav-backdrop" aria-hidden="true" onClick={() => setOpen(false)}></div>
      <div className="nav-drawer" role="dialog" aria-hidden={!open}>
        <a href="#top" onClick={(e) => smoothTo(e, '#top')}>About</a>
        <a href="#work" onClick={(e) => smoothTo(e, '#work')}>Portfolio</a>
        <a href="#contact" onClick={(e) => smoothTo(e, '#contact')}>Contact</a>
        <Btn kind="primary" href="https://www.upwork.com/freelancers/mahamudhasanrashel" target="_blank" rel="noreferrer noopener" arrow onClick={() => setOpen(false)}>Work with me</Btn>
      </div>
    </nav>
  );
};

const Hero = () => (
  <header id="top" className="shell hero" data-screen-label="01 Hero">
    <div>
      <div className="hero-status">
        <span className="status-pill">
          <span className="status-dot" aria-hidden="true"></span>
          Available for new project
        </span>
      </div>
      <div className="hero-eyebrow-row">
        <Eyebrow>Full Stack Software Engineer · Bangladesh</Eyebrow>
      </div>
      <p className="hero-sub">
        Hey, I'm Rashel. I build web and mobile products <span className="hl">end to end</span>, the kind of stuff real people open every day. Next.js on the front, Node.js on the back, React Native on phones, and a soft spot for WordPress that simply will not leave me alone.
      </p>
      <p className="hero-sub">
        I have been freelancing since 2011, back when Upwork was still called oDesk. A hundred plus projects later, I am still here with a <span className="hl">100% Job Success Score</span> that I am quietly superstitious about.
      </p>
      <div className="hero-cta-row">
        <span className="hero-soc"><SocialRow /></span>
      </div>
    </div>
    <div className="portrait-wrap">
      <div className="hero-deco" style={{ top: -30, right: -10 }}></div>
      <span className="portrait-frame" aria-hidden="true"></span>
      <span className="portrait-tag" aria-hidden="true">RASHEL</span>
      <div className="portrait">
        <img src="uploads/rasel.webp" alt="Mahamud Hasan Rashel" loading="lazy" />
      </div>
      <span className="portrait-dot" aria-hidden="true"></span>
    </div>
  </header>
);

// ───── Upwork stats strip ─────

const StatBox = ({ icon, target, suffix, label, sub, format, duration = 1.6 }) => {
  const ref = React.useRef(null);
  const numRef = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const gsap = window.gsap;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (!e.isIntersecting) return;
        io.unobserve(e.target);
        const obj = { v: 0 };
        if (gsap) {
          gsap.to(obj, {
            v: target,
            duration,
            ease: 'power2.out',
            onUpdate: () => {
              if (numRef.current) numRef.current.textContent = (format ? format(obj.v) : Math.round(obj.v).toString());
            }
          });
        } else if (numRef.current) {
          numRef.current.textContent = format ? format(target) : String(target);
        }
      });
    }, { threshold: 0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [target, duration]);
  return (
    <div className="stat-box" ref={ref}>
      <div className="stat-icon" aria-hidden="true">{icon}</div>
      <div className="stat-body">
        <div className="stat-num">
          <span ref={numRef}>0</span>
          {suffix && <span className="stat-suffix">{suffix}</span>}
        </div>
        <div className="stat-label">{label}</div>
        {sub && <div className="stat-sub">{sub}</div>}
      </div>
    </div>
  );
};

const UpworkStats = () => (
  <section className="shell upwork-stats" data-screen-label="01b Stats">
    <div className="stats-head">
      <Eyebrow>The Upwork receipts, since 2011 (back when it was still oDesk)</Eyebrow>
    </div>
    <div className="stats-row">
      <StatBox
        icon={<svg viewBox="0 0 24 24" width="24" height="24"><path d="M12 2l2.39 4.84 5.34.78-3.86 3.77.91 5.31L12 14.77 7.22 16.7l.91-5.31L4.27 7.62l5.34-.78z" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round"/></svg>}
        target={100}
        suffix="%"
        label="Job Success Score"
        sub="Held steady across every contract, every client"
        duration={1.2}
      />
      <StatBox
        icon={<svg viewBox="0 0 24 24" width="24" height="24"><path d="M3 7h18v13H3zM8 7V5a2 2 0 012-2h4a2 2 0 012 2v2" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round"/><path d="M9 12l2 2 4-4" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>}
        target={100}
        suffix="+"
        label="Projects delivered"
        sub="Web platforms, mobile apps, AI integrations"
        duration={1.8}
      />
      <StatBox
        icon={<svg viewBox="0 0 24 24" width="24" height="24"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" strokeWidth="1.6"/><path d="M12 7v5l3 2" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>}
        target={5000}
        suffix="+ hrs"
        label="Tracked on Upwork"
        sub="Real, head-down build time"
        format={(v) => Math.round(v).toLocaleString('en-US')}
        duration={2.4}
      />
    </div>
  </section>
);

// ───── Unified 4-phase workflow morph ─────
// Web (left) → Mobile (right) → AI (left) → Deploy (right)
// Single pinned section. Device alternates sides per phase.

const PHASES = [
  {
    key: 'web',
    title: 'Web is where most of the work lives.',
    body: [
      "Over a decade of marketing sites, dashboards, and full SaaS surfaces. React and Next.js on the front, Node.js carrying the back, the database wherever it makes sense.",
      <>And yes, I still happily ship WordPress. Custom themes from scratch, on-page SEO plugins, including one that lives in the <a className="text-accent-link" href="https://profiles.wordpress.org/oiiio/" target="_blank" rel="noreferrer noopener">official WordPress.org plugin directory</a>.</>,
    ],
    side: 'right',
  },
  {
    key: 'mobile',
    title: 'One codebase, two app stores.',
    body: [
      "React Native is my home for mobile. One codebase, the App Store and Google Play, none of the usual tax of running two separate teams.",
      "From flight booking platforms to portfolio apps for creative pros, I have shipped a fair stack of production React Native apps that real people open every day.",
    ],
    side: 'left',
  },
  {
    key: 'ai',
    title: 'AI, the part I am learning out loud.',
    body: [
      "This is the new frontier and I am right there in it with everyone else. A few pet projects on the weekends, a few real client integrations on the weekdays. Plugging LLMs into existing apps, wiring up RAG so the model actually knows the business, and shipping small agentic flows with n8n and LangChain.",
      "I am not going to pretend I have a decade of AI experience, because nobody honestly does. What I can promise is a curious builder who reads the new papers, tries the new tools, and is happy to learn alongside your team as the stack evolves week by week.",
    ],
    side: 'right',
  },
  {
    key: 'deploy',
    title: 'Then comes the boring, beautiful part. Shipping it.',
    body: [
      "A pretty git diff is not a product. I move the code onto AWS, EC2 for the app, RDS for the database, and the smaller services that quietly keep the lights on.",
      "GitHub Actions handles the CI/CD pipeline, Docker keeps everything behaving the same way on my laptop, on staging, and on the real server. The goal is calm deploys. No 2 a.m. fire drills, no Slack panic, just a green checkmark and a customer who has no idea you pushed anything at all.",
    ],
    side: 'left',
  },
];

const easeInOutCubic = (t) => t < 0.5 ? 4*t*t*t : 1 - Math.pow(-2*t+2, 3)/2;

// Per-phase position for the floating device (in pin-relative units).
// xPercent: 0 = far left, 100 = far right. yPercent: 0 = top, 100 = bottom.
const DEVICE_SLOTS = {
  web:    { xPercent: 72, yPercent: 50, scale: 1.00 },
  mobile: { xPercent: 28, yPercent: 50, scale: 0.95 },
  ai:     { xPercent: 72, yPercent: 50, scale: 1.00 },
  deploy: { xPercent: 28, yPercent: 50, scale: 0.95 },
};

const lerp = (a, b, t) => a + (b - a) * t;

const WorkflowMorph = () => {
  const sectionRef = React.useRef(null);
  const anchorsRef = React.useRef([]);
  const floatRef = React.useRef(null);
  const visualsRef = React.useRef([]);

  React.useEffect(() => {
    const gsap = window.gsap;
    const ScrollTrigger = window.ScrollTrigger;
    if (!gsap || !ScrollTrigger) return;
    gsap.registerPlugin(ScrollTrigger);

    const N = PHASES.length;

    const measure = (i) => {
      const a = anchorsRef.current[i];
      const s = sectionRef.current;
      if (!a || !s) return null;
      const ar = a.getBoundingClientRect();
      const sr = s.getBoundingClientRect();
      return {
        x: ar.left - sr.left,
        y: ar.top - sr.top,
        w: ar.width,
        h: ar.height,
      };
    };

    let st;

    const build = () => {
      if (st) st.kill();

      const p0 = measure(0);
      if (!p0) return;
      gsap.set(floatRef.current, {
        x: p0.x, y: p0.y, width: p0.w, height: p0.h,
      });
      visualsRef.current.forEach((el, i) => {
        if (el) gsap.set(el, { opacity: i === 0 ? 1 : 0 });
      });

      const tl = gsap.timeline({
        defaults: { ease: 'power2.inOut' },
      });

      for (let i = 0; i < N - 1; i++) {
        const nxt = measure(i + 1);
        if (!nxt) continue;
        // Give transitions touching the mobile phase extra room so the
        // device has time to scroll/transform with the new visual visible.
        const touchesMobile = PHASES[i].key === 'mobile' || PHASES[i + 1].key === 'mobile';
        const moveDur = touchesMobile ? 1.6 : 1;
        tl.to(floatRef.current, {
          x: nxt.x, y: nxt.y, width: nxt.w, height: nxt.h,
          duration: moveDur,
        });
        // Sequential fades centered in the middle of the move — fade out
        // fully completes before the next fades in, so they never stack.
        const fadeOffset = (moveDur - 0.5) / 2;
        tl.to(visualsRef.current[i],     { opacity: 0, duration: 0.25, ease: 'power1.in'  }, `<+=${fadeOffset}`);
        tl.to(visualsRef.current[i + 1], { opacity: 1, duration: 0.25, ease: 'power1.out' }, `<+=0.25`);
      }

      st = ScrollTrigger.create({
        trigger: sectionRef.current,
        start: 'top 15%',
        end: 'bottom bottom',
        scrub: 0.8,
        animation: tl,
        invalidateOnRefresh: true,
      });
    };

    build();

    const onResize = () => {
      ScrollTrigger.refresh();
      build();
    };
    window.addEventListener('resize', onResize);

    return () => {
      if (st) st.kill();
      window.removeEventListener('resize', onResize);
    };
  }, []);

  return (
    <section ref={sectionRef} className="workflow-section" id="work" data-screen-label="02 Workflow">
      {PHASES.map((p, i) => (
        <div key={p.key} className={`wf-row visual-${p.side}`}>
          <div className="shell wf-row-inner">
            <div className="wf-text">
              <h2 className="h2" style={{ marginBottom: 22 }}>{p.title}</h2>
              {p.body.map((b, j) => <p key={j}>{b}</p>)}
            </div>
            <div className="wf-anchor" ref={el => anchorsRef.current[i] = el}>
              <div className="wf-anchor-visual"><PhaseVisual kind={p.key} /></div>
            </div>
          </div>
        </div>
      ))}
      <div ref={floatRef} className="wf-floating">
        {PHASES.map((p, i) => (
          <div key={p.key} ref={el => visualsRef.current[i] = el} className="wf-float-visual">
            <PhaseVisual kind={p.key} />
          </div>
        ))}
      </div>
    </section>
  );
};

const PhaseVisual = ({ kind }) => {
  if (kind === 'web') return <WebVisual />;
  if (kind === 'mobile') return <MobileVisual />;
  if (kind === 'ai') return <AIVisual />;
  if (kind === 'deploy') return <DeployVisual />;
  return null;
};

const WEB_MENUS = [
  ["Overview", "Projects", "Activity", "Settings"],
  ["Dashboard", "Clients", "Invoices", "Reports"],
  ["Inbox", "Pipeline", "Insights", "Team"]
];

const WebVisual = () => {
  const [menuIdx, setMenuIdx] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => setMenuIdx(i => (i + 1) % WEB_MENUS.length), 3000);
    return () => clearInterval(id);
  }, []);
  const menu = WEB_MENUS[menuIdx];
  return (
  <div className="phase-visual web">
    <div className="vbrowser">
      <div className="vbrowser-bar">
        <div className="dots"><span></span><span></span><span></span></div>
        <div className="url"><span className="url-lock">⚲</span>rashel.cv<span className="url-caret"></span></div>
        <div className="vbrowser-tabs">
          <span className="vt active"></span><span className="vt"></span><span className="vt"></span>
        </div>
      </div>
      <div className="vbrowser-body">
        <div className="vside">
          <div className="vside-logo">R<span className="vside-logo-dot">.</span></div>
          <div className="vside-menu" key={menuIdx}>
            {menu.map((item, i) => (
              <div key={item} className={`vside-item${i === 0 ? " act" : ""}`}>{item}</div>
            ))}
          </div>
        </div>
        <div className="vmain">
          <div className="vmain-head">
            <div className="vmain-title">Rashel<span className="vmain-title-dot">.</span></div>
            <div className="vmain-pill">Live</div>
          </div>
          <div className="vmain-sub">Real-time signal · Updated 2s ago</div>
          <div className="vcards">
            <div className="m-card a"><span className="vc-label">MRR</span><span className="vc-num">$48k</span></div>
            <div className="m-card b"><span className="vc-label">Users</span><span className="vc-num">2.1k</span></div>
            <div className="m-card c"><span className="vc-label">Health</span><span className="vc-num">98%</span></div>
          </div>
          <div className="vchart">
            <svg viewBox="0 0 240 60" preserveAspectRatio="none">
              <defs>
                <linearGradient id="vchart-grad" x1="0" x2="0" y1="0" y2="1">
                  <stop offset="0%" stopColor="#9463ee" stopOpacity="0.35" />
                  <stop offset="100%" stopColor="#9463ee" stopOpacity="0" />
                </linearGradient>
              </defs>
              <path className="vchart-fill" d="M0,48 L20,42 L40,46 L60,30 L80,36 L100,22 L120,28 L140,14 L160,20 L180,10 L200,18 L220,8 L240,12 L240,60 L0,60 Z" />
              <polyline className="vchart-line" points="0,48 20,42 40,46 60,30 80,36 100,22 120,28 140,14 160,20 180,10 200,18 220,8 240,12" />
            </svg>
          </div>
          <div className="vbrowser-foot">
            <div className="vstat dot"><i></i>Live</div>
            <div className="vstat bar"><span></span></div>
            <div className="vstat val"><span className="vstat-up">▲</span>+12.4%</div>
          </div>
        </div>
      </div>
    </div>
  </div>
  );
};

const MOBILE_CARDS = [
  { tag: "Inbox", num: "12" },
  { tag: "Tasks", num: "4" },
  { tag: "Focus", num: "2h" },
  { tag: "Streak", num: "7d" }
];

const MobileVisual = () => {
  const [activeCard, setActiveCard] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => setActiveCard(i => (i + 1) % MOBILE_CARDS.length), 1400);
    return () => clearInterval(id);
  }, []);
  return (
    <div className="phase-visual mobile">
      <div className="vphone">
        <div className="vphone-screen">
          <div className="vp-notch"></div>
          <div className="vp-status">
            <span>9:41</span>
            <span className="vp-status-right">
              <span className="vp-signal"><i></i><i></i><i></i><i></i></span>
              <span className="vp-batt"><span></span></span>
            </span>
          </div>
          <div className="vp-content">
            <div className="vp-greeting">Good morning</div>
            <div className="vp-name">Rashel<span className="vp-name-dot">.</span></div>
            <div className="vp-grid">
              {MOBILE_CARDS.map((c, i) => (
                <div
                  key={c.tag}
                  className={`m-card ${"abcd"[i]}${activeCard === i ? " is-active" : ""}`}
                >
                  <span className="vp-tag">{c.tag}</span>
                  <span className="vp-num">{c.num}</span>
                </div>
              ))}
            </div>
            <div className="vp-detail">
              <span className="vp-detail-label">{MOBILE_CARDS[activeCard].tag}</span>
              <span className="vp-detail-bar"><span style={{ width: `${30 + activeCard * 18}%` }}></span></span>
            </div>
          </div>
          <div className="vp-home"></div>
        </div>
      </div>
    </div>
  );
};

const AI_AGENT_LINES = [
  "Pulled 47 commits across 6 repos. Drafting a recap grouped by project — surfacing the checkout fix and the migration first",
  "Scanned 218 PRs this week. Highlighting the auth refactor and three flaky tests now green",
  "Read 92 issues. Clustering them by surface — checkout, search, and onboarding lead the list"
];

const AIVisual = () => {
  const [text, setText] = React.useState("");
  React.useEffect(() => {
    let line = 0, i = 0, timer;
    const tick = () => {
      const target = AI_AGENT_LINES[line];
      if (i <= target.length) {
        setText(target.slice(0, i));
        i++;
        timer = setTimeout(tick, 35);
      } else {
        timer = setTimeout(() => {
          line = (line + 1) % AI_AGENT_LINES.length;
          i = 0;
          setText("");
          tick();
        }, 2200);
      }
    };
    tick();
    return () => clearTimeout(timer);
  }, []);
  return (
    <div className="phase-visual ai">
      <div className="vai">
        <div className="vai-prompt">
          <span className="who">User</span>
          <span className="what">Generate a weekly recap from the team's activity logs.</span>
        </div>
        <div className="vai-prompt response">
          <span className="ai-orb"></span>
          <span className="who">Agent</span>
          <span className="what">
            {text}<span className="ai-cursor"></span>
          </span>
        </div>
      </div>
    </div>
  );
};

const DEPLOY_STEPS = ["install", "test", "build", "deploy"];

const DeployVisual = () => {
  const [active, setActive] = React.useState(3);
  React.useEffect(() => {
    const id = setInterval(() => {
      setActive(a => (a + 1) % (DEPLOY_STEPS.length + 1));
    }, 1800);
    return () => clearInterval(id);
  }, []);
  const stepClass = (i) => {
    if (active === DEPLOY_STEPS.length) return "vstep done";
    if (i < active) return "vstep done";
    if (i === active) return "vstep running";
    return "vstep";
  };
  const stepGlyph = (i) => {
    if (active === DEPLOY_STEPS.length) return "✓";
    if (i < active) return "✓";
    if (i === active) return "→";
    return "·";
  };
  return (
    <div className="phase-visual deploy">
      <div className="vdeploy">
        <div className="vdeploy-pipe">
          {DEPLOY_STEPS.map((s, i) => (
            <div key={s} className={stepClass(i)}>{stepGlyph(i)} {s}</div>
          ))}
        </div>
        <div className="vdeploy-log">
          <div><span className="dim">$</span> git push origin main</div>
          <div><span className="ok">✓</span> Linted 248 files — clean</div>
          <div><span className="ok">✓</span> Built in 24.3s — 0 errors</div>
          <div><span className="ok">✓</span> 312 unit tests passed</div>
          <div><span className="warn">!</span> Bundle 318kb · -8% vs main</div>
          <div><span className="ok">✓</span> Uploaded 42 assets to CDN</div>
          <div>Pushing to <span className="url">rashel.cv</span></div>
          <div className="log-final"><span className="ok">✓</span> Deployed in 1m 12s</div>
        </div>
      </div>
    </div>
  );
};

// A pause before the portfolio — a quiet quote on craft.
const PrincipleStatement = () => (
  <section className="shell" data-screen-label="06 Quote">
    <div className="principle-block">
      <p className="principle-text">
        Don't automate an undisciplined workflow. The computer won't solve what the customer's management can't.
      </p>
      <div className="quote-attr">— Larry Bernstein</div>
    </div>
  </section>
);

const ProjectCardWeb = ({ year, role, title, stack, outcome }) => (
  <article className="proj web">
    <div className="proj-thumb">
      <div className="proj-thumb-mock">
        <i className="t"></i>
        <i style={{width: '70%'}}></i>
        <i style={{width: '50%'}}></i>
        <div className="row"><span></span><span></span><span></span></div>
      </div>
    </div>
    <div className="proj-meta">
      <Eyebrow>{year} · {role}</Eyebrow>
      <h4>{title}</h4>
      <div className="stack">
        {stack.map((s, i) => <span key={s} className={`chip ${i === 0 ? 'featured' : ''}`}>{s}</span>)}
        <span className="chip outcome">{outcome}</span>
      </div>
    </div>
  </article>
);

const ProjectCardApp = ({ year, role, title, stack, outcome }) => (
  <article className="proj app">
    <div className="proj-thumb">
      <div className="proj-thumb-mock">
        <i className="t"></i>
        <i style={{width: '60%'}}></i>
        <div className="ps-icon-grid">
          <span></span><span></span><span></span><span></span>
        </div>
      </div>
    </div>
    <div className="proj-meta">
      <Eyebrow>{year} · {role}</Eyebrow>
      <h4>{title}</h4>
      <div className="stack">
        {stack.map((s, i) => <span key={s} className={`chip ${i === 0 ? 'featured' : ''}`}>{s}</span>)}
        <span className="chip outcome">{outcome}</span>
      </div>
    </div>
  </article>
);

const Portfolio = () => (
  <section className="shell section" id="portfolio" data-screen-label="07 Portfolio">
    <div className="portfolio-intro">
      <div>
        <Eyebrow>Selected work — 2024 / 2026</Eyebrow>
        <h2 className="h2" style={{ marginTop: 12 }}>A small, careful catalog.</h2>
      </div>
      <p>
        Two web platforms and three mobile apps I'm allowed to share publicly.
        The rest of the case studies live behind NDA — link to my Upwork below.
      </p>
    </div>
    <div className="portfolio-grid">
      <ProjectCardWeb
        year="2026" role="Lead frontend"
        title="Lattice — analytics dashboard for B2B teams"
        stack={["Next.js 15", "TypeScript", "Tailwind"]}
        outcome="+14% retention"
      />
      <ProjectCardWeb
        year="2025" role="Frontend"
        title="Tide — checkout redesign for fintech"
        stack={["React", "Stripe", "Framer"]}
        outcome="+22% conversion"
      />
      <ProjectCardApp
        year="2025" role="Native mobile"
        title="Lull — sleep coach"
        stack={["React Native", "Expo", "Reanimated"]}
        outcome="50k MAU"
      />
      <ProjectCardApp
        year="2024" role="Mobile + AI"
        title="Tutor — Claude-powered learning"
        stack={["Expo", "Claude API", "tRPC"]}
        outcome="4.8★ on App Store"
      />
      <ProjectCardApp
        year="2024" role="iOS"
        title="Hearth — recipe journal"
        stack={["Swift", "SwiftUI", "CoreData"]}
        outcome="Editor's pick"
      />

      <article className="proj video">
        <div className="video-meta">
          <Eyebrow>Client testimonial · 02:14</Eyebrow>
          <h4>"Rashel shipped what we'd been talking about for a year, in three weeks."</h4>
        </div>
        <button className="play-btn" aria-label="Play testimonial"></button>
      </article>

      <div className="stack-card">
        <div>
          <Eyebrow>Tech I work in daily</Eyebrow>
          <h4 style={{ marginTop: 10 }}>The full stack, in one place.</h4>
        </div>
        <div className="stack-chips">
          <span className="chip featured">React</span>
          <span className="chip featured">Next.js</span>
          <span className="chip featured">TypeScript</span>
          <span className="chip">React Native</span>
          <span className="chip">Expo</span>
          <span className="chip">Swift</span>
          <span className="chip">Tailwind</span>
          <span className="chip">tRPC</span>
          <span className="chip">Postgres</span>
          <span className="chip">Vercel</span>
          <span className="chip">GitHub Actions</span>
          <span className="chip">Cloudflare</span>
        </div>
        <Btn kind="secondary" href="https://www.upwork.com/freelancers/mahamudhasanrashel" arrow>
          <span className="text-accent-link">Read the long version</span>
        </Btn>
      </div>
    </div>
  </section>
);

const NDA = () => (
  <section className="shell section" data-screen-label="08 NDA">
    <div className="nda">
      <p>
        Most of my work sits under client confidentiality, so this page only shows the projects I'm free to talk about in public.
        For the longer story, my <a href="https://www.upwork.com/freelancers/mahamudhasanrashel" target="_blank" rel="noreferrer noopener"><strong>Upwork profile</strong></a> has the full catalogue, ratings, and the kinds of teams I usually slot into.
      </p>
      <p>
        Still not sure I'm your person? Here is the easy part. For any <strong>long-term contract</strong>, I offer the <strong>first 40 hours completely free</strong>, real work on your codebase, no strings attached. You get to see how I code, how I communicate, and how I fit with the rest of your team before a single invoice is sent. If it clicks, we keep going. If it doesn't, you walk away with the work and we part on friendly terms.
      </p>
      <Btn kind="primary" href="https://www.upwork.com/freelancers/mahamudhasanrashel" arrow>
        View Upwork profile
      </Btn>
    </div>
  </section>
);

const BigTestimonial = () => (
  <section className="shell section" data-screen-label="09 Testimonial">
    <div className="testimonial">
      <div className="test-thumb">
        <button className="play-btn" aria-label="Play video"></button>
      </div>
      <div className="test-quote">
        <q>
          We brought Rashel in for what we thought was a two-week polish pass on our dashboard.
          He stayed for the rest of the quarter and rebuilt the parts we'd been afraid to touch.
          Calm, fast, and ridiculously thorough — he writes the kind of frontend code our designers fall in love with on the first review.
        </q>
        <div className="test-author">
          <div className="test-avatar">M</div>
          <div>
            <div className="name">Maya Lindqvist</div>
            <div className="role">CTO · Lattice Analytics</div>
          </div>
        </div>
      </div>
    </div>
  </section>
);

// ───── Social icon row ─────

const SocialIcon = ({ kind, label, href = "#" }) => {
  const paths = {
    linkedin: <path d="M4.98 3.5C4.98 4.88 3.86 6 2.5 6S0 4.88 0 3.5 1.12 1 2.5 1s2.48 1.12 2.48 2.5zM.22 8h4.56v14H.22zM8 8h4.37v1.92h.06c.61-1.15 2.1-2.36 4.32-2.36 4.62 0 5.47 3.04 5.47 6.99V22h-4.56v-6.13c0-1.46-.03-3.34-2.04-3.34-2.04 0-2.36 1.6-2.36 3.24V22H8z" fill="currentColor"/>,
    whatsapp: <path d="M17.6 14.3c-.3-.15-1.77-.87-2.04-.97s-.47-.15-.67.15-.77.97-.94 1.17-.34.22-.64.07c-.3-.15-1.27-.47-2.42-1.5-.9-.8-1.5-1.78-1.67-2.08s-.02-.46.13-.61c.13-.13.3-.34.45-.51.15-.17.2-.29.3-.49.1-.2.05-.37-.02-.52s-.67-1.62-.92-2.21c-.24-.58-.49-.5-.67-.51l-.57-.01c-.2 0-.52.07-.79.37s-1.04 1.02-1.04 2.48 1.07 2.88 1.22 3.08c.15.2 2.1 3.2 5.08 4.49.71.31 1.26.49 1.69.63.71.23 1.36.2 1.87.12.57-.09 1.77-.72 2.02-1.42.25-.7.25-1.3.17-1.42-.07-.12-.27-.2-.57-.34zM12 .5C5.65.5.5 5.65.5 12c0 2.04.53 4.04 1.55 5.8L.5 23.5l5.86-1.53A11.4 11.4 0 0012 23.5c6.35 0 11.5-5.15 11.5-11.5S18.35.5 12 .5zm0 21A9.46 9.46 0 016.78 19.95l-.37-.22-3.48.91.93-3.39-.24-.39A9.5 9.5 0 1121.5 12 9.5 9.5 0 0112 21.5z" fill="currentColor"/>,
    twitter: <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" fill="currentColor"/>,
    email: <path d="M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2zM20 6l-8 5-8-5zm0 12H4V8l8 5 8-5z" fill="currentColor"/>,
    github: <path d="M12 .3a12 12 0 00-3.79 23.4c.6.11.82-.26.82-.58V21.1c-3.34.73-4.05-1.6-4.05-1.6-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.21.09 1.84 1.24 1.84 1.24 1.07 1.83 2.81 1.3 3.5.99.1-.78.42-1.31.76-1.61-2.66-.3-5.47-1.34-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.31-.54-1.53.12-3.18 0 0 1.01-.32 3.3 1.23a11.5 11.5 0 016 0c2.29-1.55 3.3-1.23 3.3-1.23.66 1.65.25 2.87.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.6-2.81 5.62-5.49 5.92.43.37.81 1.1.81 2.22v3.29c0 .32.21.69.83.58A12 12 0 0012 .3" fill="currentColor"/>,
    upwork: <path d="M18.56 5.97c-2.18 0-4.05 1.4-4.91 3.39-1.31-1.97-2.31-4.29-2.89-6.24H7.85v7.55a3.04 3.04 0 11-6.07 0V3.12H0v7.55a4.85 4.85 0 009.69 0c.06-.07.06-.13.06-.13.74 1.5 1.86 2.94 3.05 4.06l-1.36 6.14h1.81L14.2 16a8.65 8.65 0 004.36 1.13c2.83 0 5.13-2.31 5.13-5.16 0-2.84-2.29-5.16-5.13-5.16zm0 8.49c-1.31 0-2.55-.55-3.55-1.36l.13-.45.06-.13c.19-1.04.74-2.83 2.83-2.83 1.55 0 2.83 1.27 2.83 2.83.06 1.62-1.24 2.95-2.83 2.95z" fill="currentColor"/>,
  };
  return (
    <a className="soc-icon" href={href} aria-label={label} title={label} target={/^(https?:|mailto:|tel:)/i.test(href) ? "_blank" : undefined} rel={/^https?:\/\//i.test(href) ? "noreferrer noopener" : undefined}>
      <svg viewBox="0 0 24 24" width="20" height="20" xmlns="http://www.w3.org/2000/svg">
        {paths[kind]}
      </svg>
    </a>
  );
};

const SocialRow = () => (
  <div className="soc-row">
    <SocialIcon kind="linkedin" label="LinkedIn" href="https://www.linkedin.com/in/rasel-wp/" />
    <SocialIcon kind="whatsapp" label="WhatsApp" href="https://wa.me/8801611141900" />
    <SocialIcon kind="twitter" label="Twitter / X" href="https://x.com/rashel_wp" />
    <SocialIcon kind="email" label="Email" href="mailto:hi@rashel.cv" />
    <SocialIcon kind="github" label="GitHub" href="https://github.com/Rasel-Mahmud" />
    <SocialIcon kind="upwork" label="Upwork" href="https://www.upwork.com/freelancers/mahamudhasanrashel" />
  </div>
);

const Footer = () => (
  <footer className="shell foot-wrap" id="contact" data-screen-label="11 Footer">
    <div className="foot-row">
      <div>
        <Eyebrow>Get in touch</Eyebrow>
        <h2 className="h2" style={{ marginTop: 14, marginBottom: 16, maxWidth: '16ch' }}>
          One project at a time, done properly. Yours, maybe?
        </h2>
        <Btn kind="primary" href="mailto:hi@rashel.cv" arrow>hi@rashel.cv</Btn>
        <div style={{ marginTop: 28 }}>
          <Eyebrow>Find me on</Eyebrow>
          <div style={{ marginTop: 10 }}>
            <SocialRow />
          </div>
        </div>
      </div>
      <div className="foot-logo" aria-hidden="true">R<span className="dot"></span></div>
    </div>

  </footer>
);

// ───── Projects section (zigzag two-column rows) ─────

const PROJECTS = [
  {
    id: "melissa",
    title: "Personal coach website on WordPress",
    description: [
      "A custom WordPress site for Melissa Krestensen, a personal coach, healer, and course creator based in Australia. The site introduces who she is, what she offers, and how to work with her.",
      "I built the theme from scratch and added the parts that run her practice. Clients can book one-on-one sessions through an integrated booking system, a small shop lets her sell her favourite picks, an enrolment flow opens up her online courses, and a journal section gives her room to write.",
      "Marketing is wired in too. Google Tag Manager handles every event, and Meta Pixel tracks her Facebook ads, so her ad spend stays measurable end to end.",
    ],
    demoTone: "web",
    image: "uploads/project-one.webp",
    testimonialName: "Melissa Krestensen",
    testimonialRole: "Personal Coach · Australia",
    vimeoId: "218330866",
    thumbImage: "uploads/testimonial-one.webp",
    stack: ["WordPress", "Custom Theme", "WooCommerce", "Bookly", "LearnDash", "Google Tag Manager", "Meta Pixel", "Yoast SEO"],
    liveHref: "https://melissakrestensen.com.au/",
    liveLabel: "Visit live website",
    layout: {
      left: ["image", "description"],
      right: ["video", "stack", "cta"],
    },
  },
  {
    id: "antiaging",
    title: "Anti-aging podcast site on WordPress",
    description: [
      "A WordPress site for Anti-Aging Hacks, a podcast and resource hub by Faraz Khan that covers longevity, wellness, and anti-aging routines. The site brings together his podcast episodes, blog posts, product recommendations, and a free starter book for new subscribers.",
      "I built the whole theme from scratch using Divi, the visual builder, so the design matches the brand without being locked into a marketplace template. Each podcast episode has its own page with the player, show notes, and guest details, the blog runs the long-form articles, and a Recommends section curates the products Faraz stands behind.",
      "For podcast publishing I plugged in a dedicated podcasting plugin so episodes feed cleanly into Apple Podcasts and Spotify. A few performance and SEO plugins handle the rest, keeping the site fast and discoverable.",
    ],
    demoTone: "web",
    image: "uploads/project-two.webp",
    tallImage: true,
    testimonialName: "Faraz Khan",
    testimonialRole: "Host · Anti-Aging Hacks",
    vimeoId: "522298530",
    thumbImage: "uploads/testimonial-two.webp",
    stack: ["WordPress", "Divi Theme", "Divi Theme Builder", "Seriously Simple Podcasting", "Mailchimp", "WP Rocket", "Yoast SEO"],
    liveHref: "https://antiaginghacks.net/",
    liveLabel: "Visit live website",
    layout: {
      left: ["video", "description", "stack"],
      right: ["image", "cta"],
    },
  },
  {
    id: "kriyakarak",
    kind: "mobile",
    title: "KriyaKarak — a React Native app for creative professionals (iOS & Android)",
    description: [
      "KriyaKarak is a mobile platform for creative professionals in Bangladesh: artists, designers, photographers, musicians, and studios, built around the tagline \"Dream. Create. Succeed.\" Each user gets a customisable portfolio, a branded URL, a service menu, and a studio showcase to put their work in front of clients.",
      "The booking side runs the day-to-day: a flexible online calendar with tentative reservations, cancellation policies, sub-task management for jobs, and QR code scanning for on-site reservation check-in. Client management, content publishing, and social media integrations sit alongside it so a creative can run their whole practice from one app.",
      "I built it end-to-end in React Native with a shared codebase across iOS and Android. Redux Toolkit holds the predictable state and Redux Saga orchestrates the side effects: auth flow, calendar sync, booking workflows, and image upload queues. The same release branch ships to both the App Store and Google Play.",
    ],
    images: [
      "uploads/kriyakarak/kriyakarak-1.webp",
      "uploads/kriyakarak/kriyakarak-2.webp",
      "uploads/kriyakarak/kriyakarak-3.webp",
      "uploads/kriyakarak/kriyakarak-4.webp",
      "uploads/kriyakarak/kriyakarak-5.webp",
      "uploads/kriyakarak/kriyakarak-6.webp",
    ],
    stack: ["React Native", "Redux", "Redux Toolkit", "Redux Saga", "React Navigation", "TypeScript", "Reanimated", "Firebase"],
    appStoreHref: "https://apps.apple.com/us/app/kriyakarak/id6504998861",
    playStoreHref: "https://play.google.com/store/apps/details?id=com.kriyakarak.sp",
  },
  {
    id: "skytrip",
    kind: "mobile",
    flipped: true,
    title: "Skytrip — a React Native flight-booking app (iOS & Android)",
    description: [
      "Skytrip is a flight-booking app for travellers in Bangladesh and beyond, built with Bdtask Limited. One-way and round-trip search, both domestic and international routes, with results pulled from top-rated airlines and surfaced in seconds. Pick a date, pick a carrier, book the seat, that's the whole journey.",
      "The app handles the messy parts of travel: airline-by-airline fare comparison, traveller and passport details, fare rules, payment, and ticket delivery, all from a single shared codebase across iOS and Android.",
      "I built the mobile front-end in React Native. Redux Toolkit holds the search and booking state, Redux Saga orchestrates the asynchronous flows: airline search fan-out, payment callbacks, booking confirmations, and ticket sync. The same release branch ships to both the App Store and Google Play.",
    ],
    images: [
      "uploads/skytrip/skytrip-1.webp",
      "uploads/skytrip/skytrip-2.webp",
      "uploads/skytrip/skytrip-3.webp",
      "uploads/skytrip/skytrip-4.webp",
      "uploads/skytrip/skytrip-5.webp",
      "uploads/skytrip/skytrip-6.webp",
    ],
    stack: ["React Native", "Redux", "Redux Toolkit", "Redux Saga", "React Navigation", "TypeScript", "Reanimated", "Firebase"],
    appStoreHref: "https://apps.apple.com/us/app/skytrip-app/id1627468388",
    playStoreHref: "https://play.google.com/store/apps/details?id=com.skytrip",
  },
  {
    id: "icprint",
    title: "Online printing platform on ASP.NET",
    description: [
      "IC Print is one of Australia's leading online printing companies, with a catalogue that runs the day-to-day print stack: flyers, business cards, posters, stickers, and the rest. Customers can browse the catalogue, log in, design their own artwork in the browser, and place an order in a single flow.",
      "The platform runs on ASP.NET MVC at the back, with a dedicated ASP.NET developer owning the server side. I joined as the front-end engineer and own everything the customer sees: the storefront, the product configurator, the in-browser designer, the cart and checkout, and the account area where orders, reorders, and artwork files live.",
      "Day-to-day that means writing the Razor views and partials on top of the ASP.NET controllers, building the SCSS design system, and shipping the JavaScript that powers the interactive design canvas: live previews, price recalculation, file uploads, and validation against print-ready specs.",
    ],
    demoTone: "web",
    image: "uploads/icprint.webp",
    imageAspect: "555 / 716",
    testimonialName: "Luke",
    testimonialRole: "IC Print · Australia",
    vimeoId: "218331494",
    thumbImage: "uploads/testimonial-icprint.webp",
    stack: ["ASP.NET MVC", "Razor Views", "HTML", "SCSS", "JavaScript", "jQuery", "Bootstrap", "Canvas / SVG"],
    liveHref: "https://icprint.com.au/",
    liveLabel: "Visit live website",
    layout: {
      left: ["video", "description", "stack"],
      right: ["image", "cta"],
    },
  },
];

const DemoFrame = ({ tone, label, image, tall, aspect }) => {
  if (image) {
    const style = aspect ? { aspectRatio: aspect } : undefined;
    return (
      <div className={`proj-demo proj-demo-image${tall ? " is-tall" : ""}`} style={style} aria-label={`${label || "Project"} screenshot`}>
        <img src={image} alt={label || ""} loading="lazy" />
      </div>
    );
  }
  if (tone === "mobile") {
    return (
      <div className="proj-demo proj-demo-mobile" aria-label={`${label} screenshot placeholder`}>
        <div className="proj-demo-mobile-inner">
          <div className="pdm-phone">
            <div className="pdm-screen">
              <div className="pdm-notch" />
              <div className="pdm-row pdm-title" />
              <div className="pdm-row" style={{ width: "70%" }} />
              <div className="pdm-grid">
                <span /><span /><span /><span />
              </div>
              <div className="pdm-row" style={{ width: "55%", marginTop: "auto" }} />
              <div className="pdm-row" style={{ width: "85%" }} />
            </div>
          </div>
        </div>
        <span className="proj-demo-tag">{label}</span>
      </div>
    );
  }
  if (tone === "fintech") {
    return (
      <div className="proj-demo proj-demo-web fintech" aria-label={`${label} screenshot placeholder`}>
        <div className="proj-demo-bar">
          <span className="dots"><i /><i /><i /></span>
          <span className="proj-demo-url">{label}</span>
        </div>
        <div className="proj-demo-body">
          <div className="pdw-side">
            <i className="pdw-step done" />
            <i className="pdw-step done" />
            <i className="pdw-step active" />
            <i className="pdw-step" />
          </div>
          <div className="pdw-main">
            <div className="pdw-line t" />
            <div className="pdw-line" style={{ width: "60%" }} />
            <div className="pdw-card">
              <span />
              <span style={{ width: "40%" }} />
            </div>
            <div className="pdw-card alt">
              <span />
              <span style={{ width: "60%" }} />
            </div>
            <div className="pdw-pay">Pay $248.00</div>
          </div>
        </div>
      </div>
    );
  }
  // default web (dashboard)
  return (
    <div className="proj-demo proj-demo-web" aria-label={`${label} screenshot placeholder`}>
      <div className="proj-demo-bar">
        <span className="dots"><i /><i /><i /></span>
        <span className="proj-demo-url">{label}</span>
      </div>
      <div className="proj-demo-body dashboard">
        <div className="pdw-side wide">
          <i className="t" />
          <i /><i /><i /><i />
        </div>
        <div className="pdw-main">
          <div className="pdw-line t" />
          <div className="pdw-stats">
            <div><span className="lbl">MRR</span><span className="num">$48k</span></div>
            <div><span className="lbl">Users</span><span className="num">2.1k</span></div>
            <div><span className="lbl">Health</span><span className="num">98%</span></div>
          </div>
          <div className="pdw-chart">
            <svg viewBox="0 0 240 70" preserveAspectRatio="none">
              <polyline
                points="0,52 20,46 40,50 60,32 80,38 100,22 120,28 140,14 160,20 180,8 200,16 220,6 240,10"
                fill="none" stroke="currentColor" strokeWidth="1.6"
                strokeLinejoin="round" strokeLinecap="round"
              />
            </svg>
          </div>
        </div>
      </div>
    </div>
  );
};

const VideoThumb = ({ id, name, role, vimeoId, thumbImage }) => (
  <a
    className={`proj-video-thumb${thumbImage ? " has-image" : ""}`}
    href={`https://player.vimeo.com/video/${vimeoId}?autoplay=1&title=0&byline=0&portrait=0&dnt=1`}
    data-glightbox={`type: video; width: 1080; height: 608;`}
    data-gallery={`testimonial-${id}`}
    aria-label={`Play testimonial from ${name}`}
  >
    {thumbImage
      ? <img className="pvt-image" src={thumbImage} alt="" loading="lazy" />
      : <span className="pvt-bg" />}
    <span className="pvt-grain" />
    {!thumbImage && (
      <span className="pvt-portrait">{name.split(" ").map(s => s[0]).slice(0, 2).join("")}</span>
    )}
    <span className="pvt-meta">
      <span className="pvt-name">{name}</span>
      {role && <span className="pvt-role">{role}</span>}
    </span>
    <span className="pvt-play" aria-hidden="true">
      <span className="pvt-play-tri" />
    </span>
  </a>
);

const SLOTS = {
  image: (p) => p.image
    ? <DemoFrame key="image" tone={p.demoTone} label={p.demoLabel} image={p.image} tall={p.tallImage} aspect={p.imageAspect} />
    : <DemoFrame key="image" tone={p.demoTone} label={p.demoLabel} />,
  description: (p) => (
    <div key="description" className="proj-col-text">
      {Array.isArray(p.description)
        ? p.description.map((para, i) => <p key={i} className="proj-row-desc">{para}</p>)
        : <p className="proj-row-desc">{p.description}</p>}
    </div>
  ),
  video: (p) => (
    <VideoThumb
      key="video"
      id={p.id}
      name={p.testimonialName}
      role={p.testimonialRole}
      vimeoId={p.vimeoId}
      thumbImage={p.thumbImage}
    />
  ),
  stack: (p) => p.stack && p.stack.length > 0 ? (
    <div key="stack" className="proj-stack-block">
      <h4 className="proj-stack-heading">Tech stack</h4>
      <div className="proj-row-stack" aria-label="Tech stack">
        {p.stack.map((s) => <span key={s} className="chip">{s}</span>)}
      </div>
    </div>
  ) : null,
  cta: (p) => p.liveHref ? (
    <Btn key="cta" kind="primary" href={p.liveHref} arrow className="proj-live-btn">
      {p.liveLabel || "Visit live website"}
    </Btn>
  ) : null,
};

const DEFAULT_LAYOUT = {
  left: ["image", "description", "stack"],
  right: ["video", "cta"],
};

const AppleIcon = () => (
  <svg className="store-btn-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
    <path fill="currentColor" d="M16.365 1.43c0 1.14-.43 2.21-1.16 3.01-.79.88-2.07 1.55-3.13 1.47-.13-1.1.4-2.25 1.13-3.04.81-.88 2.18-1.54 3.16-1.44zM20.5 17.27c-.55 1.27-.81 1.84-1.52 2.96-.98 1.55-2.36 3.48-4.07 3.5-1.52.02-1.91-.99-3.97-.98-2.06.01-2.49 1-4.01.98-1.71-.02-3.02-1.76-4-3.31-2.74-4.34-3.03-9.43-1.34-12.14C2.78 6.32 4.66 5.18 6.42 5.18c1.79 0 2.92.99 4.4.99 1.44 0 2.31-.99 4.39-.99 1.56 0 3.22.85 4.39 2.31-3.86 2.12-3.23 7.65.9 9.78z"/>
  </svg>
);

const PlayIcon = () => (
  <svg className="store-btn-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
    <path fill="currentColor" d="M3.6 1.6c-.4.3-.6.8-.6 1.4v18.3c0 .6.2 1.1.7 1.4l10-10L3.6 1.6zm10.7 8.9 3.3-1.9c1-.6 1-2 0-2.6L4.7 1.4l9.6 9.1zm0 3 9-5.2c1-.6 1-2 0-2.6l-3.3-1.9-5.7 5.7 0 4zm-1 1L3.6 24c.3.2.7.2 1.1 0l12.9-7.4-4.3-2.1z"/>
  </svg>
);

const AppStoreBadge = ({ href }) => (
  <a className="btn btn-primary store-btn" href={href} target="_blank" rel="noreferrer noopener" aria-label="Download on the App Store">
    <AppleIcon />
    <span>App Store</span>
    <span className="btn-arrow" aria-hidden="true">↗</span>
  </a>
);

const PlayStoreBadge = ({ href }) => (
  <a className="btn btn-primary store-btn" href={href} target="_blank" rel="noreferrer noopener" aria-label="Get it on Google Play">
    <PlayIcon />
    <span>Google Play</span>
    <span className="btn-arrow" aria-hidden="true">↗</span>
  </a>
);

const MobileAppRow = ({ project }) => {
  const shots = (
    <div className="proj-mobile-shots">
      {project.images.map((src, i) => (
        <div key={src} className="proj-mobile-shot">
          <img src={src} alt={`${project.title} screen ${i + 1}`} loading="lazy" />
        </div>
      ))}
    </div>
  );
  const aside = (
    <div className="proj-mobile-aside">
      <div className="proj-col-text">
        {project.description.map((para, i) => (
          <p key={i} className="proj-row-desc">{para}</p>
        ))}
      </div>
      {project.stack && project.stack.length > 0 && (
        <div className="proj-stack-block">
          <h4 className="proj-stack-heading">Tech stack</h4>
          <div className="proj-row-stack" aria-label="Tech stack">
            {project.stack.map((s) => <span key={s} className="chip">{s}</span>)}
          </div>
        </div>
      )}
      <div className="proj-store-row">
        {project.appStoreHref && <AppStoreBadge href={project.appStoreHref} />}
        {project.playStoreHref && <PlayStoreBadge href={project.playStoreHref} />}
      </div>
    </div>
  );
  return (
    <article className="proj-row proj-row-mobile-app" data-screen-label={`07 Projects — ${project.id}`}>
      <header className="proj-row-header">
        <h3 className="proj-row-title">{project.title}</h3>
      </header>
      <div className={`proj-row-mobile-grid${project.flipped ? " is-flipped" : ""}`}>
        {project.flipped ? <>{aside}{shots}</> : <>{shots}{aside}</>}
      </div>
    </article>
  );
};

const ProjectRow = ({ project }) => {
  if (project.kind === "mobile") return <MobileAppRow project={project} />;
  const layout = project.layout || DEFAULT_LAYOUT;
  return (
    <article className="proj-row" data-screen-label={`07 Projects — ${project.id}`}>
      <header className="proj-row-header">
        {project.eyebrow && !project.image && <Eyebrow>{project.eyebrow}</Eyebrow>}
        <h3 className="proj-row-title">{project.title}</h3>
      </header>
      <div className="proj-row-grid">
        <div className="proj-col proj-col-left">
          {layout.left.map(slot => SLOTS[slot] && SLOTS[slot](project))}
        </div>
        <div className="proj-col proj-col-right">
          {layout.right.map(slot => SLOTS[slot] && SLOTS[slot](project))}
        </div>
      </div>
    </article>
  );
};

const Projects = () => {
  React.useEffect(() => {
    if (!window.GLightbox) return;
    const lb = window.GLightbox({
      selector: ".projects-section .proj-video-thumb",
      touchNavigation: false,
      keyboardNavigation: false,
      loop: false,
      openEffect: "fade",
      closeEffect: "fade",
      videosWidth: "min(1080px, 92vw)",
    });
    return () => lb.destroy();
  }, []);
  return (
    <section className="shell section projects-section" id="projects" data-screen-label="07 Projects">
      <div className="projects-intro">
        <h2 className="h2">
          A few favourites from the catalogue.
        </h2>
        <p className="projects-lead">
          Shipped end to end, still in the wild, still used by real people. Hit play on any of the videos and the clients tell it better than I can.
        </p>
      </div>

      <div className="projects-rows">
        {PROJECTS.map((p) => (
          <ProjectRow key={p.id} project={p} />
        ))}
      </div>
    </section>
  );
};

// ───── Client feedback (Upwork) — placeholders, swap with real reviews ─────

const FEEDBACK = [
  {
    quote: "Rasel is a properly excellent senior frontend developer. Deep technical skill, great eye for detail, and the kind of developer who actually cares whether the end result looks and feels right — not just whether the code compiles. Communication is clear, delivery is fast, and he handles complex UI work with confidence. He's also not afraid of new technology, and dedicated to constantly learning. Proudly one of my best hires, and one of those rare hires you immediately want to keep working with. Highly, highly recommended.",
    project: "React Native Mobile App (iOS · Android)",
  },
  {
    quote: "He did a great job following directions and asking for clarity whenever questions arose. He is very knowledgeable with WordPress and was a big help to us.",
    project: "WordPress website",
  },
  {
    quote: "He did a great job of redesigning our site and making all of the changes I asked for. He gave me some great ideas for things that I didn't even know I wanted. I will be hiring him again.",
    project: "Redesign website",
  },
  {
    quote: "Rashel did an amazing job!! Communication was perfect and work was solid. Will work with again!",
    project: "React Native Mobile App (iOS · Android)",
  },
  {
    quote: "Very knowledgeable freelancer. Knows his stuff! Will hire again.",
    project: "VPS server configuration for high-end visitor",
  },
  {
    quote: "As always great working with Rashel. Great freelancer who delivers as promised and ahead of schedule.",
    project: "WordPress website",
  },
  {
    quote: "It was a pleasure to work with Rasel. His communication is excellent. He regularly followed up and always asked questions to clarify anything that was not clear. His WordPress development skills are top notch — he was able to build a great custom WordPress theme from a vague brief and very rough wireframes. Next time I have need of a WordPress theme Rasel will be my first choice.",
    project: "WordPress website",
  },
  {
    quote: "As always, I'm extremely satisfied in working with Rashel. His promptness, attention to detail, quality and experience is invaluable. I will definitely hire him again.",
    project: "WordPress website",
  },
  {
    quote: "Rashel was easy to work with. He got to work immediately, was flexible when I provided feedback, and gave recommendations when I needed them. I recommend his work.",
    project: "Email template design and code for Mailchimp",
  },
  {
    quote: "Rasel has been great to work with. He provided details of this task steps, created video information. I'll definitely work with him again if I have a similar task.",
    project: "Website update and bug fixes",
  },
];

const QuoteMark = () => (
  <svg className="feedback-mark" viewBox="0 0 32 24" width="28" height="22" aria-hidden="true">
    <path fill="currentColor" d="M0 24V13.6C0 9.2 1 5.7 3 3.1 5 0.5 7.9-.5 11.7.4l-1 4.5c-2 .1-3.5.9-4.6 2.3-1.1 1.4-1.5 3.1-1.4 5.1H11V24H0zm18 0V13.6c0-4.4 1-7.9 3-10.5C23 .5 25.9-.5 29.7.4l-1 4.5c-2 .1-3.5.9-4.6 2.3-1.1 1.4-1.5 3.1-1.4 5.1H29V24H18z"/>
  </svg>
);

const FEEDBACK_VIDEOS = {
  jennifer: { id: "jennifer", name: "Jennifer Irish", role: "WordPress project",            vimeoId: "218331229", thumb: "uploads/testimonial-jennifer.webp", orientation: "vertical" },
  siraj:    { id: "siraj",    name: "Siraj Sayed",    role: "React Native mobile app",      vimeoId: "806017607", thumb: "uploads/testimonial-siraj.webp",    orientation: "horizontal" },
  erik:     { id: "erik",     name: "Erik Lovell",    role: "WordPress website",            vimeoId: "218749208", thumb: "uploads/testimonial-erik.webp",     orientation: "vertical" },
};

const Feedback = () => {
  React.useEffect(() => {
    if (!window.GLightbox) return;
    // Horizontal video — Plyr handles it fine in 16:9.
    const lbH = window.GLightbox({
      selector: ".feedback-section .feedback-video.is-horizontal",
      touchNavigation: false,
      keyboardNavigation: false,
      loop: false,
      openEffect: "fade",
      closeEffect: "fade",
      plyr: { config: { ratio: "16:9", muted: false, autoplay: true } },
    });

    // Vertical videos — open a synchronous custom popup so Vimeo's player
    // gets the user gesture token (lets it autoplay with sound) AND we
    // control the iframe sizing directly (Plyr would force 16:9).
    const openVerticalPopup = (v) => {
      const overlay = document.createElement("div");
      overlay.className = "vplayer-overlay";
      overlay.innerHTML = `
        <button class="vplayer-close" aria-label="Close">&times;</button>
        <div class="vplayer-frame">
          <iframe
            src="https://player.vimeo.com/video/${v.vimeoId}?autoplay=1&title=0&byline=0&portrait=0&dnt=1"
            allow="autoplay; fullscreen; picture-in-picture"
            allowfullscreen
            frameborder="0"
          ></iframe>
        </div>
      `;
      const close = () => {
        document.body.classList.remove("vplayer-open");
        overlay.remove();
      };
      overlay.addEventListener("click", (ev) => {
        if (ev.target === overlay || ev.target.classList.contains("vplayer-close")) {
          close();
        }
      });
      document.addEventListener("keydown", function esc(ev) {
        if (ev.key === "Escape") {
          document.removeEventListener("keydown", esc);
          close();
        }
      });
      document.body.appendChild(overlay);
      document.body.classList.add("vplayer-open");
    };
    const onVerticalClick = (e) => {
      e.preventDefault();
      e.stopPropagation();
      const link = e.currentTarget;
      const id = link.getAttribute("data-gallery").replace("feedback-", "");
      const v = FEEDBACK_VIDEOS[id];
      if (v) openVerticalPopup(v);
    };
    const verticalLinks = document.querySelectorAll(".feedback-section .feedback-video.is-vertical");
    verticalLinks.forEach((l) => l.addEventListener("click", onVerticalClick));
    return () => {
      verticalLinks.forEach((l) => l.removeEventListener("click", onVerticalClick));
      lbH.destroy();
    };
  }, []);
  const VideoCard = ({ v }) => (
    <a
      className={`feedback-card feedback-video is-${v.orientation} proj-video-thumb has-image`}
      href={`https://player.vimeo.com/video/${v.vimeoId}?autoplay=1&title=0&byline=0&portrait=0&dnt=1`}
      data-glightbox={v.orientation === "horizontal"
        ? `type: video; width: 1080px; height: 608px;`
        : `type: video; width: 440px; height: 782px;`}
      data-gallery={`feedback-${v.id}`}
      aria-label={`Play video testimonial from ${v.name}`}
    >
      <img className="pvt-image" src={v.thumb} alt="" loading="lazy" />
      <span className="pvt-grain" />
      <span className="pvt-meta">
        <span className="pvt-name">{v.name}</span>
        <span className="pvt-role">{v.role}</span>
      </span>
      <span className="pvt-play" aria-hidden="true">
        <span className="pvt-play-tri" />
      </span>
    </a>
  );
  const TextCard = ({ f, k }) => (
    <figure key={k} className="feedback-card">
      <QuoteMark />
      <blockquote className="feedback-quote">{f.quote}</blockquote>
      <figcaption className="feedback-meta">
        <span className="feedback-project">{f.project}</span>
      </figcaption>
    </figure>
  );
  // Sequence: 9 text + 3 videos = 12 cells in a 3-col grid (5 rows).
  // Vertical videos span 2 rows (Jennifer top-right, Erik bottom-right).
  // Horizontal video spans 2 cols (Siraj middle row, cols 2-3).
  const t = FEEDBACK;
  return (
    <section className="shell section feedback-section" data-screen-label="07 Feedback">
      <div className="feedback-rows">
        {/* Top band — three balanced columns */}
        <div className="feedback-row">
          <div className="feedback-col">
            <TextCard k="t1" f={t[1]} />
            <TextCard k="t3" f={t[3]} />
            <TextCard k="t5" f={t[5]} />
          </div>
          <div className="feedback-col">
            <TextCard k="t4" f={t[4]} />
            <TextCard k="t0" f={t[0]} />
          </div>
          <div className="feedback-col">
            <VideoCard v={FEEDBACK_VIDEOS.jennifer} />
          </div>
        </div>
        {/* Middle band — Siraj sits next to a feedback card so the video
            isn't comically tall. The placeholder quote is editable below. */}
        <div className="feedback-row feedback-row-mid">
          <div className="feedback-col">
            <TextCard
              k="placeholder-mid"
              f={{
                quote: "Working with Rasel feels like adding a senior teammate to your project, not just a freelancer. He gets the brief, asks the right questions, and ships polished work on time — every time.",
                project: "Frontend collaboration · Placeholder",
              }}
            />
          </div>
          <VideoCard v={FEEDBACK_VIDEOS.siraj} />
        </div>
        {/* Bottom band — three balanced columns */}
        <div className="feedback-row">
          <div className="feedback-col">
            <VideoCard v={FEEDBACK_VIDEOS.erik} />
          </div>
          <div className="feedback-col">
            <TextCard k="t6" f={t[6]} />
            <TextCard k="t8" f={t[8]} />
          </div>
          <div className="feedback-col">
            <TextCard k="t2" f={t[2]} />
            <TextCard k="t7" f={t[7]} />
            <TextCard k="t9" f={t[9]} />
          </div>
        </div>
      </div>
    </section>
  );
};

Object.assign(window, {
  Nav, Hero, UpworkStats, WorkflowMorph,
  PrincipleStatement, Portfolio, NDA, BigTestimonial, Footer,
  Projects, Feedback,
});
