// mapemedia — shared primitives, reused across all pages.
// Adapted from the design-system website UI kit.

const SectionEyebrow = ({ children }) =>
<span className="eyebrow">{children}</span>;


// Render a title string that may contain <em>..</em> as React nodes,
// so the <h2> can be direct-edited in the preview without crashing.
const renderSectionTitle = (title) =>
  String(title).split(/(<em>.*?<\/em>)/g).map((p, i) => {
    const m = p.match(/^<em>([\s\S]*?)<\/em>$/);
    return m ? <em key={i}>{m[1]}</em> : <React.Fragment key={i}>{p}</React.Fragment>;
  });

const SectionHead = ({ eyebrow, title, intro, center }) =>
<div className={center ? "sec-head center" : "sec-head"}>
    <SectionEyebrow>{eyebrow}</SectionEyebrow>
    <h2>{renderSectionTitle(title)}</h2>
    {intro ? <p>{intro}</p> : null}
  </div>;


const Button = ({ variant = "primary", children, href, ...rest }) => {
  const base = {
    fontFamily: 'var(--font-sans)', fontWeight: 500, fontSize: 11,
    letterSpacing: '.14em', textTransform: 'uppercase', lineHeight: 1,
    borderRadius: 0, cursor: 'pointer',
    transition: 'background .2s, color .2s, border-color .2s',
    textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 8
  };
  const variants = {
    primary: { background: 'var(--primary)', color: '#fff', border: 0, padding: '14px 32px' },
    ghost: { background: 'transparent', color: 'rgba(255,255,255,.7)', border: '.5px solid rgba(255,255,255,.2)', padding: '13px 28px' },
    ghostDark: { background: 'transparent', color: 'var(--body)', border: '.5px solid var(--hairline-color)', padding: '13px 28px' },
    ghostCopper: { background: 'transparent', color: 'var(--primary)', border: '.5px solid rgba(184,115,51,.35)', padding: '9px 20px' }
  };
  const style = { ...base, ...variants[variant] };
  const Tag = href ? 'a' : 'button';
  const hover = (e) => {
    if (variant === 'primary') e.currentTarget.style.background = 'var(--primary-active)';
    if (variant === 'ghost') {e.currentTarget.style.borderColor = 'rgba(255,255,255,.5)';e.currentTarget.style.color = '#fff';}
    if (variant === 'ghostDark') {e.currentTarget.style.borderColor = 'var(--primary)';e.currentTarget.style.color = 'var(--primary)';}
    if (variant === 'ghostCopper') {e.currentTarget.style.background = 'var(--primary)';e.currentTarget.style.color = '#fff';}
  };
  const out = (e) => {Object.assign(e.currentTarget.style, style);};
  return (
    <Tag href={href} style={style} onMouseEnter={hover} onMouseLeave={out} {...rest}>
      {children}
    </Tag>);

};

// Inline CTA link with animated arrow.
const ServiceCTA = ({ children, href = "#", dark = false }) =>
<a href={href} className="service-cta"
style={{
  display: 'inline-flex', alignItems: 'center', gap: 10,
  color: 'var(--primary)', fontFamily: 'var(--font-sans)', fontWeight: 500,
  fontSize: 11, letterSpacing: '.12em', textTransform: 'uppercase', textDecoration: 'none'
}}
onMouseEnter={(e) => {const a = e.currentTarget.querySelector('.arr');if (a) a.style.transform = 'translateX(4px)';}}
onMouseLeave={(e) => {const a = e.currentTarget.querySelector('.arr');if (a) a.style.transform = 'translateX(0)';}}>
  
    {children}
    <span className="arr" style={{ transition: 'transform .2s' }}>→</span>
  </a>;


// Decorative copper rings (hero + cta).
const CopperRings = ({ size = 600, opacity = 0.12, style = {} }) =>
<div aria-hidden style={{
  position: 'absolute', width: size, height: size,
  border: `1px solid rgba(184,115,51,${opacity})`,
  borderRadius: '50%', pointerEvents: 'none', ...style
}} />;


// IntersectionObserver reveal wrapper.
const Reveal = ({ children, delay = 0, as: Tag = 'div', ...rest }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          setTimeout(() => e.target.classList.add('visible'), delay);
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.08 });
    io.observe(el);
    return () => io.disconnect();
  }, [delay]);
  return <Tag ref={ref} className="reveal" {...rest}>{children}</Tag>;
};

