// scenes.jsx — "Conception créative" — a kinetic motion study.
// Story arc: a single spark becomes a line, a form, an idea, then a full
// composition — each French word literally drawn by the animation that names it.

const C = {
  bg:       '#14110D',
  cream:    '#F0EDE4',
  creamDim: 'rgba(240,237,228,0.50)',
  clay:     '#D9805A',
  clayDeep: '#C8643C',
  ink:      '#1B1813',
  inkDim:   'rgba(27,24,19,0.55)',
  hair:     'rgba(240,237,228,0.85)',
};
const DISPLAY = "'Instrument Serif', Georgia, 'Times New Roman', serif";
const MONO    = "'JetBrains Mono', ui-monospace, SFMono-Regular, monospace";

const E = window.Easing;
const _clamp = window.clamp;

// trapezoidal opacity envelope
function trap(t, start, end, fin = 0.45, fout = 0.45, ease = E.easeInOutSine) {
  if (t <= start || t >= end) return 0;
  if (t < start + fin) return ease(_clamp((t - start) / fin, 0, 1));
  if (t > end - fout) return ease(_clamp((end - t) / fout, 0, 1));
  return 1;
}
const lerp = (a, b, t) => a + (b - a) * t;

// composition anchors
const MX = 960, MY = 460;   // mark center
const LABEL_Y = 296;

// ── Camera ───────────────────────────────────────────────────────────────────
function Camera({ children }) {
  const t = useTime();
  let s = 1 + 0.012 * E.easeInOutSine(_clamp(t / 11.5, 0, 1));
  if (t > 11.5) s = 1.012 + 0.052 * E.easeInOutSine(_clamp((t - 11.5) / 4.2, 0, 1));
  return (
    <div style={{
      position: 'absolute', inset: 0,
      transformOrigin: '960px 540px',
      transform: `scale(${s})`,
      willChange: 'transform',
    }}>
      {children}
    </div>
  );
}

// ── Background: drifting dot grid ────────────────────────────────────────────
function BackgroundLayer() {
  const t = useTime();
  const tx = (t * 4) % 64, ty = (t * 2.4) % 64;
  return (
    <svg width="1920" height="1080" style={{ position: 'absolute', inset: 0 }}>
      <defs>
        <pattern id="cc-grid" width="64" height="64" patternUnits="userSpaceOnUse"
          patternTransform={`translate(${tx} ${ty}) rotate(${t * 0.35})`}>
          <circle cx="2" cy="2" r="1.1" fill="rgba(240,237,228,0.15)" />
        </pattern>
      </defs>
      <rect width="1920" height="1080" fill="url(#cc-grid)" />
    </svg>
  );
}

// ── Warm glow behind the action ──────────────────────────────────────────────
function CenterGlow() {
  const t = useTime();
  const breathe = 0.5 + 0.5 * Math.sin(t * 1.15);
  const sc = 1 + 0.08 * Math.sin(t * 0.8);
  const op = trap(t, 0.2, 16.2, 1.2, 0.6) * (0.42 + 0.28 * breathe);
  return (
    <div style={{
      position: 'absolute', left: MX, top: 540,
      width: 1040, height: 1040,
      transform: `translate(-50%,-50%) scale(${sc})`,
      background: 'radial-gradient(circle, rgba(217,128,90,0.22), rgba(217,128,90,0) 62%)',
      opacity: op, filter: 'blur(8px)', pointerEvents: 'none',
    }} />
  );
}

