/* Three-panel diagram: geometry → lightcurve → transmission spectrum */
function TransitAnimation({ starColor = "#f6c478", planetColor = "#1a1a1a", bg = "transparent", lineColor = "#1a1a1a", mutedColor = "#9a938a", label = true, height = 380, fontFamily = "inherit" }) {
  // Canvas: generous padding so text never touches edges. Larger overall for readability.
  const W = 1100, H = height;
  const [t, setT] = React.useState(0);
  React.useEffect(() => {
    let raf, start;
    const tick = (ts) => {
      if (!start) start = ts;
      setT(((ts - start) / 7000) % 1);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  // Layout: 3 panels across, with explicit plot boxes. Labels sit above, axes inside.
  const topLabelY = 32;           // panel kicker baseline
  const topTitleY = 62;           // panel title baseline
  const plotTop = 108;            // top of plot area (extra breathing room under titles)
  const plotBot = H - 72;         // bottom of plot area (leaves room for x-axis label)
  const plotH = plotBot - plotTop;

  const gap = 40;
  const panelW = (W - gap * 2) / 3;
  const p1 = { x: 0,                       w: panelW };
  const p2 = { x: panelW + gap,            w: panelW };
  const p3 = { x: (panelW + gap) * 2,      w: panelW };

  // ── Panel 1: geometry
  const cxStar = p1.x + panelW / 2;
  const cyStar = plotTop + plotH / 2;
  const rStar = Math.min(plotH * 0.28, 60);
  const planetR = rStar * 0.3;
  const travel = rStar * 4.2;
  const planetX = cxStar - travel / 2 + t * travel;
  const d = Math.abs(planetX - cxStar);
  let depth = 0;
  if (d < rStar + planetR) {
    depth = d < rStar - planetR ? 1 : 1 - (d - (rStar - planetR)) / (2 * planetR);
  }

  // ── Panel 2: lightcurve — plot box inside p2 with margins
  const lcLeft = p2.x + 60, lcRight = p2.x + p2.w - 20;
  const lcTop = plotTop + 10, lcBot = plotBot - 24;
  const N = 180;
  const lc = [];
  for (let i = 0; i < N; i++) {
    const tt = i / (N - 1);
    const xi = cxStar - travel / 2 + tt * travel;
    const di = Math.abs(xi - cxStar);
    let de = 0;
    if (di < rStar + planetR) de = di < rStar - planetR ? 1 : 1 - (di - (rStar - planetR)) / (2 * planetR);
    const flux = 1 - de * 0.16;
    const px = lcLeft + tt * (lcRight - lcLeft);
    // Map flux [0.84, 1.00] → [lcBot, lcTop]
    const py = lcTop + (1 - (flux - 0.84) / 0.16) * (lcBot - lcTop);
    lc.push([px, py]);
  }
  const lcPath = lc.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" ");
  const cIdx = Math.round(t * (N - 1));
  const cursor = lc[cIdx];

  // ── Panel 3: spectrum
  const spLeft = p3.x + 70, spRight = p3.x + p3.w - 20;
  const spTop = plotTop + 10, spBot = plotBot - 24;
  const specN = 120;
  const spec = [];
  const gauss = (x, mu, s) => Math.exp(-0.5 * ((x - mu) / s) ** 2);
  for (let i = 0; i < specN; i++) {
    const wl = i / (specN - 1);
    let dep = 0.25
      + 0.35 * gauss(wl, 0.18, 0.12)
      + 0.55 * gauss(wl, 0.42, 0.08)
      + 0.70 * gauss(wl, 0.62, 0.06)
      + 0.45 * gauss(wl, 0.82, 0.09)
      + 0.18 * Math.exp(-wl * 3);
    const px = spLeft + wl * (spRight - spLeft);
    const py = spTop + (1 - dep) * (spBot - spTop);
    spec.push([px, py, wl]);
  }
  const specPath = spec.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" ");
  const errPts = [14, 30, 50, 68, 84, 100].map(i => spec[i]);

  const axisCol = mutedColor;

  // Font sizes (tuned up from 9/11 to 13/16)
  const FS_KICKER = 16;
  const FS_TITLE  = 24;
  const FS_AXIS   = 17;
  const FS_BAND   = 17;

  const kicker = (x, text) => (
    <text x={x} y={topLabelY} fontSize={FS_KICKER} fill={axisCol}
          style={{ letterSpacing: "0.16em", textTransform: "uppercase", fontWeight: 500 }}>{text}</text>
  );
  const title = (x, text) => (
    <text x={x} y={topTitleY} fontSize={FS_TITLE} fill={lineColor}
          style={{ letterSpacing: "0.01em" }}>{text}</text>
  );

  // Arrow between panels, anchored at vertical center of plot region
  const connectorY = (plotTop + plotBot) / 2;
  const arrow = (x1, x2) => (
    <g stroke={axisCol} strokeWidth="1" fill="none" opacity="0.55">
      <line x1={x1} y1={connectorY} x2={x2 - 8} y2={connectorY} />
      <path d={`M ${x2 - 8} ${connectorY - 4} L ${x2} ${connectorY} L ${x2 - 8} ${connectorY + 4}`} />
    </g>
  );

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: "block", background: bg, overflow: "visible", fontFamily }}>
      <defs>
        <radialGradient id="starGrad2" cx="0.5" cy="0.5" r="0.5">
          <stop offset="0%" stopColor={starColor} stopOpacity="1" />
          <stop offset="65%" stopColor={starColor} stopOpacity="0.95" />
          <stop offset="100%" stopColor={starColor} stopOpacity="0" />
        </radialGradient>
      </defs>

      {/* ── Panel 1 : Geometry ── */}
      {label && kicker(p1.x, "Observe")}
      {label && title(p1.x, "Transit")}

      <circle cx={cxStar} cy={cyStar} r={rStar * 1.7} fill="url(#starGrad2)" opacity="0.35" />
      <circle cx={cxStar} cy={cyStar} r={rStar} fill={starColor} />
      <circle cx={cxStar} cy={cyStar} r={rStar} stroke="rgba(0,0,0,0.08)" strokeWidth="1" fill="none" />
      <line x1={cxStar - travel / 2} y1={cyStar} x2={cxStar + travel / 2} y2={cyStar}
            stroke={axisCol} strokeWidth="0.6" strokeDasharray="3 5" opacity="0.45" />
      <circle cx={planetX} cy={cyStar} r={planetR} fill={planetColor} />
      {depth > 0.01 && (
        <circle cx={planetX} cy={cyStar} r={planetR + 4} fill="none"
                stroke={lineColor} strokeWidth="1.3" opacity={0.35 + depth * 0.5} />
      )}

      {/* Connector 1 → 2 */}
      {arrow(p1.x + p1.w - 10, p2.x + 4)}

      {/* ── Panel 2 : Lightcurve ── */}
      {label && kicker(p2.x, "Measure")}
      {label && title(p2.x, "Light curve")}

      {/* axes */}
      <line x1={lcLeft} y1={lcTop - 8} x2={lcLeft} y2={lcBot} stroke={axisCol} strokeWidth="0.9" />
      <line x1={lcLeft} y1={lcBot} x2={lcRight + 4} y2={lcBot} stroke={axisCol} strokeWidth="0.9" />

      {/* y-axis ticks & labels */}
      {[0, 0.5, 1].map(u => {
        const y = lcTop + u * (lcBot - lcTop);
        const v = (1 - u * 0.16).toFixed(2);
        return (
          <g key={u}>
            <line x1={lcLeft - 4} y1={y} x2={lcLeft} y2={y} stroke={axisCol} strokeWidth="0.9" />
            <text x={lcLeft - 8} y={y + 4} textAnchor="end" fontSize={FS_AXIS} fill={axisCol}
                  style={{ fontVariantNumeric: "tabular-nums" }}>{v}</text>
          </g>
        );
      })}
      {/* baseline dashed at 1.00 */}
      <line x1={lcLeft} y1={lcTop} x2={lcRight} y2={lcTop} stroke={axisCol} strokeWidth="0.5" strokeDasharray="2 4" opacity="0.4" />

      {/* axis titles */}
      <text x={lcLeft} y={lcTop - 18} fontSize={FS_AXIS} fill={axisCol}
            style={{ letterSpacing: "0.08em", textTransform: "uppercase" }}>Relative flux</text>
      <text x={lcRight} y={lcBot + 24} textAnchor="end" fontSize={FS_AXIS} fill={axisCol}
            style={{ letterSpacing: "0.08em", textTransform: "uppercase" }}>Time →</text>

      <path d={lcPath} fill="none" stroke={lineColor} strokeWidth="1.6" />
      {cursor && <circle cx={cursor[0]} cy={cursor[1]} r="4" fill={lineColor} />}

      {/* Connector 2 → 3 */}
      {arrow(p2.x + p2.w - 10, p3.x + 4)}

      {/* ── Panel 3 : Spectrum ── */}
      {label && kicker(p3.x, "Retrieve")}
      {label && title(p3.x, "Transmission spectrum")}

      <line x1={spLeft} y1={spTop - 8} x2={spLeft} y2={spBot} stroke={axisCol} strokeWidth="0.9" />
      <line x1={spLeft} y1={spBot} x2={spRight + 4} y2={spBot} stroke={axisCol} strokeWidth="0.9" />

      {/* y ticks */}
      {[["0.72%", 0], ["0.66%", 0.5], ["0.60%", 1]].map(([v, u], i) => {
        const y = spTop + u * (spBot - spTop);
        return (
          <g key={i}>
            <line x1={spLeft - 4} y1={y} x2={spLeft} y2={y} stroke={axisCol} strokeWidth="0.9" />
            <text x={spLeft - 8} y={y + 4} textAnchor="end" fontSize={FS_AXIS} fill={axisCol}
                  style={{ fontVariantNumeric: "tabular-nums" }}>{v}</text>
          </g>
        );
      })}

      {/* x ticks for wavelength */}
      {[[0.05, ""], [0.95, ""]].map(([u, v], i) => {
        const x = spLeft + u * (spRight - spLeft);
        return (
          <g key={i}>
            <line x1={x} y1={spBot} x2={x} y2={spBot + 4} stroke={axisCol} strokeWidth="0.9" />
          </g>
        );
      })}

      <text x={spLeft} y={spTop - 18} fontSize={FS_AXIS} fill={axisCol}
            style={{ letterSpacing: "0.08em", textTransform: "uppercase" }}>(Rₚ/R★)²</text>
      <text x={spRight} y={spBot + 28} textAnchor="end" fontSize={FS_AXIS} fill={axisCol}
            style={{ letterSpacing: "0.08em", textTransform: "uppercase" }}>Wavelength →</text>

      <path d={specPath} fill="none" stroke={lineColor} strokeWidth="1.6" opacity="0.95" />

      {/* Molecular band labels — placed above peaks */}
      {[[22, "H₂O"], [50, "H₂O"], [74, "CO₂"], [98, "CO"]].map(([i, lbl], k) => {
        const p = spec[i];
        return (
          <g key={k}>
            <line x1={p[0]} y1={p[1] - 6} x2={p[0]} y2={p[1] - 14} stroke={axisCol} strokeWidth="0.6" opacity="0.6" />
            <text x={p[0]} y={p[1] - 18} textAnchor="middle" fontSize={FS_BAND} fill={lineColor} opacity="0.9"
                  style={{ letterSpacing: "0.02em" }}>{lbl}</text>
          </g>
        );
      })}

      {errPts.map((p, i) => (
        <g key={i} stroke={lineColor} strokeWidth="1" opacity="0.65">
          <line x1={p[0]} y1={p[1] - 6} x2={p[0]} y2={p[1] + 6} />
          <circle cx={p[0]} cy={p[1]} r="2.2" fill={lineColor} stroke="none" />
        </g>
      ))}
    </svg>
  );
}

Object.assign(window, { TransitAnimation });