// Marquee track — duplicates items inline so translateX(-50%) loops seamlessly.
const MarqueeTrack = ({ items, gap = 80, duration = 28, style = {} }) =>
<div style={{ display: 'flex', gap, width: 'max-content', animation: `marquee ${duration}s linear infinite`, ...style }}>
    {[...items, ...items].map((label, i) => <span key={i}>{label}</span>)}
  </div>;


// "KI-generiert" provenance badge — placed on every generated image/video.
const AILabel = ({ position = 'tl', label = 'KI-generiert', style = {} }) => {
  const corners = {
    tl: { top: 12, left: 12 }, tr: { top: 12, right: 12 },
    bl: { bottom: 12, left: 12 }, br: { bottom: 12, right: 12 },
  };
  return (
    <span aria-hidden style={{
      position: 'absolute', zIndex: 4, display: 'inline-flex', alignItems: 'center', gap: 7,
      padding: '5px 10px', background: 'rgba(26,26,26,.66)',
      backdropFilter: 'blur(2px)', WebkitBackdropFilter: 'blur(2px)', pointerEvents: 'none',
      ...corners[position], ...style,
    }}>
      <span style={{ width: 5, height: 5, borderRadius: '50%', background: 'var(--primary)', flexShrink: 0 }} />
      <span style={{ fontFamily: 'var(--font-sans)', fontSize: 9, fontWeight: 500, letterSpacing: '.18em', textTransform: 'uppercase', color: '#fff' }}>{label}</span>
    </span>
  );
};

// Speaker glyph (muted / unmuted) — thin-line, matches the no-emoji rule.
const SpeakerIcon = ({ muted, size = 14 }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
    <path d="M4 9v6h4l5 4V5L8 9H4z" fill="#fff" />
    {muted
      ? <React.Fragment><line x1="17" y1="9.5" x2="22" y2="14.5" /><line x1="22" y1="9.5" x2="17" y2="14.5" /></React.Fragment>
      : <React.Fragment><path d="M16.5 8.5a5 5 0 0 1 0 7" /><path d="M19 6a9 9 0 0 1 0 12" /></React.Fragment>}
  </svg>
);

// Looping video that starts MUTED with a click-to-unmute speaker control (bottom-left).
// Pass `silent` to drop the audio entirely: permanently muted, no toggle.
const LoopVideo = ({ src, ratio = '4 / 3', dark = false, style = {}, fit = 'cover', radius = 0, silent = false }) => {
  const vref = React.useRef(null);
  const [muted, setMuted] = React.useState(true);
  const toggle = (e) => {
    e.stopPropagation(); e.preventDefault();
    const v = vref.current; if (!v) return;
    v.muted = !v.muted;
    if (!v.muted) { v.volume = 1; const p = v.play(); if (p && p.catch) p.catch(() => {}); }
    setMuted(v.muted);
  };
  return (
    <div style={{ position: 'relative', width: '100%', aspectRatio: ratio, overflow: 'hidden', background: '#000', borderRadius: radius, border: radius ? 0 : `.5px solid ${dark ? 'rgba(255,255,255,.1)' : 'var(--hairline-color)'}`, ...style }}>
      <video src={src} autoPlay loop muted playsInline preload="auto"
        ref={(el) => { vref.current = el; if (el && silent) { el.muted = true; el.defaultMuted = true; el.volume = 0; } }}
        onCanPlay={(e) => { if (silent) e.currentTarget.muted = true; const p = e.currentTarget.play(); if (p && p.catch) p.catch(() => {}); }}
        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: fit, display: 'block' }} />
      {silent ? null :
      <button onClick={toggle} aria-label={muted ? 'Ton einschalten' : 'Ton ausschalten'}
        style={{ all: 'unset', cursor: 'pointer', position: 'absolute', left: 12, bottom: 12, zIndex: 5, display: 'inline-flex', alignItems: 'center', gap: 8, padding: '7px 11px', background: 'rgba(26,26,26,.66)', backdropFilter: 'blur(2px)', WebkitBackdropFilter: 'blur(2px)' }}>
        <SpeakerIcon muted={muted} />
        <span style={{ fontFamily: 'var(--font-sans)', fontSize: 9, fontWeight: 500, letterSpacing: '.16em', textTransform: 'uppercase', color: '#fff' }}>{muted ? 'Ton an' : 'Ton aus'}</span>
      </button>}
    </div>
  );
};

