// Reusable visual primitives for the Yohann Escher hero motion.
// Brand tokens, cards, lines, glows, dashboards.

const YE = {
  bgDark: '#101214',
  bgDeeper: '#0B0D0E',
  card: '#191C1E',
  cardLight: '#202325',
  cardElev: '#23272A',
  cream: '#F7EFE6',
  creamSoft: '#FFF8F0',
  beige: '#E6D9C7',
  textMuted: 'rgba(247,239,230,0.62)',
  textDim: 'rgba(247,239,230,0.38)',
  border: 'rgba(255,255,255,0.10)',
  borderStrong: 'rgba(255,255,255,0.16)',
  orange: '#FF6B1A',
  orangeDeep: '#E9580C',
  orangeSoft: '#FFE4D2',
  orangeGlow: 'rgba(255,107,26,0.35)',
};

const SANS = "'Manrope', 'Inter', system-ui, -apple-system, sans-serif";
const SERIF = "'Instrument Serif', 'Cormorant Garamond', Georgia, serif";
const MONO = "'JetBrains Mono', ui-monospace, monospace";

// ── Vignette / atmospheric background ──────────────────────────────────────
function StageBackdrop() {
  return (
    <React.Fragment>
      <div style={{
        position: 'absolute', inset: 0,
        background: `radial-gradient(ellipse 60% 50% at 50% 50%, rgba(255,107,26,0.06), transparent 70%), ${YE.bgDark}`,
      }} />
      {/* fine grid */}
      <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: 0.18 }}>
        <defs>
          <pattern id="grid" width="64" height="64" patternUnits="userSpaceOnUse">
            <path d="M 64 0 L 0 0 0 64" fill="none" stroke="rgba(255,255,255,0.05)" strokeWidth="1"/>
          </pattern>
          <radialGradient id="gridFade" cx="50%" cy="50%" r="60%">
            <stop offset="0%" stopColor="white" stopOpacity="0.6"/>
            <stop offset="100%" stopColor="white" stopOpacity="0"/>
          </radialGradient>
          <mask id="gridMask"><rect width="100%" height="100%" fill="url(#gridFade)"/></mask>
        </defs>
        <rect width="100%" height="100%" fill="url(#grid)" mask="url(#gridMask)"/>
      </svg>
      {/* vignette */}
      <div style={{
        position: 'absolute', inset: 0,
        background: 'radial-gradient(ellipse 80% 70% at 50% 50%, transparent 50%, rgba(0,0,0,0.55) 100%)',
        pointerEvents: 'none',
      }}/>
    </React.Fragment>
  );
}

// ── Pulsing orange dot ──────────────────────────────────────────────────────
function PulseDot({ x, y, size = 14, intensity = 1, label }) {
  const t = useTime();
  const pulse = 0.5 + 0.5 * Math.sin(t * 3.2);
  const ringScale = 1 + (t * 1.6) % 2; // continuous ripple
  const ringOpacity = Math.max(0, 1 - ((t * 1.6) % 2) / 2) * 0.5 * intensity;

  return (
    <div style={{ position: 'absolute', left: x, top: y, transform: 'translate(-50%, -50%)' }}>
      <div style={{
        position: 'absolute', left: '50%', top: '50%',
        width: size * 4, height: size * 4,
        marginLeft: -size * 2, marginTop: -size * 2,
        borderRadius: '50%',
        border: `1.5px solid ${YE.orange}`,
        transform: `scale(${ringScale})`,
        opacity: ringOpacity,
      }}/>
      <div style={{
        width: size, height: size,
        borderRadius: '50%',
        background: YE.orange,
        boxShadow: `0 0 ${20 + pulse * 24}px ${pulse * 6}px ${YE.orangeGlow}`,
        transform: `scale(${0.85 + pulse * 0.2})`,
      }}/>
      {label && (
        <div style={{
          position: 'absolute', left: '50%', top: size + 14,
          transform: 'translateX(-50%)',
          fontFamily: MONO, fontSize: 10, letterSpacing: '0.18em',
          color: YE.textMuted, textTransform: 'uppercase', whiteSpace: 'nowrap',
        }}>{label}</div>
      )}
    </div>
  );
}

