// 4D Agency — interactive bits
const { useState, useEffect, useRef } = React;

/* ============================================================
   TRACK DATA
   ============================================================ */
const TRACKS = [
{ id: 1, src: "tracks/01.mp3", cover: "tracks/cover01.jpg", name: "Valse d'un printemps", style: "Nostalgie · Orchestre de chambre", bpm: "160 BPM", dur: "01:39", key: "F minor" },
{ id: 2, src: "tracks/02.mp3", cover: "tracks/cover02.jpg", name: "Village", style: "Exploration Médieval · Orchestral", bpm: "87 BPM", dur: "01:01", key: "B minor" },
{ id: 3, src: "tracks/03.mp3", cover: "tracks/cover03.jpg", name: "Le Mafieux", style: "Tension · Orchestral", bpm: "90 BPM", dur: "01:04", key: "E minor" },
{ id: 4, src: "tracks/04.mp3", cover: "tracks/cover04.jpg", name: "Excalibur", style: "Epique · Orchestral", bpm: "122 BPM", dur: "01:18", key: "D minor" },
{ id: 5, src: "tracks/05.mp3", cover: "tracks/cover05.jpg", name: "Temps qui passe", style: "Ambiance · Synthétiseurs", bpm: "77 BPM", dur: "02:29", key: "F# minor" }];


/* ============================================================
   NAVIGATION
   ============================================================ */
function Nav() {
  return (
    <nav className="nav">
      <div className="brand">
        <img src="uploads/4d-logo.svg" alt="4D Agency" className="brand-logo" />
      </div>
      <ul>
        <li><a href="#process">Processus</a></li>
        <li><a href="#excerpts">Extraits</a></li>
        <li><a href="#manifesto">Manifeste</a></li>
        <li><a href="#why">Pourquoi nous</a></li>
      </ul>
      <a href="https://calendar.app.google/ZvgjEjiUeEvqdv347" target="_blank" rel="noopener noreferrer" className="reserve">Réserver un échange</a>
    </nav>);

}

/* ============================================================
   HERO
   ============================================================ */
function Hero() {
  return (
    <section className="hero" data-screen-label="01 Hero">
      <div className="wrap">
        <div className="hero-grid">
          <div className="hero-left">
            <div className="hero-meta">
              <span>4D · Agency</span>
              <span className="line"></span>
              <span>COMPOSITEURS POUR L'IMAGE</span>
              <span className="line"></span>
              <span>Paris / Remote</span>
            </div>

            <h1>
              Créer<br />
              des musiques<br />
              <span className="it">immersives</span><br />
              pour vos oeuvres<br />
              narratives.
            </h1>

            <p className="hero-sub">Votre partenaire musical pour renforcer la narration, l’émotion et l’identité sonore de vos projets - de la première esquisse thématique au master prêt à l'intégration.



            </p>

            <div className="hero-cta">
              <a className="btn primary" href="mailto:contact@4dmusicagency.com">
                Parlez-nous de votre projet <span className="arrow">→</span>
              </a>
              <a className="btn ghost" href="#excerpts">
                Écouter nos extraits
              </a>
            </div>

            <div className="hero-reassure">
              <span className="dot"></span>
              <em>Disponibilité limitée pour conserver la qualité.</em>
            </div>
          </div>

          <div className="hero-right">
            <div className="bust-slot">
              <model-viewer
                style={{ height: "650px", width: "100%" }}
                src="uploads/cello.glb"
                alt="Violoncelle 3D — 4D Agency"
                interaction-prompt="none"
                shadow-intensity="0"
                exposure="1.2"
                environment-image="neutral"
                tone-mapping="commerce"
                field-of-view="25deg"
                camera-orbit="18deg "
                className="modelviewer"
              >
                <div slot="progress-bar"></div>
              </model-viewer>
            </div>

            {/*
            <div className="hero-meta-right">
              <span className="big">14</span>
              <span className="label">Bandes-son livrées</span>
              <span className="big">36 mois</span>
              <span className="label">Studios partenaires actifs</span>
            </div>
            */}
           
          </div>
        </div>

        <div className="scroll-cue">
          <span>Défilez</span>
          <span className="bar"></span>
          <span>Acte I</span>
        </div>
      </div>
    </section>);

}