// ── The evolving central mark: spark → line → triangle → constellation ───────
function MotionMark() {
  const t = useTime();
  if (t > 12.0) return null;

  // central dot (the through-line of the whole piece)
  const dotOp = trap(t, 0.5, 11.9, 0.6, 0.45);
  const pulse = t < 3.6 ? 2.6 * Math.sin(t * 4.2) : 0;
  const dotR = 8 + Math.max(0, pulse);

  // spark flare
  const flareT = _clamp((t - 2.7) / 1.0, 0, 1);
  const flareR = lerp(10, 150, E.easeOutCubic(flareT));
  const flareOp = (t > 2.6 && t < 3.8) ? (1 - flareT) * 0.9 : 0;

  // line motif
  const lineOp = trap(t, 3.5, 6.3, 0.4, 0.5);
  const hl = 300 * E.easeOutCubic(_clamp((t - 3.5) / 1.6, 0, 1));

  // triangle motif
  const triOp = trap(t, 6.1, 8.8, 0.45, 0.5);
  const drawT = E.easeInOutCubic(_clamp((t - 6.15) / 1.5, 0, 1));
  const W = 420, H = 260;
  const v = [
    [MX - W / 2, MY + H / 2],
    [MX, MY - H / 2],
    [MX + W / 2, MY + H / 2],
  ];
  const side = Math.hypot(W / 2, H);
  const perim = W + 2 * side;
  const triWobble = Math.sin(t * 0.9) * 1.6;

  // constellation motif
  const conOp = trap(t, 8.6, 11.7, 0.45, 0.55);
  const ringR = 172;
  const ang0 = (t - 8.6) * 0.17;
  const nodes = [];
  for (let i = 0; i < 6; i++) {
    const a = ang0 + (i / 6) * Math.PI * 2;
    const na = _clamp((t - (8.6 + i * 0.07)) / 0.45, 0, 1);
    nodes.push({
      x: MX + Math.cos(a) * ringR,
      y: MY + Math.sin(a) * ringR,
      op: E.easeOutCubic(na),
      clay: i === 1 || i === 4,
    });
  }

  return (
    <svg width="1920" height="1080" style={{ position: 'absolute', inset: 0, overflow: 'visible' }}>
      {/* flare */}
      <circle cx={MX} cy={MY} r={flareR} fill="none" stroke={C.clay}
        strokeWidth="2" opacity={flareOp} />

      {/* line */}
      <g opacity={lineOp}>
        <line x1={MX - hl} y1={MY} x2={MX + hl} y2={MY} stroke={C.hair} strokeWidth="2" />
        <circle cx={MX - hl} cy={MY} r="3.5" fill={C.cream} />
        <circle cx={MX + hl} cy={MY} r="3.5" fill={C.cream} />
      </g>

      {/* triangle */}
      <g opacity={triOp} transform={`rotate(${triWobble} ${MX} ${MY})`}>
        <polygon
          points={v.map(p => p.join(',')).join(' ')}
          fill="none" stroke={C.hair} strokeWidth="2"
          strokeDasharray={perim} strokeDashoffset={perim * (1 - drawT)}
          strokeLinejoin="round"
        />
        {v.map((p, i) => (
          <circle key={i} cx={p[0]} cy={p[1]} r="3" fill={C.clay}
            opacity={drawT > (i / 3) ? 1 : 0} />
        ))}
      </g>

      {/* constellation */}
      <g opacity={conOp}>
        {nodes.map((n, i) => (
          <g key={i} opacity={n.op}>
            <line x1={MX} y1={MY} x2={n.x} y2={n.y}
              stroke="rgba(240,237,228,0.32)" strokeWidth="1" />
            <circle cx={n.x} cy={n.y} r={n.clay ? 6 : 5}
              fill={n.clay ? C.clay : C.cream} />
          </g>
        ))}
      </g>

      {/* persistent central dot + soft halo */}
      <circle cx={MX} cy={MY} r={dotR + 9} fill={C.clay} opacity={dotOp * 0.18} />
      <circle cx={MX} cy={MY} r={dotR} fill={C.cream} opacity={dotOp} />
    </svg>
  );
}