// ── Floating chip / module card ────────────────────────────────────────────
function ModuleChip({
  x, y, label, icon,
  width = 'auto',
  delay = 0, life = Infinity,
  state = 'normal', // normal | broken | active | dim
  driftX = 0, driftY = 0, driftFreq = 0.5,
  scale = 1,
  highlight = false,
}) {
  const t = useTime();
  const localT = Math.max(0, t - delay);
  const enter = clamp(localT / 0.5, 0, 1);
  const eased = Easing.easeOutCubic(enter);
  const lifeT = life === Infinity ? 1 : clamp((t - delay) / life, 0, 1);
  const exit = life === Infinity ? 1 : (lifeT > 0.85 ? 1 - (lifeT - 0.85) / 0.15 : 1);

  // gentle float
  const fx = Math.sin(t * driftFreq + x * 0.01) * driftX;
  const fy = Math.cos(t * driftFreq * 0.9 + y * 0.01) * driftY;

  const broken = state === 'broken';
  const dim = state === 'dim';
  const active = state === 'active' || highlight;

  const flicker = broken ? (0.55 + 0.45 * Math.sin(t * 9 + x)) : 1;
  const opacity = eased * exit * flicker * (dim ? 0.45 : 1);

  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      transform: `translate(calc(-50% + ${fx}px), calc(-50% + ${fy}px)) scale(${scale * (0.92 + 0.08 * eased)})`,
      opacity,
      display: 'flex', alignItems: 'center', gap: 8,
      padding: '8px 14px',
      background: active ? 'linear-gradient(180deg, #2A201A, #1B1816)' : YE.card,
      border: `1px solid ${active ? 'rgba(255,107,26,0.55)' : YE.border}`,
      borderRadius: 999,
      boxShadow: active
        ? `0 8px 28px rgba(255,107,26,0.18), 0 0 0 1px rgba(255,107,26,0.15)`
        : '0 6px 20px rgba(0,0,0,0.35)',
      fontFamily: SANS, fontSize: 13, fontWeight: 500,
      color: dim ? YE.textMuted : YE.cream,
      letterSpacing: '-0.005em',
      whiteSpace: 'nowrap',
      width,
    }}>
      {icon && (
        <span style={{
          width: 18, height: 18, display: 'inline-flex',
          alignItems: 'center', justifyContent: 'center',
          color: active ? YE.orange : YE.textMuted,
        }}>{icon}</span>
      )}
      {label}
      {active && (
        <span style={{
          width: 6, height: 6, borderRadius: '50%',
          background: YE.orange,
          boxShadow: `0 0 10px ${YE.orange}`,
        }}/>
      )}
    </div>
  );
}

// ── Tiny SVG icons ──────────────────────────────────────────────────────────
const Icons = {
  bolt: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M13 2L3 14h7l-1 8 10-12h-7l1-8z"/></svg>,
  user: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 4-7 8-7s8 3 8 7"/></svg>,
  msg: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12a8 8 0 0 1-11.6 7.1L4 21l1.8-5A8 8 0 1 1 21 12z"/></svg>,
  chart: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M3 21V5M3 21h18M7 16v-4M11 16V9M15 16v-7M19 16v-2"/></svg>,
  doc: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M6 2h9l5 5v15H6z"/><path d="M14 2v6h6"/></svg>,
  funnel: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round"><path d="M3 4h18l-7 9v7l-4-2v-5z"/></svg>,
  cal: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 9h18M8 3v4M16 3v4"/></svg>,
  cog: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M5 19l2-2M17 7l2-2"/></svg>,
  star: <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3 7 7 .5-5.5 4.5L18 22l-6-4-6 4 1.5-8L2 9.5 9 9z"/></svg>,
  spark: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round"><path d="M12 2l2 7 7 2-7 2-2 7-2-7-7-2 7-2z"/></svg>,
  check: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12l5 5L20 7"/></svg>,
  globe: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>,
  flow: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><rect x="3" y="3" width="6" height="6" rx="1"/><rect x="15" y="15" width="6" height="6" rx="1"/><path d="M9 6h4a2 2 0 0 1 2 2v8"/></svg>,
};