/* ============================================================
   MARQUEE BAND
   ============================================================ */
function Marquee() {
  const items = [
  "Jeu narratif", "Film", "Serie", "Documentaire",
  "Jeu narratif", "Film", "Serie", "Documentaire",
  "Jeu narratif", "Film"];

  const block =
  <span>
      {items.map((t, i) =>
    <React.Fragment key={i}>
          {t}<span className="sep">✦</span>
        </React.Fragment>
    )}
    </span>;

  return (
    <div className="marquee" aria-hidden="true">
      <div className="marquee-track">
        {block}{block}
      </div>
    </div>);

}

/* ============================================================
   PROCESS
   ============================================================ */
function Process() {
  const steps = [
  {
    roman: "I", title: "Composition", lede: "Conception des thèmes et motifs.",
    body: "Nous traduisons vos intentions narratives et émotionnelles en idées musicales cohérente et identifiable.",
    tag: "Brief · références · démos thématiques"
  },
  {
    roman: "II", title: "Orchestration", lede: "Instruments, textures, émotion.",
    body: "Chaque thème se déploie à travers une palette sonore sur mesure, où instruments acoustiques, textures et synthétiseurs définissent sa couleur émotionnelle.",
    tag: "Maquette · prise live · sound design"
  },
  {
    roman: "III", title: "Mixing / Mastering", lede: "Prêt à l’intégration.",
    body: "Exports, stems et masters préparés selon les contraintes techniques de votre production, avec une livraison claire et prête à l’intégration.",
    tag: "Stems · loops · masters · documentation"
  }];


  return (
    <section className="section-paper" id="process" data-screen-label="02 Process">
      <div className="slate-num">Acte I — Le processus</div>
      <div className="wrap">
        <div className="section-head">
          <span className="num">02 ·</span>
          <h2>Le <span className="it">processus</span></h2>
          <div className="right">Trois étapes pour creer une musique cohérente, immersive et prête à l'intégration.


          </div>
        </div>

        <div className="process-grid">
          {steps.map((s, i) =>
          <article className="process-card" key={i}>
              <div className="roman">{s.roman}</div>
              <h3>{s.title}</h3>
              <div className="lede">{s.lede}</div>
              <p>{s.body}</p>
              <div className="footnote">
                <span>{s.tag}</span>
                <span>·</span>
              </div>
            </article>
          )}
        </div>

        <div className="process-tools" style={{ padding: "28px 0px 0px" }}>
          <span className="label" style={{ padding: "0px", margin: "0px", width: "100%" }}>BOITE À OUTILS ·</span>
          {["Cubase 13", "Kontakt 8", "Orchestral Tools", "Vienna Ensemble Pro", "Serum", "Omnisphere", "Fabfilter"].map((t) => <span className="tool" key={t}>{t}</span>
          )}
        </div>
      </div>
    </section>);

}

/* ============================================================
   EXCERPTS PLAYER
   ============================================================ */
function makeFallbackBars(trackId) {
  const seed = trackId * 37 + 11;
  return Array.from({ length: 84 }, (_, i) => {
    const v = Math.sin((i + seed) * 0.27) * 0.5 + Math.cos((i + seed) * 0.11) * 0.3 + 0.55;
    return Math.max(0.18, Math.min(1, v + Math.sin(i * 0.7 + seed) * 0.18));
  });
}