// ── Orchestration: the idea radiates into a full composition ─────────────────
function mulberry32(a) {
  return function () {
    a |= 0; a = a + 0x6D2B79F5 | 0;
    let t = Math.imul(a ^ a >>> 15, 1 | a);
    t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}
const FIELD = (() => {
  const rnd = mulberry32(11);
  const cx = 960, cy = 540;
  const types = ['dot', 'ring', 'tri', 'sq', 'seg', 'plus'];
  const items = [];
  for (let i = 0; i < 44; i++) {
    let x, y, tries = 0;
    do { x = 210 + rnd() * 1500; y = 150 + rnd() * 780; tries++; }
    while (tries < 24 && x > 545 && x < 1375 && y > 455 && y < 645);
    items.push({
      x, y,
      type: types[(rnd() * types.length) | 0],
      size: 13 + rnd() * 30,
      clay: rnd() < 0.2,
      rot: rnd() * 360,
      spin: (rnd() - 0.5) * 30,
      dax: (rnd() - 0.5), day: (rnd() - 0.5),
      dist: Math.hypot(x - cx, y - cy),
    });
  }
  const maxD = Math.max(...items.map(i => i.dist));
  items.forEach(it => { it.delay = it.dist / maxD; });
  return items;
})();

function FieldShape({ type, size, color, sw }) {
  const s = size;
  switch (type) {
    case 'dot':  return <circle r={s / 2} fill={color} />;
    case 'ring': return <circle r={s / 2} fill="none" stroke={color} strokeWidth={sw} />;
    case 'tri':  return <polygon points={`0,${-s/2} ${s/2},${s/2} ${-s/2},${s/2}`}
                          fill="none" stroke={color} strokeWidth={sw} strokeLinejoin="round" />;
    case 'sq':   return <rect x={-s/2} y={-s/2} width={s} height={s} fill="none"
                          stroke={color} strokeWidth={sw} />;
    case 'seg':  return <line x1={-s/2} y1="0" x2={s/2} y2="0" stroke={color} strokeWidth={sw} />;
    case 'plus': return <g stroke={color} strokeWidth={sw}>
                          <line x1={-s/2} y1="0" x2={s/2} y2="0" />
                          <line x1="0" y1={-s/2} x2="0" y2={s/2} />
                        </g>;
    default: return null;
  }
}

function Orchestration() {
  const t = useTime();
  if (t < 11.4 || t > 16.2) return null;
  const conv = E.easeInCubic(_clamp((t - 15.2) / 0.75, 0, 1)); // collapse to center
  return (
    <svg width="1920" height="1080" style={{ position: 'absolute', inset: 0, overflow: 'visible' }}>
      {FIELD.map((it, i) => {
        const start = 11.6 + it.delay * 1.7;
        const ap = _clamp((t - start) / 0.55, 0, 1);
        if (ap <= 0) return null;
        const sc = E.easeOutBack(ap) * (1 - 0.45 * conv);
        const op = ap * (1 - conv);
        const local = Math.max(0, t - start);
        const x = lerp(it.x + it.dax * local * 7, 960, conv);
        const y = lerp(it.y + it.day * local * 7, 540, conv);
        const rot = it.rot + it.spin * local;
        const col = it.clay ? C.clay : 'rgba(240,237,228,0.78)';
        return (
          <g key={i} transform={`translate(${x} ${y}) rotate(${rot}) scale(${sc})`} opacity={op}>
            <FieldShape type={it.type} size={it.size} color={col} sw={1.6} />
          </g>
        );
      })}
    </svg>
  );
}

// ── Kinetic words ────────────────────────────────────────────────────────────
const WORDS = [
  { t: [1.0, 3.45], text: 'Une étincelle', y: 700, size: 138 },
  { t: [3.7, 5.95], text: 'Une ligne',     y: 700, size: 138 },
  { t: [6.2, 8.45], text: 'Une forme',     y: 700, size: 138 },
  { t: [8.7, 11.4], text: 'Une idée',      y: 700, size: 138 },
  { t: [12.2, 15.45], text: 'Et tout prend forme', y: 540, size: 152 },
];

function Word({ text, start, end, y, size }) {
  const t = useTime();
  if (t < start - 0.1 || t > end + 0.1) return null;
  const chars = text.split('');
  const eout = E.easeInCubic(_clamp((t - (end - 0.45)) / 0.45, 0, 1));
  const groupTy = -eout * 16;
  return (
    <div style={{
      position: 'absolute', left: 960, top: y,
      transform: `translate(-50%,-50%) translateY(${groupTy}px)`,
      fontFamily: DISPLAY, fontSize: size, fontWeight: 400,
      color: C.cream, letterSpacing: '0.005em', lineHeight: 1,
      whiteSpace: 'pre', display: 'flex', willChange: 'transform, opacity',
    }}>
      {chars.map((ch, i) => {
        const cStart = start + i * 0.038;
        const inP = E.easeOutCubic(_clamp((t - cStart) / 0.55, 0, 1));
        const ty = (1 - inP) * 26;
        const blur = (1 - inP) * 7;
        return (
          <span key={i} style={{
            display: 'inline-block',
            transform: `translateY(${ty}px)`,
            opacity: inP * (1 - eout),
            filter: blur > 0.2 ? `blur(${blur}px)` : 'none',
            whiteSpace: 'pre',
          }}>{ch}</span>
        );
      })}
      <span style={{ color: C.clay, opacity: 1 - eout }}>.</span>
    </div>
  );
}

function KineticWords() {
  return (
    <React.Fragment>
      {WORDS.map((w, i) => (
        <Word key={i} text={w.text} start={w.t[0]} end={w.t[1]} y={w.y} size={w.size} />
      ))}
    </React.Fragment>
  );
}

// ── Scene index labels ───────────────────────────────────────────────────────
const LABELS = [
  { t: [1.0, 3.45], n: '01', s: 'ORIGINE' },
  { t: [3.7, 5.95], n: '02', s: 'DIRECTION' },
  { t: [6.2, 8.45], n: '03', s: 'STRUCTURE' },
  { t: [8.7, 11.4], n: '04', s: 'INTENTION' },
  { t: [12.2, 15.45], n: '05', s: 'COMPOSITION' },
];
function Labels() {
  const t = useTime();
  return (
    <React.Fragment>
      {LABELS.map((l, i) => {
        const op = trap(t, l.t[0], l.t[1], 0.4, 0.4);
        if (op <= 0) return null;
        return (
          <div key={i} style={{
            position: 'absolute', left: 960, top: LABEL_Y,
            transform: 'translate(-50%,-50%)',
            fontFamily: MONO, fontSize: 19, fontWeight: 500,
            letterSpacing: '0.34em', opacity: op,
            display: 'flex', gap: '0.85em', alignItems: 'center',
          }}>
            <span style={{ color: C.clay }}>{l.n}</span>
            <span style={{ color: C.creamDim }}>{l.s}</span>
          </div>
        );
      })}
    </React.Fragment>
  );
}

// ── Endplate: reveal of the project name on a cream panel ─────────────────────
function Endplate() {
  const t = useTime();
  if (t < 15.7) return null;
  const panel = E.easeInOutSine(_clamp((t - 15.85) / 0.5, 0, 1));

  // small clay mark with a hairline drawing outward
  const lineHalf = 70 * E.easeOutCubic(_clamp((t - 16.2) / 1.4, 0, 1));
  const markBreathe = 1 + 0.12 * Math.sin(t * 1.6);

  // title
  const titleP = E.easeOutCubic(_clamp((t - 16.35) / 0.8, 0, 1));
  // tagline + rule
  const tagP = E.easeOutCubic(_clamp((t - 17.0) / 0.8, 0, 1));
  const ruleW = 360 * E.easeInOutCubic(_clamp((t - 17.2) / 1.8, 0, 1));

  return (
    <div style={{
      position: 'absolute', inset: 0, background: C.cream,
      opacity: panel, overflow: 'hidden',
    }}>
      {/* faint grid echo on cream */}
      <svg width="1920" height="1080" style={{ position: 'absolute', inset: 0, opacity: 0.5 }}>
        <defs>
          <pattern id="cc-grid2" width="64" height="64" patternUnits="userSpaceOnUse">
            <circle cx="2" cy="2" r="1" fill="rgba(27,24,19,0.10)" />
          </pattern>
        </defs>
        <rect width="1920" height="1080" fill="url(#cc-grid2)" />
      </svg>

      {/* mark */}
      <svg width="1920" height="1080" style={{ position: 'absolute', inset: 0, overflow: 'visible' }}>
        <line x1={960 - lineHalf} y1="402" x2={960 + lineHalf} y2="402"
          stroke={C.clayDeep} strokeWidth="1.5" />
        <circle cx="960" cy="402" r={5.5 * markBreathe} fill={C.clayDeep} />
      </svg>

      {/* title */}
      <div style={{
        position: 'absolute', left: 960, top: 512,
        transform: `translate(-50%,-50%) translateY(${(1 - titleP) * 18}px)`,
        opacity: titleP, fontFamily: DISPLAY, fontSize: 120, color: C.ink,
        letterSpacing: '0.005em', whiteSpace: 'pre', lineHeight: 1,
      }}>
        Conception <span style={{ fontStyle: 'italic' }}>créative</span>
      </div>

      {/* tagline */}
      <div style={{
        position: 'absolute', left: 960, top: 612,
        transform: 'translate(-50%,-50%)', opacity: tagP,
        fontFamily: MONO, fontSize: 18, fontWeight: 500,
        letterSpacing: '0.4em', color: C.inkDim,
      }}>
        ÉTUDE DE MOUVEMENT · MMXXVI
      </div>

      {/* drawing rule */}
      <div style={{
        position: 'absolute', left: 960, top: 666,
        transform: 'translateX(-50%)', width: ruleW, height: 1.5,
        background: C.clayDeep, opacity: 0.8,
      }} />
    </div>
  );
}

// ── Vignette overlay (dark scenes only) ──────────────────────────────────────
function Vignette() {
  const t = useTime();
  const op = 1 - E.easeInOutSine(_clamp((t - 15.6) / 0.5, 0, 1));
  if (op <= 0) return null;
  return (
    <div style={{
      position: 'absolute', inset: 0, pointerEvents: 'none', opacity: op,
      background: 'radial-gradient(ellipse 75% 70% at 50% 50%, rgba(0,0,0,0) 42%, rgba(0,0,0,0.55) 100%)',
    }} />
  );
}

Object.assign(window, {
  C, Camera, BackgroundLayer, CenterGlow, MotionMark,
  Orchestration, KineticWords, Labels, Endplate, Vignette,
});