// Portrait iPhone mockup wrapping a looping video.
const IPhoneMock = ({ src, width = 230, dark = true }) => (
  <div style={{ position: 'relative', width, flexShrink: 0, padding: 9, background: '#0c0c0c', borderRadius: 38, border: '1px solid rgba(255,255,255,.12)' }}>
    <div style={{ position: 'relative', overflow: 'hidden', borderRadius: 30, aspectRatio: '9 / 19.5', background: '#000' }}>
      <LoopVideo src={src} ratio="9 / 19.5" radius={30} />
      {/* dynamic island */}
      <div aria-hidden style={{ position: 'absolute', top: 10, left: '50%', transform: 'translateX(-50%)', width: 74, height: 19, borderRadius: 12, background: '#000', zIndex: 6 }} />
    </div>
  </div>
);

// Reusable before/after comparison slider (drag handle).
const BAWidget = ({ beforeSrc, afterSrc, beforeLabel = 'Vorher', afterLabel = 'Nachher', dark = false, ratio = '4 / 3' }) => {
  const [pos, setPos] = React.useState(50);
  const ref = React.useRef(null);
  const dragging = React.useRef(false);
  const setFromClientX = (clientX) => {
    const el = ref.current;if (!el) return;
    const r = el.getBoundingClientRect();
    setPos(Math.max(0, Math.min(100, (clientX - r.left) / r.width * 100)));
  };
  React.useEffect(() => {
    const move = (e) => {if (dragging.current) setFromClientX(e.touches ? e.touches[0].clientX : e.clientX);};
    const up = () => {dragging.current = false;};
    window.addEventListener('mousemove', move);window.addEventListener('mouseup', up);
    window.addEventListener('touchmove', move, { passive: false });window.addEventListener('touchend', up);
    return () => {window.removeEventListener('mousemove', move);window.removeEventListener('mouseup', up);window.removeEventListener('touchmove', move);window.removeEventListener('touchend', up);};
  }, []);
  const border = dark ? 'rgba(255,255,255,.1)' : 'var(--hairline-color)';
  const lbl = (side) => ({
    position: 'absolute', top: 14, [side]: 14, zIndex: 2,
    fontFamily: 'var(--font-sans)', fontSize: 9, fontWeight: 500, letterSpacing: '.2em', textTransform: 'uppercase',
    color: side === 'right' ? 'var(--primary)' : '#fff', background: 'rgba(26,26,26,.7)', padding: '6px 12px'
  });
  const imgStyle = { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', display: 'block' };
  return (
    <div ref={ref}
    onMouseDown={(e) => {dragging.current = true;setFromClientX(e.clientX);}}
    onTouchStart={(e) => {dragging.current = true;setFromClientX(e.touches[0].clientX);}}
    style={{ position: 'relative', width: '100%', aspectRatio: ratio, overflow: 'hidden', border: `.5px solid ${border}`, cursor: 'ew-resize', userSelect: 'none', background: 'var(--surface-warm-grey)' }}>
      <img src={afterSrc} alt={afterLabel} draggable="false" style={imgStyle} />
      <div style={{ position: 'absolute', inset: 0, clipPath: `inset(0 ${100 - pos}% 0 0)` }}>
        <img src={beforeSrc} alt={beforeLabel} draggable="false" style={imgStyle} />
      </div>
      <span style={lbl('left')}>{beforeLabel}</span>
      <span style={lbl('right')}>{afterLabel}</span>
      <div style={{ position: 'absolute', top: 0, bottom: 0, left: `${pos}%`, width: 2, background: 'var(--primary)', transform: 'translateX(-1px)', zIndex: 2 }}>
        <div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: 40, height: 40, borderRadius: '50%', background: 'var(--primary)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 14, letterSpacing: '-.05em' }}>‹›</div>
      </div>
    </div>);

};

// Lightweight YouTube facade: poster thumbnail + play button, swaps to the
// embed (autoplay) on click. More reliable than a cold iframe in previews.
const YTLite = ({ id, title = 'Video', dark = false, ratio = '16 / 9' }) => {
  const [play, setPlay] = React.useState(false);
  const border = dark ? 'rgba(255,255,255,.1)' : 'var(--hairline-color)';
  return (
    <div style={{ position: 'relative', width: '100%', aspectRatio: ratio, overflow: 'hidden', border: `.5px solid ${border}`, background: '#000' }}>
      {play ?
      <iframe
        src={`https://www.youtube.com/embed/${id}?autoplay=1&rel=0&modestbranding=1&playsinline=1`}
        title={title} allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen
        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', border: 0, display: 'block' }}>
      </iframe> :

      <button onClick={() => setPlay(true)} aria-label={`${title} abspielen`}
      style={{ all: 'unset', cursor: 'pointer', position: 'absolute', inset: 0, display: 'block' }}>
          <img src={`https://img.youtube.com/vi/${id}/maxresdefault.jpg`} alt={title} draggable="false"
        onError={(e) => {e.currentTarget.src = `https://img.youtube.com/vi/${id}/hqdefault.jpg`;}}
        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
          <span aria-hidden style={{ position: 'absolute', inset: 0, background: 'rgba(26,26,26,.28)' }} />
          <span aria-hidden style={{
          position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)',
          width: 72, height: 72, borderRadius: '50%', background: 'var(--primary)',
          display: 'flex', alignItems: 'center', justifyContent: 'center'
        }}>
            <span style={{ width: 0, height: 0, borderTop: '13px solid transparent', borderBottom: '13px solid transparent', borderLeft: '20px solid #fff', marginLeft: 5 }} />
          </span>
          <span style={{ position: 'absolute', left: 16, bottom: 16, fontFamily: 'var(--font-sans)', fontSize: 10, fontWeight: 500, letterSpacing: '.18em', textTransform: 'uppercase', color: '#fff', background: 'rgba(26,26,26,.66)', padding: '7px 13px' }}>▶ {title}</span>
        </button>
      }
      {!play &&
      <a href={`https://www.youtube.com/watch?v=${id}`} target="_blank" rel="noopener noreferrer"
      onClick={(e) => e.stopPropagation()}
      style={{ position: 'absolute', top: 14, right: 14, zIndex: 3, fontFamily: 'var(--font-sans)', fontSize: 9, fontWeight: 500, letterSpacing: '.16em', textTransform: 'uppercase', color: '#fff', background: 'rgba(26,26,26,.66)', padding: '7px 12px', textDecoration: 'none' }}>
          Auf YouTube ↗
        </a>
      }
    </div>);

};

