/* global React, ReactDOM */
const { useState, useEffect, useRef, useCallback, useMemo } = React;

function GitHubIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
      <path d="M12 2C6.477 2 2 6.477 2 12c0 4.418 2.865 8.166 6.839 9.489.5.092.682-.217.682-.482 0-.237-.009-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.154-1.11-1.462-1.11-1.462-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0 1 12 6.836a9.59 9.59 0 0 1 2.504.337c1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.163 22 16.418 22 12c0-5.523-4.477-10-10-10z"/>
    </svg>
  );
}

function LinkedInIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
      <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065 2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
    </svg>
  );
}

// ----- Reveal on scroll -----
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll('.reveal');
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add('is-visible');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' });
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  });
}

// ----- Spotlight cursor -----
function Spotlight() {
  useEffect(() => {
    const onMove = (e) => {
      document.documentElement.style.setProperty('--mx', e.clientX + 'px');
      document.documentElement.style.setProperty('--my', e.clientY + 'px');
    };
    window.addEventListener('pointermove', onMove);
    return () => window.removeEventListener('pointermove', onMove);
  }, []);
  return <div className="spotlight" />;
}

// ----- Glass with hover sheen -----
function Glass({ className = '', children, style, interactive = true, ...rest }) {
  const ref = useRef(null);
  const onMove = (e) => {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    el.style.setProperty('--hx', e.clientX - r.left + 'px');
    el.style.setProperty('--hy', e.clientY - r.top + 'px');
  };
  return (
    <div
      ref={ref}
      className={`glass ${interactive ? 'glass--interactive' : ''} ${className}`}
      style={{ ...style, "--hx": "387.09765625px", "--hy": "165.71484375px" }}
      onPointerMove={interactive ? onMove : undefined}
      {...rest}>
      
      {children}
    </div>);

}

// ----- Magnetic wrapper -----
function Magnetic({ children, strength = 0.3, className = '' }) {
  const ref = useRef(null);
  const onMove = (e) => {
    const el = ref.current;if (!el) return;
    const r = el.getBoundingClientRect();
    const dx = (e.clientX - (r.left + r.width / 2)) * strength;
    const dy = (e.clientY - (r.top + r.height / 2)) * strength;
    el.style.transform = `translate(${dx}px, ${dy}px)`;
  };
  const onLeave = () => {
    const el = ref.current;if (!el) return;
    el.style.transform = 'translate(0,0)';
  };
  return (
    <span
      ref={ref}
      className={className}
      style={{ display: 'inline-block', transition: 'transform 0.45s cubic-bezier(0.22,1,0.36,1)' }}
      onPointerMove={onMove}
      onPointerLeave={onLeave}>
      
      {children}
    </span>);

}