// ── Connecting line between two points (animated draw) ─────────────────────
function ConnectLine({
  x1, y1, x2, y2,
  start = 0, drawDur = 0.6,
  color = 'rgba(255,255,255,0.16)',
  dashed = false, broken = false,
  width = 1, glow = false,
  pulse = false, pulseDelay = 0,
}) {
  const t = useTime();
  const localT = Math.max(0, t - start);
  const draw = Easing.easeInOutCubic(clamp(localT / drawDur, 0, 1));

  const dx = x2 - x1, dy = y2 - y1;
  const len = Math.hypot(dx, dy);
  if (len === 0) return null;

  // For broken lines, render as two segments with gap
  if (broken) {
    const gap = 0.15 + 0.1 * Math.sin(t * 2 + x1);
    const seg1End = 0.5 - gap / 2;
    const seg2Start = 0.5 + gap / 2;
    return (
      <React.Fragment>
        <ConnectLine x1={x1} y1={y1} x2={x1 + dx * seg1End} y2={y1 + dy * seg1End}
          start={start} drawDur={drawDur} color={color} dashed={dashed} width={width}/>
        <ConnectLine x1={x1 + dx * seg2Start} y1={y1 + dy * seg2Start} x2={x2} y2={y2}
          start={start} drawDur={drawDur} color={color} dashed={dashed} width={width}/>
      </React.Fragment>
    );
  }

  // pulse dot traveling along line
  let pulseEl = null;
  if (pulse) {
    const cycle = 1.4;
    const pT = ((t - pulseDelay) % cycle) / cycle;
    if (t > pulseDelay && pT >= 0) {
      const px = x1 + dx * pT;
      const py = y1 + dy * pT;
      pulseEl = (
        <circle cx={px} cy={py} r={4} fill={YE.orange}
          style={{ filter: `drop-shadow(0 0 6px ${YE.orange})` }}/>
      );
    }
  }

  return (
    <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible', pointerEvents: 'none' }}>
      <line
        x1={x1} y1={y1}
        x2={x1 + dx * draw} y2={y1 + dy * draw}
        stroke={color}
        strokeWidth={width}
        strokeDasharray={dashed ? '4 6' : undefined}
        strokeLinecap="round"
        style={glow ? { filter: `drop-shadow(0 0 4px ${color})` } : undefined}
      />
      {pulseEl}
    </svg>
  );
}

// ── Curved connector ──────────────────────────────────────────────────────
function CurveConnect({ x1, y1, x2, y2, start = 0, drawDur = 0.6, color = 'rgba(255,107,26,0.5)', width = 1.2, pulse = false, pulseDelay = 0, pulseDur = 1.6 }) {
  const t = useTime();
  const localT = Math.max(0, t - start);
  const draw = Easing.easeInOutCubic(clamp(localT / drawDur, 0, 1));

  const mx = (x1 + x2) / 2;
  const my = (y1 + y2) / 2;
  const dx = x2 - x1;
  const dy = y2 - y1;
  // perpendicular offset for curve
  const len = Math.hypot(dx, dy);
  const ox = -dy / len * (len * 0.18);
  const oy = dx / len * (len * 0.18);
  const cx = mx + ox * 0.4;
  const cy = my + oy * 0.4;

  const path = `M ${x1} ${y1} Q ${cx} ${cy} ${x2} ${y2}`;

  // pulse along curve
  let pulseEl = null;
  if (pulse && t > pulseDelay) {
    const pT = ((t - pulseDelay) % pulseDur) / pulseDur;
    // approximate position on quadratic curve
    const u = pT;
    const px = (1 - u) * (1 - u) * x1 + 2 * (1 - u) * u * cx + u * u * x2;
    const py = (1 - u) * (1 - u) * y1 + 2 * (1 - u) * u * cy + u * u * y2;
    pulseEl = (
      <circle cx={px} cy={py} r={3.5} fill={YE.orange}
        style={{ filter: `drop-shadow(0 0 8px ${YE.orange})` }}/>
    );
  }

  return (
    <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible', pointerEvents: 'none' }}>
      <path d={path} stroke={color} strokeWidth={width} fill="none"
        strokeLinecap="round"
        style={{
          strokeDasharray: 1000,
          strokeDashoffset: 1000 * (1 - draw),
        }}
      />
      {pulseEl}
    </svg>
  );
}