// Looping YouTube embed: autoplays muted on repeat, no chrome. For ambient
// showreel loops where the video IS the visual, not a click target.
const YTLoop = ({ id, title = 'Video', dark = false, ratio = '16 / 9', label }) => {
  const border = dark ? 'rgba(255,255,255,.1)' : 'var(--hairline-color)';
  const src = `https://www.youtube.com/embed/${id}?autoplay=1&mute=1&loop=1&playlist=${id}&controls=0&rel=0&modestbranding=1&playsinline=1&disablekb=1&iv_load_policy=3`;
  return (
    <div style={{ position: 'relative', width: '100%', aspectRatio: ratio, overflow: 'hidden', border: `.5px solid ${border}`, background: '#000' }}>
      <iframe src={src} title={title} tabIndex={-1}
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        style={{ position: 'absolute', top: '50%', left: '50%', width: '100%', height: '100%', transform: 'translate(-50%,-50%) scale(1.02)', border: 0, display: 'block', pointerEvents: 'none' }}>
      </iframe>
      {label &&
      <span style={{ position: 'absolute', left: 0, bottom: 0, fontFamily: 'var(--font-sans)', fontSize: 9, fontWeight: 500, letterSpacing: '.2em', textTransform: 'uppercase', color: '#fff', background: 'rgba(26,26,26,.72)', padding: '8px 14px', borderTop: '2px solid var(--primary)' }}>{label}</span>
      }
    </div>);

};