// ----- Floating orb (parallax to scroll) -----
function Orb({ x, y, size, color, depth = 0.2 }) {
  const ref = useRef(null);
  useEffect(() => {
    const onScroll = () => {
      if (!ref.current) return;
      const sy = window.scrollY;
      ref.current.style.transform = `translateY(${sy * depth}px)`;
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, [depth]);
  return (
    <div
      ref={ref}
      className="orb"
      style={{
        left: x, top: y, width: size, height: size,
        background: `radial-gradient(circle, ${color} 0%, transparent 70%)`
      }} />);


}

const NAV_IDS = ['home', 'about', 'stack', 'projects', 'experience', 'interests', 'contact'];

// ----- Nav -----
function Nav({ t, lang, setLang }) {
  const [active, setActive] = useState('home');
  const [menuOpen, setMenuOpen] = useState(false);

  useEffect(() => {
    const onScroll = () => {
      let cur = 'home';
      for (const id of NAV_IDS) {
        const el = document.getElementById(id);
        if (!el) continue;
        if (el.getBoundingClientRect().top < window.innerHeight * 0.4) cur = id;
      }
      setActive(cur);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, []);

  // Close sheet on scroll or desktop resize
  useEffect(() => {
    if (!menuOpen) return;
    const close = () => setMenuOpen(false);
    window.addEventListener('scroll', close, { passive: true, once: true });
    window.addEventListener('resize', close, { once: true });
    return () => {
      window.removeEventListener('scroll', close);
      window.removeEventListener('resize', close);
    };
  }, [menuOpen]);

  const close = () => setMenuOpen(false);

  return (
    <>
      <nav className="nav glass">
        <div className="nav-brand">LF.</div>
        {NAV_IDS.map((id) => (
          <a key={id} href={`#${id}`} className={`nav-link ${active === id ? 'active' : ''}`}>
            {t.nav[id]}
          </a>
        ))}
        <div className="nav-lang">
          <button className={lang === 'it' ? 'active' : ''} onClick={() => setLang('it')}>IT</button>
          <button className={lang === 'en' ? 'active' : ''} onClick={() => setLang('en')}>EN</button>
        </div>
        <div className="nav-social">
          <a href={window.SOCIAL.github} target="_blank" rel="noopener" aria-label="GitHub" className="nav-social-link"><GitHubIcon /></a>
          <a href={window.SOCIAL.linkedin} target="_blank" rel="noopener" aria-label="LinkedIn" className="nav-social-link"><LinkedInIcon /></a>
        </div>
        {/* Burger — visible only on mobile */}
        <button
          className={`nav-burger${menuOpen ? ' open' : ''}`}
          onClick={() => setMenuOpen((o) => !o)}
          aria-label={menuOpen ? 'Chiudi menu' : 'Apri menu'}
          aria-expanded={menuOpen}
        >
          <span /><span /><span />
        </button>
      </nav>

      {/* Mobile sheet */}
      <div className={`nav-sheet glass${menuOpen ? ' is-open' : ''}`} aria-hidden={!menuOpen}>
        {NAV_IDS.map((id, i) => (
          <a
            key={id}
            href={`#${id}`}
            className={`nav-sheet-link${active === id ? ' active' : ''}`}
            style={{ transitionDelay: menuOpen ? `${i * 0.045}s` : '0s' }}
            onClick={close}
            tabIndex={menuOpen ? 0 : -1}
          >
            <span className="nav-sheet-num">0{i + 1}</span>
            {t.nav[id]}
          </a>
        ))}
        <div className="nav-sheet-bottom">
          <button className={lang === 'it' ? 'active' : ''} onClick={() => { setLang('it'); close(); }}>IT</button>
          <button className={lang === 'en' ? 'active' : ''} onClick={() => { setLang('en'); close(); }}>EN</button>
          <div className="nav-sheet-social">
            <a href={window.SOCIAL.github} target="_blank" rel="noopener" aria-label="GitHub" className="nav-social-link" onClick={close}><GitHubIcon /></a>
            <a href={window.SOCIAL.linkedin} target="_blank" rel="noopener" aria-label="LinkedIn" className="nav-social-link" onClick={close}><LinkedInIcon /></a>
          </div>
        </div>
      </div>

      {menuOpen && <div className="nav-backdrop" onClick={close} />}
    </>
  );
}

// ----- Now Playing card — live Spotify data -----
function NowPlayingCard() {
  const [track, setTrack] = useState(null); // null = loading
  const [localProg, setLocalProg] = useState(0);
  const trackRef = useRef(null);

  const poll = useCallback(async () => {
    try {
      const res = await fetch('/api/now-playing');
      if (!res.ok) return;
      const data = await res.json();
      setTrack(data);
      trackRef.current = data;
      if (data.isPlaying && data.duration) {
        setLocalProg(data.progress / data.duration);
      }
    } catch { /* keep previous state on network error */ }
  }, []);

  useEffect(() => {
    poll();
    const id = setInterval(poll, 10000);
    return () => clearInterval(id);
  }, [poll]);

  // Tick progress locally between API polls
  useEffect(() => {
    const id = setInterval(() => {
      if (trackRef.current?.isPlaying && trackRef.current?.duration) {
        setLocalProg((p) => Math.min(1, p + 1000 / trackRef.current.duration));
      }
    }, 1000);
    return () => clearInterval(id);
  }, []);

  const fmt = (ms) => {
    const s = Math.floor(ms / 1000);
    return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`;
  };

  const title    = track?.title   ?? (track === null ? '…' : '—');
  const artist   = track?.artist  ?? '';
  const albumArt = track?.albumArt;
  const duration = track?.duration ?? 220000;
  const isPlaying = track?.isPlaying ?? false;

  return (
    <Glass className="side-card now-playing" interactive={true}>
      <h4>
        ♪ Now playing
        {track !== null && !isPlaying && (
          <span className="np-last-badge">· last played</span>
        )}
      </h4>
      <div className="np-row">
        <div
          className="np-cover"
          aria-hidden="true"
          style={albumArt ? {
            backgroundImage: `url(${albumArt})`,
            backgroundSize: 'cover',
            backgroundPosition: 'center',
          } : {}}
        >
          {isPlaying && (
            <div className="np-bars">
              {[0, 1, 2, 3].map((i) => (
                <span key={i} style={{ animationDelay: `${i * 0.15}s` }} />
              ))}
            </div>
          )}
        </div>
        <div className="np-meta">
          <div className="np-title">{title}</div>
          <div className="np-artist">{artist}</div>
        </div>
      </div>
      <div className="np-progress">
        <div className="np-progress-fill" style={{ width: `${localProg * 100}%` }} />
      </div>
      <div className="np-time">
        <span>{fmt(localProg * duration)}</span>
        <span>{fmt(duration)}</span>
      </div>
    </Glass>
  );
}

// ----- F1 standings card -----
const F1_CALENDAR_2026 = [
  { name: 'Australian Grand Prix', short: 'Australia',   circuit: 'Albert Park · Melbourne',           round: 1,  img: 'Melburn',      date: new Date('2026-03-08T06:00:00Z') },
  { name: 'Chinese Grand Prix',    short: 'China',       circuit: 'Shanghai International Circuit',    round: 2,  img: 'Shangai',      date: new Date('2026-03-15T07:00:00Z') },
  { name: 'Japanese Grand Prix',   short: 'Japan',       circuit: 'Suzuka Circuit',                    round: 3,  img: 'suzuka',       date: new Date('2026-03-29T05:00:00Z') },
  { name: 'Miami Grand Prix',      short: 'Miami',       circuit: 'Miami International Autodrome',     round: 4,  img: 'Miami',        date: new Date('2026-05-03T21:00:00-04:00') },
  { name: 'Canadian Grand Prix',   short: 'Canada',      circuit: 'Circuit Gilles Villeneuve',         round: 5,  img: 'Canada',       date: new Date('2026-05-24T14:00:00-04:00') },
  { name: 'Monaco Grand Prix',     short: 'Monaco',      circuit: 'Circuit de Monaco',                 round: 6,  img: 'Monaco',       date: new Date('2026-06-07T13:00:00+02:00') },
  { name: 'Barcelona Grand Prix',  short: 'Spain',       circuit: 'Circuit de Barcelona-Catalunya',    round: 7,  img: 'Barcellona',   date: new Date('2026-06-14T13:00:00+02:00') },
  { name: 'Austrian Grand Prix',   short: 'Austria',     circuit: 'Red Bull Ring · Spielberg',         round: 8,  img: 'Austria',      date: new Date('2026-06-28T13:00:00+02:00') },
  { name: 'British Grand Prix',    short: 'Britain',     circuit: 'Silverstone Circuit',               round: 9,  img: 'Siverstone',   date: new Date('2026-07-05T14:00:00+01:00') },
  { name: 'Belgian Grand Prix',    short: 'Belgium',     circuit: 'Spa-Francorchamps',                 round: 10, img: 'Spa',          date: new Date('2026-07-19T13:00:00+02:00') },
  { name: 'Hungarian Grand Prix',  short: 'Hungary',     circuit: 'Hungaroring · Budapest',            round: 11, img: 'Budapest',     date: new Date('2026-07-26T13:00:00+02:00') },
  { name: 'Dutch Grand Prix',      short: 'Netherlands', circuit: 'Circuit Park Zandvoort',            round: 12, img: 'Zandvoort',    date: new Date('2026-08-23T13:00:00+02:00') },
  { name: 'Italian Grand Prix',    short: 'Italy',       circuit: 'Autodromo Nazionale di Monza',      round: 13, img: 'Monza',        date: new Date('2026-09-06T13:00:00+02:00') },
  { name: 'Spanish Grand Prix',    short: 'Madrid',      circuit: 'Madring · Madrid',                  round: 14, img: 'Madrid',       date: new Date('2026-09-13T13:00:00+02:00') },
  { name: 'Azerbaijan Grand Prix', short: 'Azerbaijan',  circuit: 'Baku City Circuit',                 round: 15, img: 'Baku',         date: new Date('2026-09-26T11:00:00+04:00') },
  { name: 'Singapore Grand Prix',  short: 'Singapore',   circuit: 'Marina Bay Street Circuit',         round: 16, img: 'Singapore',    date: new Date('2026-10-11T20:00:00+08:00') },
  { name: 'United States Grand Prix', short: 'USA',      circuit: 'COTA · Austin',                     round: 17, img: 'Austin',       date: new Date('2026-10-25T20:00:00-05:00') },
  { name: 'Mexico City Grand Prix', short: 'Mexico',     circuit: 'Autódromo Hermanos Rodríguez',      round: 18, img: 'Mexico',       date: new Date('2026-11-01T20:00:00-06:00') },
  { name: 'Brazilian Grand Prix',  short: 'Brazil',      circuit: 'Interlagos · São Paulo',            round: 19, img: 'Brazil',       date: new Date('2026-11-08T18:00:00-03:00') },
  { name: 'Las Vegas Grand Prix',  short: 'Las Vegas',   circuit: 'Las Vegas Strip Circuit',           round: 20, img: 'Las_Vegas',    date: new Date('2026-11-22T22:00:00-08:00') },
  { name: 'Qatar Grand Prix',      short: 'Qatar',       circuit: 'Losail International Circuit',      round: 21, img: 'Lusail',       date: new Date('2026-11-29T15:00:00+03:00') },
  { name: 'Abu Dhabi Grand Prix',  short: 'Abu Dhabi',   circuit: 'Yas Marina Circuit',                round: 22, img: 'Abu_Dhabi',   date: new Date('2026-12-06T13:00:00+04:00') },
];

function F1Card() {
  const [, setTick] = useState(0);
  useEffect(() => {
    const i = setInterval(() => setTick((t) => t + 1), 1000);
    return () => clearInterval(i);
  }, []);

  const now = new Date();
  const next = F1_CALENDAR_2026.find((r) => r.date > now) || F1_CALENDAR_2026[F1_CALENDAR_2026.length - 1];
  const diff = Math.max(0, next.date - now);
  const days  = Math.floor(diff / 86400000);
  const hours = Math.floor((diff % 86400000) / 3600000);
  const mins  = Math.floor((diff % 3600000) / 60000);
  const secs  = Math.floor((diff % 60000) / 1000);

  return (
    <Glass className="side-card f1-card" interactive={true}>
      {/* track image — decorative background layer */}
      <div className="f1-track-bg" aria-hidden="true">
        <div className="f1-track-glow" />
        <img
          className="f1-track-img"
          src={`F1_Tracks/${next.img}-removebg-preview.png`}
          alt=""
          draggable="false"
        />
      </div>

      {/* foreground content */}
      <h4 className="f1-fg">🏁 Next race · R{next.round}</h4>
      <div className="f1-name f1-fg">{next.name}</div>
      <div className="f1-track-label f1-fg">{next.circuit}</div>

      <div className="f1-countdown f1-fg">
        <div><span className="num">{String(days).padStart(2,'0')}</span><span className="lbl">days</span></div>
        <div><span className="num">{String(hours).padStart(2,'0')}</span><span className="lbl">hrs</span></div>
        <div><span className="num">{String(mins).padStart(2,'0')}</span><span className="lbl">min</span></div>
        <div><span className="num">{String(secs).padStart(2,'0')}</span><span className="lbl">sec</span></div>
      </div>

      <div className="f1-flag f1-fg" aria-hidden="true">
        {Array.from({ length: 32 }).map((_, i) => (
          <span key={i} className={(Math.floor(i / 8) + i) % 2 === 0 ? 'on' : ''} />
        ))}
      </div>
    </Glass>
  );
}

// ----- Live clock -----
function LiveClock() {
  const [now, setNow] = useState(new Date());
  useEffect(() => {
    const i = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(i);
  }, []);
  const time = now.toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });
  return (
    <Glass className="side-card" interactive={true}>
      <h4>Now in Verona</h4>
      <div className="clock">{time}</div>
      <div className="clock-meta">CET · GMT+1</div>
      <div style={{ flex: 1 }} />
      <div className="clock-meta" style={{ marginTop: 16, color: 'var(--fg-dim)' }}>
        <span style={{ color: 'var(--accent-c)' }}>●</span> Currently building something
      </div>
    </Glass>);

}

// ----- Terminal card -----
function TerminalCard({ lang }) {
  return (
    <Glass className="side-card" interactive={true}>
      <h4>~/whoami</h4>
      <div className="terminal">
        <div><span className="prompt">$</span> <span className="cmd">cat profile.json</span></div>
        <div className="out">{'{'}</div>
        <div className="out">&nbsp;&nbsp;<span className="key">"name"</span>: <span className="str">"Leonardo"</span>,</div>
        <div className="out">&nbsp;&nbsp;<span className="key">"role"</span>: <span className="str">"Full-Stack Dev"</span>,</div>
        <div className="out">&nbsp;&nbsp;<span className="key">"city"</span>: <span className="str">"Verona, IT"</span>,</div>
        <div className="out">&nbsp;&nbsp;<span className="key">"stack"</span>: [<span className="str">"Java"</span>, <span className="str">"Py"</span>, <span className="str">"JS"</span>, <span className="str">"Swift"</span>],</div>
        <div className="out">&nbsp;&nbsp;<span className="key">"status"</span>: <span className="str">"{lang === 'it' ? 'in costruzione' : 'building'}"</span></div>
        <div className="out">{'}'}</div>
        <div style={{ marginTop: 8 }}><span className="prompt">$</span> <span className="cursor" /></div>
      </div>
    </Glass>);

}

// ----- Currently Building card -----
function CurrentlyBuildingCard({ lang }) {
  return (
    <Glass className="wip-card" interactive={true}>
      <div className="wip-header">
        <span className="wip-label">⚡ Currently building</span>
      </div>
      <div className="wip-name">{window.WIP.name}</div>
      <div className="wip-desc">{window.WIP.desc[lang]}</div>
      <div className="wip-tags">
        {window.WIP.tags.map((tag) => <span key={tag} className="wip-tag">{tag}</span>)}
      </div>
      <div className="wip-status">
        <span className="wip-dot" />
        <span>{lang === 'it' ? 'In sviluppo attivo' : 'Active development'}</span>
      </div>
    </Glass>
  );
}

// ----- Hero -----
function Hero({ t }) {
  return (
    <section id="home" className="hero">
      <div className="container">
        <div className="hero-grid">
          <div className="hero-left reveal">
            <Glass className="hero-card">

              <div className="hero-status">
                <span className="dot" /> {t.hero.status}
              </div>
              <div className="hero-role">{t.hero.role}</div>
              <h1 className="hero-name">
                <span className="word">{t.hero.name[0]}</span>
                <br />
                <span className="word">{t.hero.name[1]}</span>
              </h1>
              <p className="hero-tagline">{t.hero.tagline}</p>
              <div className="hero-cta">
                <Magnetic strength={0.25}>
                  <a href="#contact" className="btn btn--primary">
                    {t.hero.ctaPrimary} <span className="arrow">→</span>
                  </a>
                </Magnetic>
                <Magnetic strength={0.2}>
                  <a href="#about" className="btn">
                    {t.hero.ctaSecondary}
                  </a>
                </Magnetic>
              </div>
              <div className="hero-meta">
                {t.hero.meta.map((m, i) =>
                <div key={i} className="hero-meta-item">
                    <span className="lbl">{m.lbl}:</span> <span>{m.val}</span>
                  </div>
                )}
              </div>
            </Glass>
            <CurrentlyBuildingCard lang={t === window.PORTFOLIO_DATA.it ? 'it' : 'en'} />
          </div>
          <div className="hero-side reveal delay-2">
            <LiveClock />
            <NowPlayingCard />
            <F1Card />
            <TerminalCard lang={t === window.PORTFOLIO_DATA.it ? 'it' : 'en'} />
          </div>
        </div>
      </div>
    </section>);

}

// ----- Hero portrait (transparent png on glow, no card chrome) -----
function HeroPortrait() {
  const [errored, setErrored] = useState(false);
  if (errored) return null;
  return (
    <div className="hero-portrait reveal">
      <div className="hero-portrait-glow" aria-hidden="true" />
      <img
        src="photo.png"
        alt="Leonardo Foroncelli"
        className="hero-portrait-img"
        onError={() => setErrored(true)}
      />
    </div>
  );
}

// ----- Interests -----
function Interests({ t }) {
  return (
    <section id="interests">
      <Orb x="70%" y="30%" size="340px" color="color-mix(in oklab, var(--accent-c) 30%, transparent)" depth={0.07} />
      <div className="container">
        <div className="reveal" style={{ marginBottom: 48, maxWidth: 720 }}>
          <span className="eyebrow">{t.interests.eyebrow}</span>
          <h2 className="section-title">{t.interests.title}</h2>
          <p className="muted" style={{ fontSize: 17, lineHeight: 1.55, marginTop: 12 }}>{t.interests.sub}</p>
        </div>
        <div className="interests-grid">
          {t.interests.items.map((it, i) =>
          <Glass key={i} className={`interest-card interest-${it.accent || 'plain'} reveal delay-${i % 4 + 1}`}>
              <div className="interest-visual" aria-hidden="true">
                <InterestArt accent={it.accent} />
              </div>
              <div className="interest-glyph" aria-hidden="true">{it.glyph}</div>
              <div className="interest-name">{it.name}</div>
              <div className="interest-desc">{it.desc}</div>
            </Glass>
          )}
        </div>
      </div>
    </section>);

}

// Decorative SVG art per interest
function InterestArt({ accent }) {
  if (accent === 'tennis') {
    return (
      <svg viewBox="0 0 200 200" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%' }}>
        <defs>
          <radialGradient id="ball" cx="35%" cy="35%">
            <stop offset="0%" stopColor="#dfff5e" />
            <stop offset="100%" stopColor="#7fa314" />
          </radialGradient>
        </defs>
        <circle cx="155" cy="55" r="48" fill="url(#ball)" opacity="0.85" />
        <path d="M 110 30 Q 155 55 200 25" fill="none" stroke="#fff" strokeWidth="1.5" opacity="0.7" />
        <path d="M 115 80 Q 155 55 195 90" fill="none" stroke="#fff" strokeWidth="1.5" opacity="0.7" />
      </svg>);

  }
  if (accent === 'f1') {
    return (
      <svg viewBox="0 0 200 200" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%' }}>
        {Array.from({ length: 8 }).map((_, r) =>
        Array.from({ length: 4 }).map((_, c) => {
          const isOn = (r + c) % 2 === 0;
          return <rect key={`${r}-${c}`} x={140 + c * 14} y={r * 14} width="14" height="14" fill={isOn ? '#fff' : 'transparent'} opacity="0.15" />;
        })
        )}
        <path d="M 0 130 Q 80 100 200 140" fill="none" stroke="var(--accent-a)" strokeWidth="2" opacity="0.5" />
        <path d="M 0 145 Q 80 115 200 155" fill="none" stroke="var(--accent-c)" strokeWidth="1" opacity="0.4" />
      </svg>);

  }
  if (accent === 'music') {
    return (
      <svg viewBox="0 0 200 200" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%' }}>
        {Array.from({ length: 22 }).map((_, i) => {
          const h = 8 + Math.abs(Math.sin(i * 0.7)) * 60 + Math.abs(Math.cos(i * 1.3)) * 25;
          return <rect key={i} x={5 + i * 9} y={120 - h / 2} width="4" height={h} rx="2" fill="var(--accent-c)" opacity={0.3 + i % 3 * 0.15} />;
        })}
      </svg>);

  }
  if (accent === 'gaming') {
    return (
      <svg viewBox="0 0 200 200" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%' }}>
        {/* pixel art controller-ish shape */}
        {[
        [10, 0], [11, 0], [12, 0], [13, 0],
        [9, 1], [10, 1], [11, 1], [12, 1], [13, 1], [14, 1],
        [8, 2], [9, 2], [14, 2], [15, 2],
        [8, 3], [15, 3]].
        map(([x, y], i) =>
        <rect key={i} x={120 + x * 5} y={10 + y * 5} width="5" height="5" fill="var(--accent-a)" opacity="0.5" />
        )}
        {/* grid pattern */}
        {Array.from({ length: 6 }).map((_, i) =>
        <line key={i} x1="0" y1={i * 30} x2="200" y2={i * 30} stroke="var(--accent-c)" strokeWidth="0.5" opacity="0.1" />
        )}
      </svg>);

  }
  if (accent === 'break') {
    return (
      <svg viewBox="0 0 200 200" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%' }}>
        <path d="M 0 100 L 60 60 L 100 90 L 140 50 L 200 80 L 200 200 L 0 200 Z" fill="var(--accent-b)" opacity="0.18" />
        <path d="M 0 100 L 60 60 L 100 90 L 140 50 L 200 80" fill="none" stroke="var(--accent-c)" strokeWidth="1.5" opacity="0.4" />
      </svg>);

  }
  if (accent === 'coffee') {
    return (
      <svg viewBox="0 0 200 200" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%' }}>
        <path d="M 150 30 Q 145 20 155 15 Q 165 10 160 0" fill="none" stroke="var(--accent-c)" strokeWidth="2" opacity="0.5" />
        <path d="M 165 35 Q 160 25 170 20 Q 180 15 175 5" fill="none" stroke="var(--accent-c)" strokeWidth="2" opacity="0.4" />
        <path d="M 180 30 Q 175 20 185 15" fill="none" stroke="var(--accent-c)" strokeWidth="2" opacity="0.3" />
      </svg>);

  }
  return null;
}

// ----- About -----
function About({ t }) {
  return (
    <section id="about">
      <Orb x="-10%" y="20%" size="380px" color="color-mix(in oklab, var(--accent-a) 35%, transparent)" depth={-0.08} />
      <div className="container">
        <div className="reveal" style={{ marginBottom: 48 }}>
          <span className="eyebrow">{t.about.eyebrow}</span>
          <h2 className="section-title" style={{ maxWidth: 900 }}>{t.about.title}</h2>
        </div>
        <div className="about-grid">
          <Glass className="about-card reveal">
            <div className="about-text">
              <p>{t.about.p1}</p>
              <p className="muted">{t.about.p2}</p>
            </div>
          </Glass>
          <div className="stats reveal delay-1">
            {t.about.stats.map((s, i) =>
            <Glass key={i} className="stat-card">
                <div className="stat-num">{s.num}</div>
                <div className="stat-lbl">{s.lbl}</div>
              </Glass>
            )}
          </div>
        </div>
      </div>
    </section>);

}

// ----- Stack -----
function Stack({ t }) {
  return (
    <section id="stack">
      <Orb x="80%" y="10%" size="320px" color="color-mix(in oklab, var(--accent-b) 35%, transparent)" depth={0.1} />
      <div className="container">
        <div className="reveal" style={{ marginBottom: 48, maxWidth: 720 }}>
          <span className="eyebrow">{t.stack.eyebrow}</span>
          <h2 className="section-title">{t.stack.title}</h2>
          <p className="muted" style={{ fontSize: 17, lineHeight: 1.55, marginTop: 12 }}>{t.stack.sub}</p>
        </div>
        <div className="stack-grid">
          {window.STACK_DATA.map((s, i) =>
          <Glass key={s.name} className={`stack-card reveal delay-${i % 4 + 1}`}>
              <div className="stack-icon" dangerouslySetInnerHTML={{ __html: s.glyph }} />
              <div>
                <div className="stack-name">{s.name}</div>
                <div className="stack-meta">{s.meta}</div>
              </div>
              <div className="stack-bar"><span style={{ width: `${s.level}%` }} /></div>
            </Glass>
          )}
        </div>
      </div>
    </section>);

}

// ----- Project visual placeholder (subtle gradient + glyph) -----
function ProjectVisual({ seed = 0 }) {
  const angles = [120, 200, 60, 300, 30];
  const a = angles[seed % angles.length];
  return (
    <div className="project-visual" style={{
      background: `radial-gradient(ellipse 60% 50% at ${30 + seed * 17 % 50}% ${30 + seed * 23 % 50}%, color-mix(in oklab, var(--accent-a) 35%, transparent), transparent 70%), linear-gradient(${a}deg, transparent, color-mix(in oklab, var(--accent-b) 18%, transparent))`,
      filter: 'blur(0px)'
    }} />);

}

// ----- Project Modal -----
function ProjectModal({ project, lang, onClose }) {
  const detail = project.detail;
  const isIt = lang === 'it';

  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = '';
    };
  }, [onClose]);

  return (
    <div className="modal-overlay" onClick={onClose}>
      <Glass className="modal-card" onClick={(e) => e.stopPropagation()} interactive={false}>
        <button className="modal-close" onClick={onClose} aria-label="Chiudi">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M18 6L6 18M6 6l12 12"/></svg>
        </button>

        {project.video ? (
          <div className="modal-img-wrap">
            <video
              src={project.video}
              className="modal-video"
              autoPlay
              muted
              loop
              playsInline
              controls
            />
          </div>
        ) : project.image ? (
          <div className="modal-img-wrap">
            <img
              src={project.image}
              alt={project.title}
              className="modal-img"
              onError={(e) => { e.currentTarget.parentElement.style.display = 'none'; }}
            />
          </div>
        ) : null}

        <div className="modal-header">
          <span className="project-cat">{project.cat}</span>
          {project.status === 'wip' && (
            <span className="modal-wip-badge">
              <span className="wip-dot" style={{ width: 6, height: 6 }} />
              {isIt ? 'In sviluppo' : 'In progress'}
            </span>
          )}
        </div>
        <h2 className="modal-title">{project.title}</h2>

        {detail && <p className="modal-body">{detail.body}</p>}

        {project.link && (
          <a href={project.link} target="_blank" rel="noopener" className="modal-link-btn">
            {project.linkLabel ? project.linkLabel[lang] : (isIt ? 'Visita il sito' : 'Visit site')} <span className="arrow">→</span>
          </a>
        )}

        <div className="project-tags" style={{ marginTop: 16 }}>
          {project.tags.map((tag) => <span key={tag} className="project-tag">{tag}</span>)}
        </div>

        {detail?.learned?.length > 0 && (
          <div className="modal-section">
            <div className="modal-section-label">{isIt ? 'Cosa ho imparato' : 'What I learned'}</div>
            <ul className="modal-list">
              {detail.learned.map((l, i) => <li key={i}>{l}</li>)}
            </ul>
          </div>
        )}

        {detail?.challenge && (
          <div className="modal-section">
            <div className="modal-section-label">{isIt ? 'La sfida principale' : 'Main challenge'}</div>
            <p className="modal-body">{detail.challenge}</p>
          </div>
        )}
      </Glass>
    </div>
  );
}

// ----- Projects -----
function Projects({ t, lang }) {
  const [selected, setSelected] = useState(null);

  return (
    <>
      <section id="projects">
        <Orb x="-5%" y="60%" size="420px" color="color-mix(in oklab, var(--accent-c) 30%, transparent)" depth={0.05} />
        <div className="container">
          <div className="reveal" style={{ marginBottom: 48, maxWidth: 720 }}>
            <span className="eyebrow">{t.projects.eyebrow}</span>
            <h2 className="section-title">{t.projects.title}</h2>
            <p className="muted" style={{ fontSize: 17, lineHeight: 1.55, marginTop: 12 }}>{t.projects.sub}</p>
          </div>
          <div className="projects-grid">
            {t.projects.items.map((p, i) =>
            <Glass key={i} className={`project-card ${p.span} reveal delay-${i % 4 + 1}`} onClick={() => setSelected(p)} style={{ cursor: 'pointer' }}>
                <ProjectVisual seed={i} />
                <div>
                  <div className="project-cat">{p.cat}</div>
                  <div className="project-title">{p.title}</div>
                  <div className="project-desc">{p.desc}</div>
                </div>
                <div className="project-tags">
                  {p.tags.map((tag) => <span key={tag} className="project-tag">{tag}</span>)}
                </div>
                <div className="project-arrow" aria-hidden="true">
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M7 17L17 7M17 7H8M17 7V16" /></svg>
                </div>
              </Glass>
            )}
          </div>
        </div>
      </section>
      {selected && <ProjectModal project={selected} lang={lang} onClose={() => setSelected(null)} />}
    </>
  );
}

// ----- Experience -----
function Experience({ t }) {
  return (
    <section id="experience">
      <Orb x="85%" y="40%" size="360px" color="color-mix(in oklab, var(--accent-a) 30%, transparent)" depth={-0.06} />
      <div className="container">
        <div className="reveal" style={{ marginBottom: 48, maxWidth: 720 }}>
          <span className="eyebrow">{t.experience.eyebrow}</span>
          <h2 className="section-title">{t.experience.title}</h2>
        </div>
        <div className="timeline" style={{ maxWidth: 840 }}>
          {t.experience.items.map((it, i) =>
          <div key={i} className={`timeline-item reveal delay-${i % 4 + 1}`}>
              <Glass className="timeline-card">
                <div className="timeline-meta">
                  <span>{it.date}</span><span className="dot" /><span>{i === 0 ? t === window.PORTFOLIO_DATA.it ? 'In corso' : 'Ongoing' : ''}</span>
                </div>
                <div className="timeline-title">{it.title}</div>
                <div className="timeline-org">{it.org}</div>
                <div className="timeline-desc">{it.desc}</div>
              </Glass>
            </div>
          )}
        </div>
      </div>
    </section>);

}

// ----- Contact -----
function Contact({ t }) {
  const [copied, setCopied] = useState(false);
  const email = 'leonardoforoncelli@gmail.com';
  const copy = () => {
    navigator.clipboard?.writeText(email);
    setCopied(true);
    setTimeout(() => setCopied(false), 1800);
  };
  return (
    <section id="contact">
      <div className="container">
        <Glass className="contact-card reveal">
          <span className="eyebrow" style={{ justifyContent: 'center' }}>{t.contact.eyebrow}</span>
          <h2>{t.contact.title}</h2>
          <p className="muted" style={{ fontSize: 17, lineHeight: 1.6, maxWidth: 560, margin: '0 auto' }}>{t.contact.sub}</p>
          <div style={{ marginTop: 36, marginBottom: 36 }}>
            <a className="email" href={`mailto:${email}`}>{email}</a>
          </div>
          <div className="hero-cta" style={{ justifyContent: 'center', marginTop: 0 }}>
            <Magnetic strength={0.25}>
              <a href={`mailto:${email}`} className="btn btn--primary">{t.contact.ctaPrimary} <span className="arrow">→</span></a>
            </Magnetic>
            <Magnetic strength={0.2}>
              <button onClick={copy} className="btn">{copied ? '✓ ' + (t === window.PORTFOLIO_DATA.it ? 'Copiato' : 'Copied') : t.contact.ctaSecondary}</button>
            </Magnetic>
          </div>
          <div className="contact-social">
            <Magnetic strength={0.2}>
              <a href={window.SOCIAL.github} target="_blank" rel="noopener" className="btn">
                <GitHubIcon /> GitHub
              </a>
            </Magnetic>
            <Magnetic strength={0.2}>
              <a href={window.SOCIAL.linkedin} target="_blank" rel="noopener" className="btn">
                <LinkedInIcon /> LinkedIn
              </a>
            </Magnetic>
          </div>
        </Glass>
      </div>
    </section>);

}

// ----- Footer -----
function Footer({ t }) {
  return (
    <footer className="footer">
      <div className="container" style={{ display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: 12, width: '100%' }}>
        <span>{t.footer.left}</span>
        <span>{t.footer.right}</span>
      </div>
    </footer>);

}

// ----- Tweaks -----
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "palette": "violet",
  "blur": 22,
  "theme": "dark"
} /*EDITMODE-END*/;

function Tweaks() {
  const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS);
  useEffect(() => {
    document.documentElement.dataset.palette = tweaks.palette;
    document.documentElement.dataset.theme = tweaks.theme;
    document.documentElement.style.setProperty('--glass-blur', tweaks.blur + 'px');
  }, [tweaks]);

  return (
    <window.TweaksPanel title="Tweaks">
      <window.TweakSection label="Palette">
        <window.TweakRadio
          value={tweaks.palette}
          onChange={(v) => setTweak('palette', v)}
          options={[
          { label: 'Violet', value: 'violet' },
          { label: 'Indigo', value: 'indigo' },
          { label: 'Pink', value: 'pink' }]
          } />
        
      </window.TweakSection>
      <window.TweakSection label="Theme">
        <window.TweakRadio
          value={tweaks.theme}
          onChange={(v) => setTweak('theme', v)}
          options={[
          { label: 'Dark', value: 'dark' },
          { label: 'Light', value: 'light' }]
          } />
        
      </window.TweakSection>
      <window.TweakSection label="Glass blur">
        <window.TweakSlider
          value={tweaks.blur}
          onChange={(v) => setTweak('blur', v)}
          min={4} max={40} step={1} unit="px" />
        
      </window.TweakSection>
    </window.TweaksPanel>);

}

// ----- App -----
function App() {
  const [lang, setLang] = useState(() => localStorage.getItem('lf_lang') || 'it');
  useEffect(() => {localStorage.setItem('lf_lang', lang);}, [lang]);
  const t = window.PORTFOLIO_DATA[lang];
  useReveal();

  return (
    <>
      <div className="aurora" />
      <div className="grain" />
      <Spotlight />
      <Nav t={t} lang={lang} setLang={setLang} />
      <main>
        <Hero t={t} />
        <About t={t} />
        <Stack t={t} />
        <Projects t={t} lang={lang} />
        <Experience t={t} />
        <Interests t={t} />
        <Contact t={t} />
      </main>
      <Footer t={t} />
      <Tweaks />
    </>);

}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);