/* global React, gsap, ScrollTrigger, prefersReduced, FadeIn, SectionLabel, ContactButton, GhostButton, MailIcon, GithubIcon, LinkedinIcon */
const { useRef: useRefS, useLayoutEffect: useLayoutEffectS } = React;

const isMobile = () => typeof window !== 'undefined' && window.matchMedia('(max-width: 767px)').matches;

/* --------------------------------- Navbar --------------------------------- */
function Navbar() {
  const [open, setOpen] = React.useState(false);

  const links = [
    ['À propos', '#about'],
    ['Stack', '#stack'],
    ['Projets', '#projects'],
    ['Contact', '#contact'],
  ];

  React.useEffect(() => {
    document.body.style.overflow = open ? 'hidden' : '';
    return () => { document.body.style.overflow = ''; };
  }, [open]);

  const teleportTo = (e, href) => {
    e.preventDefault();
    const overlay = document.getElementById('teleport-overlay');
    const el = href === '#top' ? null : document.querySelector(href);

    let targetY = 0;
    if (el) {
      const st = ScrollTrigger.getAll().find(t => t.trigger === el);
      targetY = st ? st.start : el.getBoundingClientRect().top + window.scrollY;
    }

    if (prefersReduced || !overlay) { window.scrollTo(0, targetY); return; }

    gsap.killTweensOf(overlay);
    gsap.timeline()
      .set(overlay, { pointerEvents: 'auto' })
      .to(overlay, { opacity: 1, duration: 0.22, ease: 'power2.in' })
      .call(() => { window.scrollTo(0, targetY); })
      .to(overlay, { opacity: 0, duration: 0.5, ease: 'power2.out', delay: 0.05 })
      .set(overlay, { pointerEvents: 'none' });
  };

  const handleLink = (e, href) => {
    setOpen(false);
    teleportTo(e, href);
  };

  return (
    <>
      {/* Barre de navigation */}
      <nav className="fixed inset-x-0 top-0 z-50 flex items-center justify-between px-6 nav-safe-top md:px-10 mix-blend-difference">
        <a href="/admin"
           className="display-heading select-none" style={{ fontSize: 'clamp(1.1rem, 2vw, 1.5rem)' }}>MDC</a>

        {/* Liens desktop */}
        <div className="hidden md:flex items-center gap-9">
          {links.map(([label, href]) => (
            <a key={href} href={href} onClick={(e) => teleportTo(e, href)}
               className="text-lg font-medium uppercase tracking-[0.16em] text-bone transition-opacity duration-200 hover:opacity-70">
              {label}
            </a>
          ))}
        </div>

        {/* Bouton hamburger (mobile) */}
        <button
          className="md:hidden flex flex-col justify-center items-end gap-[5px] py-1 px-1"
          onClick={() => setOpen(o => !o)}
          aria-label={open ? 'Fermer le menu' : 'Ouvrir le menu'}
        >
          <span className={`block h-[2px] w-6 bg-bone rounded-full transition-all duration-300 origin-center ${open ? 'rotate-45 translate-y-[7px]' : ''}`} />
          <span className={`block h-[2px] bg-bone rounded-full transition-all duration-300 ${open ? 'w-0 opacity-0' : 'w-4'}`} />
          <span className={`block h-[2px] w-6 bg-bone rounded-full transition-all duration-300 origin-center ${open ? '-rotate-45 -translate-y-[7px]' : ''}`} />
        </button>
      </nav>

      {/* Overlay menu mobile */}
      <div
        className="fixed inset-0 z-40 bg-night flex flex-col items-center justify-center gap-10 md:hidden transition-opacity duration-300"
        style={{ opacity: open ? 1 : 0, pointerEvents: open ? 'auto' : 'none' }}
      >
        {links.map(([label, href], i) => (
          <a
            key={href}
            href={href}
            onClick={(e) => handleLink(e, href)}
            className="display-heading uppercase tracking-[0.1em] transition-all duration-300"
            style={{
              fontSize: 'clamp(2.2rem, 11vw, 3.5rem)',
              transitionDelay: open ? `${i * 60 + 40}ms` : '0ms',
              transform: open ? 'translateY(0)' : 'translateY(20px)',
              opacity: open ? 1 : 0,
            }}
          >
            {label}
          </a>
        ))}
      </div>
    </>
  );
}