// Node-based comparison slider: arbitrary content on each side.
// `before` fills from the left up to the handle; `after` is the base layer.
const BACompareNode = ({ before, after, beforeLabel = 'Vorher', afterLabel = 'Nachher', dark = false, ratio = '16 / 9', initial = 62 }) => {
  const [pos, setPos] = React.useState(initial);
  const ref = React.useRef(null);
  const dragging = React.useRef(false);
  const setFromClientX = (clientX) => {
    const el = ref.current;if (!el) return;
    const r = el.getBoundingClientRect();
    setPos(Math.max(0, Math.min(100, (clientX - r.left) / r.width * 100)));
  };
  React.useEffect(() => {
    const move = (e) => {if (dragging.current) {if (e.cancelable && e.touches) e.preventDefault();setFromClientX(e.touches ? e.touches[0].clientX : e.clientX);}};
    const up = () => {dragging.current = false;};
    window.addEventListener('mousemove', move);window.addEventListener('mouseup', up);
    window.addEventListener('touchmove', move, { passive: false });window.addEventListener('touchend', up);
    return () => {window.removeEventListener('mousemove', move);window.removeEventListener('mouseup', up);window.removeEventListener('touchmove', move);window.removeEventListener('touchend', up);};
  }, []);
  const border = dark ? 'rgba(255,255,255,.1)' : 'var(--hairline-color)';
  const lbl = (side, accent) => ({
    position: 'absolute', top: 14, [side]: 14, zIndex: 3,
    fontFamily: 'var(--font-sans)', fontSize: 9, fontWeight: 500, letterSpacing: '.2em', textTransform: 'uppercase',
    color: accent ? 'var(--primary)' : '#fff', background: 'rgba(26,26,26,.7)', padding: '6px 12px', pointerEvents: 'none'
  });
  return (
    <div ref={ref}
    onMouseDown={(e) => {dragging.current = true;setFromClientX(e.clientX);}}
    onTouchStart={(e) => {dragging.current = true;setFromClientX(e.touches[0].clientX);}}
    style={{ position: 'relative', width: '100%', aspectRatio: ratio, overflow: 'hidden', border: `.5px solid ${border}`, cursor: 'ew-resize', userSelect: 'none', background: '#000' }}>
      {/* after = base (revealed on the left as the handle moves left) */}
      <div style={{ position: 'absolute', inset: 0 }}>{after}</div>
      {/* before = overlay clipped from left edge to handle */}
      <div style={{ position: 'absolute', inset: 0, clipPath: `inset(0 ${100 - pos}% 0 0)` }}>{before}</div>
      <span style={lbl('left', false)}>{beforeLabel}</span>
      <span style={lbl('right', true)}>{afterLabel}</span>
      <div style={{ position: 'absolute', top: 0, bottom: 0, left: `${pos}%`, width: 2, background: 'var(--primary)', transform: 'translateX(-1px)', zIndex: 2 }}>
        <div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: 40, height: 40, borderRadius: '50%', background: 'var(--primary)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 14, letterSpacing: '-.05em' }}>‹›</div>
      </div>
    </div>);

};

// ── Global tweak bridge ───────────────────────────────────────────────
// Mirrors accent + headline-font tweaks to localStorage so the look carries
// across all four pages in preview. Apply on every page at load.
const MM_TWEAK_KEY = 'mm_global_tweaks';
function readGlobalTweaks() {
  try {return JSON.parse(localStorage.getItem(MM_TWEAK_KEY) || '{}');}
  catch (e) {return {};}
}
function applyGlobalTweaks(t) {
  const root = document.documentElement;
  if (t.accent) {
    root.style.setProperty('--primary', t.accent);
    // derive a darker active shade
    root.style.setProperty('--primary-active', shade(t.accent, -0.16));
  }
  if (t.headlineFont) {
    root.style.setProperty('--font-display', t.headlineFont);
  }
}
function persistGlobalTweaks(patch) {
  const next = { ...readGlobalTweaks(), ...patch };
  try {localStorage.setItem(MM_TWEAK_KEY, JSON.stringify(next));} catch (e) {}
  applyGlobalTweaks(next);
}
// crude hex shade helper
function shade(hex, amt) {
  const m = hex.replace('#', '');
  if (m.length !== 6) return hex;
  let r = parseInt(m.slice(0, 2), 16),g = parseInt(m.slice(2, 4), 16),b = parseInt(m.slice(4, 6), 16);
  r = Math.max(0, Math.min(255, Math.round(r + r * amt)));
  g = Math.max(0, Math.min(255, Math.round(g + g * amt)));
  b = Math.max(0, Math.min(255, Math.round(b + b * amt)));
  return '#' + [r, g, b].map((v) => v.toString(16).padStart(2, '0')).join('');
}
// Apply immediately on script load (before React renders) to avoid flash.
applyGlobalTweaks(readGlobalTweaks());

Object.assign(window, {
  SectionEyebrow, SectionHead, Button, ServiceCTA, CopperRings, Reveal, MarqueeTrack, BAWidget, YTLite, YTLoop, BACompareNode, AILabel, SpeakerIcon, LoopVideo, IPhoneMock,
  MM_TWEAK_KEY, readGlobalTweaks, applyGlobalTweaks, persistGlobalTweaks, shade
});