function Excerpts() {
  const [active, setActive]     = useState(0);
  const [playing, setPlaying]   = useState(false);
  const [progress, setProgress] = useState(0);
  const [duration, setDuration] = useState(0);

  const audioRef     = useRef(null);
  const wantPlay     = useRef(false); // intent ref — stable across closures
  const waveformsRef = useRef({});
  const blobUrlsRef  = useRef({});
  const [bars, setBars] = useState(() => makeFallbackBars(TRACKS[0].id));

  // Create the Audio element once
  if (!audioRef.current) {
    audioRef.current = new Audio();
    audioRef.current.preload = "auto";
  }

  // Wire up persistent event listeners once
  useEffect(() => {
    const a = audioRef.current;
    const onTime = () => { if (a.duration) setProgress(a.currentTime / a.duration); };
    const onMeta = () => setDuration(a.duration);
    const onEnd  = () => { setPlaying(false); setProgress(0); wantPlay.current = false; };
    a.addEventListener("timeupdate",      onTime);
    a.addEventListener("loadedmetadata",  onMeta);
    a.addEventListener("ended",           onEnd);
    return () => {
      a.removeEventListener("timeupdate",     onTime);
      a.removeEventListener("loadedmetadata", onMeta);
      a.removeEventListener("ended",          onEnd);
      a.pause();
    };
  }, []);

  // Fetch track as blob URL (Chrome can seek blob URLs without range requests),
  // then decode waveform from the same buffer — single download for both.
  useEffect(() => {
    const a   = audioRef.current;
    const id  = TRACKS[active].id;
    const src = TRACKS[active].src;

    setProgress(0);
    setDuration(0);

    // Fast path: already cached
    if (blobUrlsRef.current[id]) {
      a.src = blobUrlsRef.current[id];
      if (wantPlay.current) a.play().catch(() => {});
      if (waveformsRef.current[id]) setBars(waveformsRef.current[id]);
      else setBars(makeFallbackBars(id));
      return;
    }

    setBars(makeFallbackBars(id));

    let dead = false;
    fetch(src)
      .then(r => { if (!r.ok) throw new Error(); return r.arrayBuffer(); })
      .then(buf => {
        if (dead) return;

        // Decode waveform — slice so decodeAudioData doesn't detach original buf
        const ctx = new (window.AudioContext || window["webkitAudioContext"])();
        ctx.decodeAudioData(buf.slice(0), decoded => {
          ctx.close();
          if (dead) return;
          const ch  = decoded.getChannelData(0);
          const seg = Math.floor(ch.length / 84);
          const rms = Array.from({ length: 84 }, (_, i) => {
            let s = 0, off = i * seg;
            for (let j = off; j < off + seg; j++) s += ch[j] * ch[j];
            return Math.sqrt(s / seg);
          });
          const mx = Math.max(...rms, 1e-6);
          const normalized = rms.map(v => 0.18 + (v / mx) * 0.82);
          waveformsRef.current[id] = normalized;
          if (!dead) setBars(normalized);
        }, () => { ctx.close(); });

        // Blob URL → Chrome can seek without HTTP range requests
        const url = URL.createObjectURL(new Blob([buf], { type: "audio/mpeg" }));
        blobUrlsRef.current[id] = url;
        a.src = url;
        if (wantPlay.current) a.play().catch(() => {});
      })
      .catch(() => {
        if (dead) return;
        a.src = src; // fallback to direct src
        if (wantPlay.current) a.play().catch(() => {});
      });

    return () => { dead = true; };
  }, [active]);

  function togglePlay() {
    const a = audioRef.current;
    if (playing) {
      a.pause();
      wantPlay.current = false;
      setPlaying(false);
    } else {
      wantPlay.current = true;
      a.play().catch(() => {});
      setPlaying(true);
    }
  }

  function selectTrack(i) {
    wantPlay.current = true;
    setPlaying(true);
    setActive(i); // triggers useEffect → loads src → plays
  }

  function seekOnWaveform(e) {
    const a = audioRef.current;
    if (!a.duration) return;
    const r = e.currentTarget.getBoundingClientRect();
    const ratio = Math.max(0, Math.min(1, (e.clientX - r.left) / r.width));
    a.currentTime = ratio * a.duration;
    setProgress(ratio);
  }

  const track  = TRACKS[active];
  const fmt    = sec => `${Math.floor(sec / 60)}:${String(Math.floor(sec % 60)).padStart(2, "0")}`;
  const curSec = duration ? progress * duration : 0;
  const durStr = duration ? fmt(duration) : track.dur;

  return (
    <section className="section-night violet" id="excerpts" data-screen-label="03 Excerpts">
      <div className="slate-num">Acte II — Extraits</div>
      <div className="wrap">
        <div className="section-head">
          <span className="num" style={{ color: "#cfc4a9" }}>03 ·</span>
          <h2 style={{ color: "var(--bone)" }}>Extraits <span className="it" style={{ color: "#d6c6f5" }}>musicaux</span></h2>
          <div className="right" style={{ color: "var(--off)" }}>
            Cinq pièces sélectionnées réalisées en 2025–2026.
            Casque recommandé&nbsp;pour entendre les détails.
          </div>
        </div>

        <div className="tracks">
          <div className="player">
            <div className="player-top">
              <div className="player-cover">
                {track.cover
                  ? <img src={track.cover} alt={track.name} className="cover-img" />
                  : <span className="label">{track.id.toString().padStart(2, "0")} · cover</span>
                }
              </div>
              <div className="player-info">
                <span className="eyebrow">4D Agency original</span>
                <h3>{track.name}</h3>
                <div className="credit">{track.style}</div>
                <div className="meta">
                  <span>BPM&nbsp;· {track.bpm.replace(" BPM", "")}</span>
                  <span>Key&nbsp;· {track.key}</span>
                  <span></span>
                </div>
              </div>
            </div>

            <div className="waveform" onClick={seekOnWaveform} style={{ cursor: "pointer" }}>
              {bars.map((h, i) => (
                <span key={i}
                  className={"bar " + (i / bars.length < progress ? "played" : "")}
                  style={{ height: `${h * 100}%` }} />
              ))}
            </div>

            <div className="transport">
              <button className="play-btn" onClick={togglePlay} aria-label="play">
                {playing
                  ? <svg className="ico" viewBox="0 0 14 14"><rect x="2" y="1" width="3.5" height="12" fill="currentColor" /><rect x="8.5" y="1" width="3.5" height="12" fill="currentColor" /></svg>
                  : <svg className="ico" viewBox="0 0 14 14"><polygon points="2,1 13,7 2,13" fill="currentColor" /></svg>
                }
              </button>
              <span className="time">{fmt(curSec)} / {durStr}</span>
              <span className="time" style={{ marginLeft: "auto", color: "var(--violet)" }}>
                {playing ? "● PLAYING" : "○ PAUSED"}
              </span>
            </div>
          </div>

          <div className="track-list">
            {TRACKS.map((t, i) => (
              <div key={t.id}
                className={"track-row " + (i === active ? "active" : "")}
                onClick={() => selectTrack(i)}>
                <span className="idx">{String(i + 1).padStart(2, "0")}</span>
                <span>
                  <div className="name">{t.name}</div>
                  <div className="style">{t.style}</div>
                </span>
                <span className="bpm">{t.bpm}</span>
                <span className="dur">{t.dur}</span>
                <span style={{ fontFamily: "var(--f-mono)", fontSize: 11, color: "var(--off)", opacity: .5 }}>
                  {i === active && playing ? "▶" : ""}
                </span>
              </div>
            ))}
          </div>
        </div>

        <div style={{ marginTop: 60, display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 24 }}>
          <div style={{ fontFamily: "var(--f-serif)", fontStyle: "italic", fontSize: 18, color: "var(--off)", maxWidth: 520 }}>
            Chaque extrait présenté est une composition originale créée pour cette démo et n’a pas encore été exploité dans une production.
          </div>
          <div style={{ display: "flex", gap: 14 }}>
            <a className="btn ghost" href="mailto:contact@4dmusicagency.com" style={{ color: "var(--bone)" }}>
              Démarrer notre collaboration
            </a>
            <a className="btn primary" href="#contact">
              Demander un devis
            </a>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ============================================================
   MANIFESTO
   ============================================================ */
function Manifesto() {
  return (
    <section className="section-paper" id="manifesto" data-screen-label="04 Manifesto">
      <div className="slate-num">Acte III — Qui sommes-nous</div>
      <div className="wrap">
        <div className="section-head">
          <span className="num">04 ·</span>
          <h2>Manifeste <span className="it">- qui sommes-nous</span></h2>
          <div className="right">
            Deux sensibilités, une même exigence : composer des musiques qui portent l’identité et l’émotion de votre projet.
          </div>
        </div>

        <div className="manifesto-grid">
          <div className="manifesto-image">
            <img src="uploads/studio.jpg" alt="4D Agency — portrait studio" className="manifesto-photo" />
            <span className="corner tl"></span>
            <span className="corner tr"></span>
            <span className="corner bl"></span>
            <span className="corner br"></span>
            <div className="caption">
              <span>Plate I</span>
              <span>4D Agency</span>
            </div>
          </div>

          <div className="manifesto-body">
            <p className="lead">
              4D&nbsp;Agency est née d’une amitié&nbsp;:
              celle d’un compositeur formé au conservatoire et d’un passionné de
              création sonore moderne. Deux écoles, une même conviction:
              <em> utiliser les nouveaux outils de production musicale au service de la composition, jamais à son détriment.</em>
            </p>

            <p>
              Nous composons des bandes-son immersives et thématiques pour l’image et l’interactif : films, séries, documentaires et jeux vidéo. Chaque projet est traité comme une œuvre singulière, avec son propre rythme, ses tensions et ses silences.
            </p>

            <div className="pullquote">
              « Dans un projet audiovisuel, la musique devient indissociable du projet et doit être portée par une ambition à sa hauteur. »
              <em>- principe fondateur, 4D Agency</em>
            </div>

            <p>
              Notre approche allie rigueur de composition, créativité et communication : échanges réguliers, délais clairs et retours intégrés à chaque étape du projet.
            </p>

            <div className="manifesto-signature">
              <div className="sig">
                Florian Nephtali
                <small>COMPOSITEUR · CO-FONDATEUR</small>
              </div>
              <div className="sig">
                Ayemen El Hor
                <small>COMPOSITEUR · CO-FONDATEUR</small>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>);

}

/* ============================================================
   WHY US
   ============================================================ */
function WhyUs() {
  const benefits = [
  {
    n: "01", title: "Identité sonore cohérente",
    body: "Un thème principal et des variations pensées pour marquer durablement l’univers et la mémoire du public.",
    ico: <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" /><circle cx="12" cy="12" r="5" /><circle cx="12" cy="12" r="1.5" fill="currentColor" /></svg>
  },
  {
    n: "02", title: "Collaboration fluide",
    body: "Un seul point de contact, des notes structurées, un cloud partagé. Vos retours sont intégrés en jours, pas en semaines.",
    ico: <svg viewBox="0 0 24 24"><path d="M4 8h16M4 12h10M4 16h13" /></svg>
  },
  {
    n: "03", title: "Production rapide & structurée",
    body: "Un planning découpé en jalons clairs. Pas de surprise, pas de débordement.",
    ico: <svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" /><path d="M3 9h18M8 4v3M16 4v3" /></svg>
  },
  {
    n: "04", title: "Qualité audio pro",
    body: "Des banques de sons et effets haut de gamme au service d’une production sonore riche et précise.",
    ico: <svg viewBox="0 0 24 24"><path d="M4 12c2 0 2-6 4-6s2 12 4 12 2-12 4-12 2 6 4 6" /></svg>
  }];

  return (
    <section className="section-brutal" id="why" data-screen-label="05 Why us">
      <div className="slate-num">Acte IV — Pourquoi 4D</div>
      <div className="wrap">
        <div className="section-head">
          <span className="num" style={{ color: "var(--off)" }}>05 ·</span>
          <h2>Pourquoi <span className="it">4D Agency</span></h2>
          <div className="right">
           Ce que vous gagnez en nous confiant la bande-son de votre projet.
          </div>
        </div>

        <div className="benefits">
          {benefits.map((b) =>
          <article className="benefit" key={b.n}>
              <span className="icon">{b.ico}</span>
              <span className="num">{b.n}</span>
              <h4>{b.title}</h4>
              <p>{b.body}</p>
            </article>
          )}
        </div>
            {/*
        <div className="split-row">
          <div className="testimonial">
            <div className="quote-mark">“</div>
            <p>
              4D a su trouver la voix de notre monde dès la première démo.
              Leur thème principal a survécu à six mois d’itérations
              gameplay&nbsp;— et c’est lui qu’on entend dans la bande-annonce.
            </p>
            <div className="byline">
              <span className="avatar">CN</span>
              <span className="who">Camille Noiret</span>
              <span className="what">· Creative Director, Studio Veilleur</span>
            </div>
          </div>
          <div className="stats">
            <div className="stat">
              <span className="v">98<small>%</small></span>
              <span className="l">Projets livrés dans les délais</span>
            </div>
            <div className="stat">
              <span className="v">12<small>h</small></span>
              <span className="l">Temps de réponse moyen</span>
            </div>
            <div className="stat">
              <span className="v">2 / 8</span>
              <span className="l">Slots projets ouverts ce trimestre</span>
            </div>
          </div>
        </div>
        */}
      </div>
    </section>);

}

/* ============================================================
   CTA BAND
   ============================================================ */
function CtaBand() {
  return (
    <section className="cta-band" id="contact" data-screen-label="06 Contact">
      <div className="light"></div>
      <div className="wrap">
        <div className="cta-content">
          <span className="eyebrow">Acte V · La rencontre</span>
          <h2>
            Lançons votre<br />
            <span className="it">bande-son</span>.
          </h2>
          <p>
           Un appel de 30 minutes suffit pour savoir si nous pouvons collaborer. Apportez vos références, votre univers ou vos doutes, nous vous aiderons à définir une direction musicale claire pour votre projet.
          </p>
          <div className="cta-actions">
            <a className="btn primary" href="mailto:contact@4dmusicagency.com">
              Parlez-nous de votre projet <span className="arrow">→</span>
            </a>
            <a className="btn ghost" href="https://calendar.app.google/ZvgjEjiUeEvqdv347" target="_blank" rel="noopener noreferrer" style={{ color: "var(--bone)" }}>
              Réserver un échange
            </a>
            <a className="btn ghost" href="mailto:contact@4dmusicagency.com" style={{ color: "var(--bone)" }}>
              Lancer une collaboration
            </a>
          </div>
        </div>
      </div>
    </section>);

}

/* ============================================================
   FOOTER
   ============================================================ */
function Footer() {
  return (
    <footer className="footer">
      <div className="wrap">
        <div className="footer-top">
          <div>
            <div className="footer-brand">
              <img src="uploads/4d-logo.svg" alt="4D Agency" className="footer-logo" />
              <small>Compositeurs pour l'image.<br /></small>
            </div>
          </div>
          <div>
            <h5>Studio</h5>
            <ul>
              <li>Processus</li>
              <li>Manifeste</li>
              <li>Compositeurs</li>
            </ul>
          </div>
          <div>
            <h5>Écoutes</h5>
            <ul>
              <li>Extraits récents</li>
              <li>Youtube</li>
              <li>Spotify</li>
            </ul>
          </div>
          <div>
            <h5>Contact</h5>
            <ul>
              <li><a href="mailto:contact@4dmusicagency.com">contact@4dmusicagency.com</a></li>
              <li><a href="tel:+33695415254">+33 (0)6 95 41 52 54</a></li>
            </ul>
          </div>
        </div>
        <div className="footer-bottom">
          <span>© 2026 · 4D Agency - Tous droits réservés</span>
          <span>v1.0</span>
        </div>
      </div>
    </footer>);

}

/* ============================================================
   APP
   ============================================================ */
function App() {
  const TweaksUI = window.TweaksUI;
  return (
    <>
      <Nav />
      <Hero />
      <Marquee />
      <Process />
      <Excerpts />
      <Manifesto />
      <WhyUs />
      <CtaBand />
      <Footer />
      {TweaksUI ? <TweaksUI /> : null}
    </>);

}

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