/* --------------------------------- About ---------------------------------- */
const _C = window.__SITE_DATA__ || {};
const ABOUT_TITLE = _C.about?.title || 'Construire avec exigence.';
const ABOUT_BODY  = _C.about?.body  || "Développeur full stack depuis plus de cinq ans, j'accompagne entreprises et porteurs de projet du front au back jusqu'au déploiement. J'aime particulièrement intégrer l'IA là où elle crée une vraie valeur, et livrer des produits robustes, rapides et agréables à utiliser. Travaillons ensemble sur quelque chose qui compte.";

function About() {
  const root = useRefS(null);
  useLayoutEffectS(() => {
    const el = root.current;
    if (!el) return;
    const q = (s) => el.querySelector(s);
    const words = el.querySelectorAll('.about-word');
    const chars = el.querySelectorAll('.about-char');

    if (prefersReduced) {
      gsap.set([...words, ...chars], { opacity: 1, y: 0 });
      gsap.set(q('.about-accent'), { scaleX: 1 });
      return;
    }
    const ctx = gsap.context(() => {
      gsap.set(words, { opacity: 0, y: 48 });
      gsap.set(chars, { opacity: 0.16 });
      gsap.set(q('.about-accent'), { scaleX: 0 });

      const tl = gsap.timeline({
        scrollTrigger: { trigger: el, start: 'top top', end: '+=1500', pin: true, scrub: 1, anticipatePin: 1, refreshPriority: 40 },
      });
      tl.to(words, { opacity: 1, y: 0, stagger: 0.16, duration: 1, ease: 'power3.out' }, 0);
      tl.to(q('.about-accent'), { scaleX: 1, duration: 1.1, ease: 'power2.out' }, 0.35);
      tl.to(chars, { opacity: 1, stagger: 0.35, ease: 'none' }, 1.0);
    }, el);
    return () => ctx.revert();
  }, []);

  return (
    <section id="about" ref={root} className="relative flex min-h-screen items-center bg-night px-5 py-28 sm:px-8 md:px-10">
      <div className="mx-auto flex w-full max-w-4xl flex-col">
        <SectionLabel>À propos</SectionLabel>
        <h2 className="display-heading mt-6 flex flex-wrap gap-x-[0.25em]" style={{ fontSize: 'clamp(2.5rem, 8vw, 7rem)' }}>
          {ABOUT_TITLE.split(' ').map((w, i) => (
            <span key={i} className="about-word inline-block">{w}</span>
          ))}
        </h2>
        <div className="about-accent mt-7 h-px w-40 origin-left bg-sage" style={{ height: 2 }} />
        <p className="mt-10 font-medium leading-relaxed text-bone" style={{ maxWidth: 640, fontSize: 'clamp(1.1rem, 2vw, 1.6rem)' }}>
          {ABOUT_BODY.split(' ').map((w, wi) => (
            <span key={wi} className="inline-block whitespace-nowrap">
              {w.split('').map((c, ci) => <span key={ci} className="about-char inline-block">{c}</span>)}
              {wi < ABOUT_BODY.split(' ').length - 1 && <span className="about-char inline-block">&nbsp;</span>}
            </span>
          ))}
        </p>
        <div className="mt-12"><ContactButton /></div>
      </div>
    </section>
  );
}

/* ------------------------------- TechStack -------------------------------- */
const STACK = _C.stack?.columns
  ? _C.stack.columns.map(col => [col.title, col.items])
  : [
      ['Frontend',        ['React', 'Next.js', 'TypeScript', 'Tailwind CSS', 'Vite']],
      ['Backend',         ['Node.js', 'Python', 'Symfony', 'PostgreSQL', 'Prisma']],
      ['DevOps & Outils', ['Docker', 'CI/CD', 'Git', 'API REST & GraphQL', 'Auth / sécurité']],
    ];
const MARQUEE = _C.stack?.marquee || ['React', 'TypeScript', 'Node.js', 'Next.js', 'Tailwind', 'GSAP', 'PostgreSQL', 'Prisma', 'Docker', 'GraphQL', 'Symfony', 'Python', 'Vite'];