// ── Glow blob ──────────────────────────────────────────────────────────────
function Glow({ x, y, size = 400, color = YE.orangeGlow, opacity = 0.6 }) {
  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      width: size, height: size,
      transform: 'translate(-50%, -50%)',
      background: `radial-gradient(circle, ${color} 0%, transparent 60%)`,
      opacity,
      pointerEvents: 'none',
      filter: 'blur(20px)',
    }}/>
  );
}

// ── Caption / scene text ───────────────────────────────────────────────────
function SceneCaption({ children, y = 950, italicWord, italicEnd, ...rest }) {
  const { localTime, duration } = useSprite();
  const enter = Easing.easeOutCubic(clamp(localTime / 0.5, 0, 1));
  const exitStart = duration - 0.4;
  const exit = localTime > exitStart ? 1 - clamp((localTime - exitStart) / 0.4, 0, 1) : 1;
  const opacity = enter * exit;
  const ty = (1 - enter) * 14;

  return (
    <div style={{
      position: 'absolute',
      left: '50%', top: y,
      transform: `translate(-50%, ${ty}px)`,
      opacity,
      fontFamily: SANS,
      fontSize: 38, fontWeight: 500,
      color: YE.cream,
      letterSpacing: '-0.02em',
      textAlign: 'center',
      lineHeight: 1.2,
      whiteSpace: 'nowrap',
      ...rest,
    }}>
      {children}
    </div>
  );
}

// Italic editorial fragment in orange
function Em({ children }) {
  return (
    <span style={{
      fontFamily: SERIF,
      fontStyle: 'italic',
      color: YE.orange,
      fontWeight: 400,
      letterSpacing: '0.01em',
    }}>{children}</span>
  );
}

// Tiny label
function Eyebrow({ children, x = '50%', y = 80, opacity = 1 }) {
  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      transform: x === '50%' ? 'translateX(-50%)' : undefined,
      fontFamily: MONO,
      fontSize: 11,
      letterSpacing: '0.32em',
      textTransform: 'uppercase',
      color: YE.textMuted,
      opacity,
      display: 'flex', alignItems: 'center', gap: 10,
    }}>
      <span style={{ width: 24, height: 1, background: YE.orange, opacity: 0.7 }}/>
      {children}
    </div>
  );
}

// ── Animated counter ───────────────────────────────────────────────────────
function CountUp({ to, from = 0, dur = 1.2, delay = 0, prefix = '', suffix = '', decimals = 0 }) {
  const { localTime } = useSprite();
  const t = clamp((localTime - delay) / dur, 0, 1);
  const eased = Easing.easeOutCubic(t);
  const v = from + (to - from) * eased;
  return <span style={{ fontVariantNumeric: 'tabular-nums' }}>{prefix}{v.toFixed(decimals)}{suffix}</span>;
}

Object.assign(window, {
  YE, SANS, SERIF, MONO,
  StageBackdrop, PulseDot, ModuleChip, Icons,
  ConnectLine, CurveConnect, Glow,
  SceneCaption, Em, Eyebrow, CountUp,
});