function TechStack() {
  const root = useRefS(null);
  const track = useRefS(null);
  const marquee = useRefS(null);

  useLayoutEffectS(() => {
    const el = root.current;
    if (!el) return;
    // Marquee (all breakpoints) — its own context.
    let marqueeCtx;
    if (!prefersReduced && marquee.current) {
      marqueeCtx = gsap.context(() => {
        gsap.to(marquee.current, { xPercent: -50, duration: 30, ease: 'none', repeat: -1 });
      }, root);
    }
    if (prefersReduced) return () => { if (marqueeCtx) marqueeCtx.revert(); };

    // ONE locked screen: scroll the 3 columns horizontally, then the columns themselves
    // rush to the centre, shrink and dissolve — fusing into the "Services" title.
    // All inside a single pinned timeline (no scroll release between the two).
    const teardown = responsiveDesktop(() => {
      const ctx = gsap.context(() => {
        const tr = track.current;
        if (!tr) return;
        const heading = el.querySelector('.stack-heading');
        const marq = el.querySelector('.stack-marquee');

        gsap.set(tr, { transformOrigin: '50% 50%', x: 0, scale: 1, opacity: 1 });

        const amount = () => Math.max(0, tr.scrollWidth - window.innerWidth + 40);
        // x that places the track's geometric centre at the viewport centre
        const toCentre = () => window.innerWidth / 2 - (tr.offsetLeft + tr.scrollWidth / 2);

        const tl = gsap.timeline({
          scrollTrigger: {
            trigger: el, start: 'top top',
            end: () => '+=' + (amount() + 1000),
            pin: true, scrub: 1, anticipatePin: 1, invalidateOnRefresh: true, refreshPriority: 30,
          },
        });
        // Phase 1 — scroll horizontally through Frontend / Backend / DevOps
        tl.to(tr, { x: () => -amount(), ease: 'none', duration: 1.0 }, 0);
        // Phase 2 — heading + marquee fade, the 3 columns converge to centre, shrink & dissolve.
        // The "Services" title is NOT shown here — the columns collapse to the centre and the
        // Services section's own intro title takes over at the same spot (see Services()).
        tl.to(heading, { opacity: 0, y: -40, duration: 0.3, ease: 'power2.in' }, 1.0);
        tl.to(marq, { opacity: 0, duration: 0.3, ease: 'power2.in' }, 1.0);
        tl.to(tr, { x: toCentre, scale: 0.14, opacity: 0, ease: 'power3.inOut', duration: 0.5 }, 1.0);
      }, el);
      return () => { ctx.revert(); };
    });

    return () => { teardown(); if (marqueeCtx) marqueeCtx.revert(); };
  }, []);

  return (
    <section id="stack" ref={root} className="relative flex min-h-screen flex-col justify-center overflow-hidden bg-night py-20 md:h-screen md:py-0">
      <div className="stack-heading px-5 sm:px-8 md:px-10">
        <div className="mx-auto flex max-w-6xl flex-col items-start">
          <SectionLabel>Stack technique</SectionLabel>
          <h2 className="display-heading mt-5" style={{ fontSize: 'clamp(2rem, 6vw, 5rem)' }}>Stack technique</h2>
        </div>
      </div>

      {/* Horizontal track (desktop) / stacked (mobile) */}
      <div className="mt-10 md:mt-16">
        <div ref={track} className="flex flex-col gap-12 px-5 sm:px-8 md:flex-row md:flex-nowrap md:gap-0 md:px-0 md:pl-10 md:pr-[8vw]">
          {STACK.map(([title, items], ci) => (
            <div key={title} className="shrink-0 md:flex md:w-[68vw] md:items-center md:px-[5vw] lg:w-[54vw]">
              <div className="w-full max-w-2xl">
                <div className="flex items-baseline gap-4">
                  <span className="text-sage font-medium tracking-[0.3em] text-sm md:text-base">{String(ci + 1).padStart(2, '0')}</span>
                  <h3 className="text-sage uppercase tracking-[0.22em] font-medium text-sm md:text-xl">{title}</h3>
                </div>
                <ul className="mt-7 flex flex-col gap-3 border-t border-[rgba(232,237,233,0.12)] pt-7 md:gap-4 md:pt-9">
                  {items.map((it) => (
                    <li key={it} className="font-light text-bone/85 transition-colors duration-300 hover:text-sage"
                        style={{ fontSize: 'clamp(1.15rem, 3vw, 2.4rem)' }}>
                      {it}
                    </li>
                  ))}
                </ul>
              </div>
            </div>
          ))}
        </div>
      </div>

      {/* Decorative marquee */}
      <div className="stack-marquee mt-14 select-none opacity-[0.12] md:absolute md:bottom-8 md:left-0 md:right-0 md:mt-0" aria-hidden="true">
        <div ref={marquee} className="marquee-track">
          {[0, 1].map((dup) => (
            <div key={dup} className="flex shrink-0">
              {MARQUEE.map((m, i) => (
                <span key={i} className="display-heading px-8" style={{ fontSize: 'clamp(2rem, 5vw, 4rem)' }}>
                  {m} <span className="text-sage">/</span>
                </span>
              ))}
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

/* -------------------------------- Services -------------------------------- */
const SERVICES = (_C.services || [
  { name: 'Sites & landing pages',  desc: 'Sites vitrines et landing pages sur-mesure, animés et optimisés, pensés pour convertir et marquer les esprits.' },
  { name: 'Applications web',       desc: "Développement d'applications complètes, du tableau de bord interne à l'outil métier, front et back maîtrisés de bout en bout." },
  { name: 'Produits SaaS',          desc: 'Conception et build de plateformes SaaS : authentification, paiements, base de données, scalabilité et déploiement.' },
  { name: 'Intégration IA',         desc: 'Ajout de fonctionnalités IA pertinentes — assistants, recherche augmentée (RAG), automatisations — branchées proprement sur vos données.' },
  { name: 'Conseil & DevOps',       desc: 'Audit, optimisation des performances, mise en place de CI/CD et accompagnement technique pour fiabiliser vos déploiements.' },
]).map((s, i) => [String(i + 1).padStart(2, '0'), s.name, s.desc]);

function Services() {
  const root = useRefS(null);
  const STATIC = prefersReduced;

  useLayoutEffectS(() => {
    const el = root.current;
    if (!el || STATIC) return; // reduced-motion → static markup, no pin

    const teardown = responsiveDesktop(() => {
      const ctx = gsap.context(() => {
        const drum = el.querySelector('.svc-drum');
        const drumwrap = el.querySelector('.svc-drumwrap');
        const intro = el.querySelector('.svc-intro');
        const tag = el.querySelector('.svc-tag');
        const faces = gsap.utils.toArray(el.querySelectorAll('.svc-face'));
        const ticks = gsap.utils.toArray(el.querySelectorAll('.svc-tick'));
        const fill = el.querySelector('.svc-fill');
        const n = faces.length;
        const step = 360 / n; // 72° between faces

        let r = 400;
        // place each face on a vertical cylinder via a z-shifted rotation pivot
        const measure = () => {
          const H = drum.getBoundingClientRect().height || drum.offsetHeight || 1;
          r = (H / 2) / Math.tan(Math.PI / n);
          faces.forEach((f) => { f.style.transformOrigin = `50% 50% ${(-r).toFixed(1)}px`; });
        };

        const setRot = (rot) => {
          faces.forEach((f, i) => {
            const a = i * step + rot;
            gsap.set(f, { rotateX: a });
            f.style.opacity = Math.max(0.08, Math.cos((a * Math.PI) / 180)).toFixed(3);
          });
          const active = (((Math.round(-rot / step)) % n) + n) % n;
          ticks.forEach((t, i) => gsap.set(t, { opacity: i === active ? 1 : 0.3, x: i === active ? 4 : 0 }));
          if (fill) gsap.set(fill, { scaleY: (-rot) / ((n - 1) * step) });
        };

        measure();
        setRot(0);

        // Intro morph initial states
        gsap.set(intro, { opacity: 1, scale: 1, y: 0, filter: 'blur(0px)', transformOrigin: '50% 50%' });
        gsap.set(tag, { opacity: 0 });
        gsap.set(drumwrap, { opacity: 0, scale: 0.82, transformOrigin: '50% 50%' });

        const proxy = { rot: 0 };
        const tl = gsap.timeline({
          scrollTrigger: {
            trigger: el, start: 'top top', end: '+=4200', pin: true, scrub: 1,
            anticipatePin: 1, refreshPriority: 20, invalidateOnRefresh: true, onRefresh: measure,
          },
        });
        // Phase 0 — the big "Services" title (handed off from the Stack fusion) shrinks and
        // rises into the section tag while the drum assembles into view.
        tl.to(intro, { y: () => -(window.innerHeight / 2 - 46), scale: 0.16, opacity: 0, filter: 'blur(4px)', ease: 'power2.inOut', duration: 0.8 }, 0);
        tl.to(tag, { opacity: 1, ease: 'power2.out', duration: 0.4 }, 0.45);
        tl.to(drumwrap, { opacity: 1, scale: 1, ease: 'power2.out', duration: 0.6 }, 0.3);
        // Phases 1-4 — spin the drum through the 5 services
        tl.to(proxy, { rot: -(n - 1) * step, ease: 'none', duration: 4, onUpdate: () => setRot(proxy.rot) }, 0.95);
      }, el);
      return () => { ctx.revert(); };
    });
    return () => { teardown(); };
  }, []);

  return (
    <section id="services" ref={root} className={'relative bg-night ' + (STATIC ? '' : 'svc-anim md:h-screen md:overflow-hidden')}>
      {/* Persistent section tag (desktop pinned view) */}
      {!STATIC && (
        <div className="svc-tag pointer-events-none absolute inset-x-0 top-8 z-20 hidden justify-center md:flex">
          <SectionLabel center>Services</SectionLabel>
        </div>
      )}

      {/* Intro title — handed off from the Stack→Services fusion, morphs up into the tag */}
      {!STATIC && (
        <div className="svc-intro pointer-events-none absolute inset-0 z-30 hidden items-center justify-center px-5 md:flex">
          <h2 className="display-heading text-center" style={{ fontSize: 'clamp(2.5rem, 9vw, 8rem)' }}>Services</h2>
        </div>
      )}

      {/* Mobile / reduced-motion header */}
      <div className={(STATIC ? '' : 'md:hidden ') + 'flex flex-col items-center px-5 pt-24 pb-10 text-center sm:px-8'}>
        <SectionLabel center>Ce que je fais</SectionLabel>
        <h2 className="display-heading mt-6" style={{ fontSize: 'clamp(2.5rem, 8vw, 7rem)' }}>Services</h2>
      </div>

      {/* Progress rail (desktop pinned view) */}
      {!STATIC && (
        <div className="absolute left-6 top-1/2 z-20 hidden -translate-y-1/2 items-center gap-4 md:flex lg:left-10">
          <div className="relative w-px bg-[rgba(232,237,233,0.15)]" style={{ height: 180 }}>
            <div className="svc-fill absolute inset-x-0 top-0 w-px bg-sage" style={{ height: '100%', transformOrigin: 'top', transform: 'scaleY(0)' }} />
          </div>
          <div className="flex flex-col justify-between" style={{ height: 180 }}>
            {SERVICES.map(([num]) => (
              <span key={num} className="svc-tick font-mono text-xs tracking-[0.3em] text-bone">{num}</span>
            ))}
          </div>
        </div>
      )}

      {/* The rotating drum */}
      <div className="svc-drumwrap relative md:absolute md:inset-0 md:flex md:items-center md:justify-center" style={{ perspective: '1100px' }}>
        <div className="svc-drum relative w-full max-w-4xl md:h-[58vh]">
          {SERVICES.map(([num, name, desc], i) => (
            <div key={num} data-i={i}
                 className="svc-face flex min-h-[72vh] items-center border-b border-[rgba(232,237,233,0.1)] bg-night px-5 sm:px-8 md:min-h-0 md:border-0 md:px-16 lg:px-24">
              <div className="w-full max-w-3xl">
                <span className="font-mono text-sm uppercase tracking-[0.3em] text-sage">{num} / 0{SERVICES.length}</span>
                <h3 className="mt-5 font-medium uppercase tracking-tight text-bone" style={{ fontSize: 'clamp(2rem, 6vw, 4.8rem)' }}>{name}</h3>
                <div className="mt-6 h-[2px] w-16 bg-sage" />
                <p className="mt-6 max-w-2xl font-light leading-relaxed text-bone/70" style={{ fontSize: 'clamp(1rem, 1.9vw, 1.5rem)' }}>{desc}</p>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

/* --------------------------------- Process -------------------------------- */
const PROCESS = (_C.process || [
  { title: 'Cadrage',        desc: 'On clarifie le besoin, les objectifs et le périmètre.' },
  { title: 'Design & archi', desc: 'Maquettes, choix techniques et architecture adaptés au projet.' },
  { title: 'Développement',  desc: 'Build itératif, propre et testé, avec points réguliers.' },
  { title: 'Livraison',      desc: 'Déploiement, optimisation et accompagnement post-lancement.' },
]).map((s, i) => [String(i + 1).padStart(2, '0'), s.title, s.desc]);

function Process() {
  const root = useRefS(null);
  useLayoutEffectS(() => {
    const el = root.current;
    if (!el) return;
    const line = el.querySelector('.proc-line');
    const steps = el.querySelectorAll('.proc-step');

    if (prefersReduced) {
      gsap.set(line, { scaleX: 1 });
      gsap.set(steps, { opacity: 1 });
      return;
    }
    const ctx = gsap.context(() => {
      gsap.set(line, { scaleX: 0 });
      gsap.set(steps, { opacity: 0.28 });

      const tl = gsap.timeline({
        scrollTrigger: { trigger: el, start: 'top top', end: '+=1600', pin: true, scrub: 1, anticipatePin: 1, refreshPriority: 10 },
      });
      tl.to(line, { scaleX: 1, duration: 1.1, ease: 'none' }, 0);
      steps.forEach((s, i) => {
        const t = 0.06 + i * 0.26;
        tl.to(s, { opacity: 1, duration: 0.25, ease: 'power2.out' }, t);
        tl.fromTo(s.querySelector('.proc-num'), { scale: 0.88 }, { scale: 1.08, duration: 0.35, ease: 'back.out(2)' }, t);
      });
    }, el);
    return () => ctx.revert();
  }, []);

  return (
    <section id="process" ref={root} className="relative flex min-h-screen flex-col justify-center rounded-t-[40px] bg-night2 px-5 py-28 sm:px-8 md:rounded-t-[60px] md:px-10">
      <div className="mx-auto w-full max-w-6xl">
        <div className="mb-16 flex flex-col items-center text-center md:mb-24">
          <SectionLabel center>Méthode</SectionLabel>
          <h2 className="display-heading mt-6" style={{ fontSize: 'clamp(2.5rem, 8vw, 7rem)' }}>Façon de bosser</h2>
        </div>

        <div className="relative">
          <div className="absolute left-0 right-0 top-[34px] hidden h-px bg-[rgba(232,237,233,0.12)] md:block" />
          <div className="proc-line absolute left-0 right-0 top-[34px] hidden h-px origin-left bg-sage md:block" />

          <div className="grid grid-cols-1 gap-12 md:grid-cols-4 md:gap-8">
            {PROCESS.map(([num, title, desc]) => (
              <div key={num} className="proc-step relative">
                <div className="mb-6 flex h-[68px] items-center">
                  <span className="proc-num stat-num inline-block" style={{ fontSize: 'clamp(2.2rem, 5vw, 3.6rem)' }}>{num}</span>
                  <span className="ml-3 mt-3 h-2.5 w-2.5 rounded-full bg-sage md:hidden" />
                </div>
                <h3 className="font-medium uppercase tracking-wide text-bone" style={{ fontSize: 'clamp(1.05rem, 2vw, 1.45rem)' }}>{title}</h3>
                <p className="mt-3 max-w-[260px] font-light leading-relaxed text-bone/60" style={{ fontSize: 'clamp(0.85rem, 1.4vw, 1.05rem)' }}>{desc}</p>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}

/* --------------------------------- Projects ------------------------------- */
// Loaded from /api/data before mount (see app.jsx bootstrap). Falls back to
// data.json defaults if the fetch failed (e.g. opening index.html directly).
const PROJECTS = (window.__SITE_DATA__?.projects) || [
  { id: '1', cat: 'Client', name: 'Madfit', desc: 'Plateforme de coaching fitness — programmes, suivi de séances et tableau de bord membre.', url: '#' },
  { id: '2', cat: 'Client', name: 'Groovit', desc: 'Application autour de la musique et des événements — découverte, réservation et profils.', url: '#' },
  { id: '3', cat: 'Client', name: 'OmydooSh', desc: 'Outil métier sur-mesure pour digitaliser un processus interne.', url: '#' },
  { id: '4', cat: 'Perso', name: 'Which FDP', desc: 'Projet personnel ludique — concept original, build full stack de A à Z.', url: '#' },
  { id: '5', cat: 'Client', name: 'Diagnovision', desc: 'Plateforme de diagnostic assistée par la vision — traitement et analyse de données.', url: '#' },
];

function ImgSlot({ label, ratio, mediaClass, tall, src }) {
  return (
    <div className="relative w-full overflow-hidden rounded-[28px] md:rounded-[44px]" style={{ aspectRatio: ratio, height: tall ? '100%' : undefined }}>
      {src
        ? <img src={src} alt={label} className={'pj-media ' + mediaClass + ' object-cover w-full h-full'} />
        : <div className={'pj-media ' + mediaClass} style={{ background: 'repeating-linear-gradient(135deg, #0E1411 0 14px, rgba(124,152,133,0.06) 14px 28px), #0E1411' }} />
      }
      {!src && (
        <div className="absolute inset-0 flex items-center justify-center">
          <span className="rounded-full border border-[rgba(232,237,233,0.18)] bg-night/30 px-4 py-1.5 font-mono text-[0.65rem] uppercase tracking-[0.2em] text-bone/45 backdrop-blur-sm">
            {label}
          </span>
        </div>
      )}
    </div>
  );
}

function Projects() {
  const root = useRefS(null);
  useLayoutEffectS(() => {
    const el = root.current;
    if (!el || prefersReduced) return;
    const ctx = gsap.context(() => {
      el.querySelectorAll('.pj-card').forEach((card) => {
        const st = { trigger: card, start: 'top bottom', end: 'bottom top', scrub: true };
        const big = card.querySelector('.pj-big');
        const s1 = card.querySelector('.pj-s1');
        const s2 = card.querySelector('.pj-s2');
        const num = card.querySelector('.pj-num');
        if (big) gsap.fromTo(big, { yPercent: -9 }, { yPercent: 9, ease: 'none', scrollTrigger: st });
        if (s1) gsap.fromTo(s1, { yPercent: -14 }, { yPercent: 14, ease: 'none', scrollTrigger: st });
        if (s2) gsap.fromTo(s2, { yPercent: 12 }, { yPercent: -12, ease: 'none', scrollTrigger: st });
        if (num) gsap.fromTo(num, { yPercent: -22 }, { yPercent: 22, ease: 'none', scrollTrigger: st });
      });
    }, el);
    return () => ctx.revert();
  }, []);

  const total = PROJECTS.length;
  return (
    <section id="projects" ref={root} className="relative z-10 -mt-10 rounded-t-[40px] bg-night px-4 pb-10 pt-24 sm:px-8 md:-mt-14 md:rounded-t-[60px] md:px-10">
      <div className="mx-auto max-w-6xl">
        <div className="mb-12 flex flex-col items-center text-center md:mb-20">
          <SectionLabel center>Sélection</SectionLabel>
          <h2 className="display-heading mt-6" style={{ fontSize: 'clamp(2.5rem, 8vw, 7rem)' }}>Projets</h2>
        </div>

        <div className="pb-[20vh]">
          {PROJECTS.map(({ id, cat, name, desc, url, images = {} }, index) => {
            const num = String(index + 1).padStart(2, '0');
            const targetScale = 1 - (total - 1 - index) * 0.04;
            return (
              <div key={id} className="pj-card sticky top-24 md:top-32" style={{ paddingTop: index * 26 }}>
                <div className="origin-top rounded-[32px] border-2 border-edge bg-night p-4 shadow-[0_-20px_60px_rgba(0,0,0,0.5)] sm:p-6 md:rounded-[56px] md:p-8" style={{ transform: `scale(${targetScale})` }}>
                  <div className="flex flex-wrap items-start justify-between gap-4 px-2 pb-5 md:px-4 md:pb-7">
                    <div className="flex items-baseline gap-4 md:gap-6">
                      <span className="pj-num display-heading leading-none" style={{ fontSize: 'clamp(2rem, 5vw, 4.5rem)', WebkitTextFillColor: 'rgba(232,237,233,0.22)' }}>{num}</span>
                      <div>
                        <div className="text-sage uppercase tracking-[0.25em] text-[0.65rem] md:text-xs">{cat}</div>
                        <h3 className="mt-1 font-medium tracking-tight text-bone" style={{ fontSize: 'clamp(1.4rem, 3.5vw, 2.6rem)' }}>{name}</h3>
                        <p className="mt-2 max-w-md font-light leading-relaxed text-bone/55 text-sm md:text-base">{desc}</p>
                      </div>
                    </div>
                    <GhostButton label="Voir le projet" href={url || '#'} />
                  </div>

                  <div className="grid grid-cols-1 gap-3 sm:grid-cols-5 md:gap-4">
                    <div className="flex flex-col gap-3 sm:col-span-2 md:gap-4">
                      <ImgSlot label="aperçu / 1" ratio="4 / 3" mediaClass="pj-s1" src={images.s1} />
                      <ImgSlot label="détail / 2" ratio="4 / 3" mediaClass="pj-s2" src={images.s2} />
                    </div>
                    <div className="sm:col-span-3">
                      <ImgSlot label="visuel principal" ratio="4 / 3.4" mediaClass="pj-big" tall src={images.big} />
                    </div>
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

/* --------------------------------- Footer --------------------------------- */
function Footer() {
  const s = window.__SITE_DATA__?.settings || {};
  const socials = [
    [MailIcon, 'Email', s.email ? `mailto:${s.email}` : 'mailto:mathys@omydoo.fr'],
    [GithubIcon, 'GitHub', s.github || '#'],
    [LinkedinIcon, 'LinkedIn', s.linkedin || '#'],
  ];
  return (
    <footer id="contact" className="relative bg-night px-5 py-28 text-center sm:px-8 md:px-10">
      <div className="mx-auto flex max-w-4xl flex-col items-center">
        <FadeIn><SectionLabel center>Contact</SectionLabel></FadeIn>
        <FadeIn delay={0.05}>
          <h2 className="display-heading mt-8" style={{ fontSize: 'clamp(2.6rem, 9vw, 8rem)' }}>Travaillons<br />ensemble.</h2>
        </FadeIn>
        <FadeIn delay={0.1}>
          <p className="mt-8 max-w-xl font-light text-bone/65" style={{ fontSize: 'clamp(1rem, 2vw, 1.35rem)' }}>
            Une idée, un produit à construire ou une équipe à renforcer&nbsp;? Écrivez-moi, on en discute.
          </p>
        </FadeIn>
        <FadeIn delay={0.15} className="mt-12"><ContactButton large /></FadeIn>

        <FadeIn delay={0.2} className="mt-20 w-full">
          <div className="flex flex-wrap items-center justify-center gap-8 border-t border-[rgba(232,237,233,0.12)] pt-10">
            {socials.map(([I, label, href]) => (
              <a key={label} href={href} onClick={(e) => { if (href === '#') e.preventDefault(); }}
                 className="group inline-flex items-center gap-2.5 text-bone/70 transition-colors duration-300 hover:text-sage">
                <I size={20} />
                <span className="text-sm uppercase tracking-[0.18em]">{label}</span>
              </a>
            ))}
          </div>
          <p className="mt-10 font-light text-bone/35 text-xs uppercase tracking-[0.25em]">© 2026 Mathys Da Cunha</p>
        </FadeIn>
      </div>
    </footer>
  );
}

Object.assign(window, { Navbar, About, TechStack, Services, Process, Projects, Footer });
