// samtal-site.jsx — the room.
// Everything is the conversation. No header. No links. No tray.
// Mouse moves → the column drifts gently.
// A faint "+" bottom-right opens a modal with three pages
// (gå i coaching · anlita alexander · kontakt) that take over dynamically.

const PACES = {
  slow: { speed: 32, gap: 1100 },
  natural: { speed: 16, gap: 700 },
  quick: { speed: 8, gap: 320 },
};

const SAMTAL_DEFAULTS = {
  lang: 'sv',
  pace: 'quick',
};

const COPY = {
  sv: {
    you: 'du',
    placeholder: 'skriv vad du vill — eller välj ovan',
    placeholderName: 'ditt namn',
    placeholderPhone: 'ditt telefonnummer',
    placeholderEmail: 'din epost',
    listening: 'lyssnar',
    open: 'rummet är öppet',
    here: 'jag är här',
    skipSilence: 'bryt tystnaden',
    silenceLabel: 'sitt i det.',
    aiLost: 'jag tappade tråden. säg det igen.',
    notes: '+',
    coaching: 'gå i coaching',
    hire: 'anlita alexander',
    contact: 'kontakt',
    back: 'åter till samtalet',
    settingsLang: 'språk',
    settingsPace: 'tempo',
    pace_slow: 'sakta',
    pace_natural: 'naturligt',
    pace_quick: 'snabbt',
  },
  en: {
    you: 'you',
    placeholder: 'type anything — or choose above',
    placeholderName: 'your name',
    placeholderPhone: 'your phone',
    placeholderEmail: 'your email',
    listening: 'listening',
    open: 'the room is open',
    here: 'i am here',
    skipSilence: 'break the silence',
    silenceLabel: 'sit in it.',
    aiLost: 'i lost the thread. say it again.',
    notes: '+',
    coaching: 'work with me',
    hire: 'hire alexander',
    contact: 'contact',
    back: 'back to the room',
    settingsLang: 'language',
    settingsPace: 'pace',
    pace_slow: 'slow',
    pace_natural: 'natural',
    pace_quick: 'quick',
  },
  es: {
    you: 'tú',
    placeholder: 'escribe lo que quieras — o elige arriba',
    placeholderName: 'tu nombre',
    placeholderPhone: 'tu teléfono',
    placeholderEmail: 'tu correo',
    listening: 'escuchando',
    open: 'la sala está abierta',
    here: 'estoy aquí',
    skipSilence: 'rompe el silencio',
    silenceLabel: 'siéntate en ello.',
    aiLost: 'perdí el hilo. dilo de nuevo.',
    notes: '+',
    coaching: 'trabajar conmigo',
    hire: 'contratar a alexander',
    contact: 'contacto',
    back: 'volver a la sala',
    settingsLang: 'idioma',
    settingsPace: 'ritmo',
    pace_slow: 'lento',
    pace_natural: 'natural',
    pace_quick: 'rápido',
  },
};


const ContactForm = ({ onSent }) => {
  const [name, setName] = React.useState('');
  const [email, setEmail] = React.useState('');
  const [msg, setMsg] = React.useState('');
  const [sent, setSent] = React.useState(false);
  const [sending, setSending] = React.useState(false);

  const send = async () => {
    if (!name || !email) return;
    setSending(true);
    try {
      await fetch('https://coachholmberg-api.alexander-894.workers.dev/contact', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name, email, message: msg })
      });
      setSent(true);
      if (onSent) onSent();
    } catch(e) {}
    setSending(false);
  };

  if (sent) return (
    <div className="contact-form">
      <p style={{opacity:0.6, fontSize:'14.5px'}}>tack. jag hör av mig.</p>
    </div>
  );

  return (
    <div className="contact-form">
      <input placeholder="ditt namn" value={name} onChange={e => setName(e.target.value)} />
      <input placeholder="din mail" value={email} onChange={e => setEmail(e.target.value)} />
      <textarea placeholder="vad rör sig hos dig som du vill prata om" value={msg} onChange={e => setMsg(e.target.value)} rows={3} />
      <button onClick={send} disabled={sending || !name || !email}>
        {sending ? '...' : 'skicka'}
      </button>
    </div>
  );
};

function SamtalSite() {
  const [lang, setLang] = React.useState(SAMTAL_DEFAULTS.lang);
  const [pace, setPace] = React.useState(SAMTAL_DEFAULTS.pace);
  const [visitorIp, setVisitorIp] = React.useState('::1');
  const [log, setLog] = React.useState([]);
  const [phase, setPhase] = React.useState('intro'); // intro|playing|waiting|asking|silent|ended
  const [input, setInput] = React.useState('');
  const [thinking, setThinking] = React.useState(false);
  const [booking, setBooking] = React.useState({});
  const [askState, setAskState] = React.useState(null);
  const [silenceCountdown, setSilenceCountdown] = React.useState(0);
  const [modal, setModal] = React.useState(null); // null|'menu'|'coaching'|'hire'|'contact'|'settings'

  const [tint, setTintState] = React.useState(() => localStorage.getItem('ch-tint') || 'none');
  const setTint = (t) => { setTintState(t); localStorage.setItem('ch-tint', t); };
  React.useEffect(() => {
    let el = document.getElementById('color-tint');
    if (!el) {
      el = document.createElement('div');
      el.id = 'color-tint';
      el.style.cssText = 'position:fixed;inset:0;pointer-events:none;z-index:9999;mix-blend-mode:color;opacity:0;transition:opacity 0.8s ease;';
      document.body.appendChild(el);
    }
    const tints = { green: { bg:'#4AFF8C', op:'0.12' }, red: { bg:'#FF4A4A', op:'0.10' }, blue: { bg:'#4A9EFF', op:'0.10' } };
    if (tints[tint]) {
      el.style.background = tints[tint].bg;
      el.style.opacity = tints[tint].op;
    } else {
      el.style.opacity = '0';
    }
  }, [tint]);

  const [sessionKod] = React.useState(() => {
    const months = ['Jan','Feb','Mar','Apr','Maj','Jun','Jul','Aug','Sep','Okt','Nov','Dec'];
    const now = new Date();
    return {
      datum: now.getDate() + ' ' + months[now.getMonth()],
      kod: Math.floor(10000 + Math.random() * 90000) + String.fromCharCode(65 + Math.floor(Math.random() * 26))
    };
  });

  const cancelRef = React.useRef(false);
  const inputRef = React.useRef(null);
  const scrollRef = React.useRef(null);
  const stageRef = React.useRef(null);
  const replies = React.useRef([]);
  // Registry of "bendable" elements: each .line and .suggest-btn that the
  // cursor should be able to push around. Element → base coords cached lazily.
  const bendablesRef = React.useRef(new Set());
  const registerBendable = React.useCallback((el) => {
    if (el) bendablesRef.current.add(el);
  }, []);
  const unregisterBendable = React.useCallback((el) => {
    if (el) bendablesRef.current.delete(el);
  }, []);

  // ── pixelated portrait canvas ──────────────────────────────────────────
  const canvasRef = React.useRef(null);
  const pixelImg = React.useRef(null);
  const pixelStepRef = React.useRef(0);
  const PIXEL_STEPS = [6, 10, 16, 26, 42, 68, 110, 9999];

  const drawPixelated = React.useCallback((res) => {
    const canvas = canvasRef.current;
    const img = pixelImg.current;
    if (!canvas || !img) return;
    const w = canvas.width, h = canvas.height;
    const small = document.createElement('canvas');
    const factor = res >= 9999 ? 1 : res / Math.max(w, h);
    small.width = Math.max(1, Math.floor(w * factor));
    small.height = Math.max(1, Math.floor(h * factor));
    const sx = small.getContext('2d');
    sx.drawImage(img, 0, 0, small.width, small.height);
    const cx = canvas.getContext('2d');
    cx.imageSmoothingEnabled = false;
    cx.clearRect(0, 0, w, h);
    cx.drawImage(small, 0, 0, w, h);
  }, []);

  const advancePixel = React.useCallback(() => {
    pixelStepRef.current = Math.min(pixelStepRef.current + 1, PIXEL_STEPS.length - 1);
    drawPixelated(PIXEL_STEPS[pixelStepRef.current]);
  }, [drawPixelated]);

  React.useEffect(() => {
    const img = new Image();
    img.src = '/alexander.jpg';
    img.onload = () => {
      pixelImg.current = img;
      const canvas = canvasRef.current;
      if (canvas) {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        drawPixelated(PIXEL_STEPS[0]);
      }
    };
    const onResize = () => {
      const canvas = canvasRef.current;
      if (canvas && pixelImg.current) {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        drawPixelated(PIXEL_STEPS[pixelStepRef.current]);
      }
    };
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, [drawPixelated]);

  const paceCfg = PACES[pace] || PACES.quick;
  const t = COPY[lang];

  // ── mouse parallax + per-line bend ////////////////////////////////////
  // global parallax shifts the column; per-element bend pushes individual
  // lines & suggestions away from the cursor. it's the page being
  // challenged by the visitor's gesture.
  React.useEffect(() => {
    let rafId = 0;
    let mouseX = window.innerWidth / 2, mouseY = window.innerHeight / 2;
    let targetX = 0, targetY = 0;
    let currentX = 0, currentY = 0;
    const onMove = (e) => {
      mouseX = e.clientX;
      mouseY = e.clientY;
      const w = window.innerWidth, h = window.innerHeight;
      targetX = (e.clientX / w - 0.5) * 2;
      targetY = (e.clientY / h - 0.5) * 2;
    };
    const tick = () => {
      currentX += (targetX - currentX) * 0.07;
      currentY += (targetY - currentY) * 0.07;
      const el = stageRef.current;
      if (el) {
        el.style.setProperty('--mx', currentX.toFixed(4));
        el.style.setProperty('--my', currentY.toFixed(4));
      }
      document.documentElement.style.setProperty('--gx', (50 + currentX * 14).toFixed(2) + '%');
      document.documentElement.style.setProperty('--gy', (40 + currentY * 10).toFixed(2) + '%');

      const MAX = 260; // px radius of influence
      bendablesRef.current.forEach((line) => {
        if (!line.isConnected) { bendablesRef.current.delete(line); return; }
        const r = line.getBoundingClientRect();
        if (r.width === 0) return;
        const cx = r.left + r.width / 2;
        const cy = r.top + r.height / 2;
        const dx = mouseX - cx;
        const dy = mouseY - cy;
        const dist = Math.hypot(dx, dy);
        if (dist > MAX) {
          line.style.setProperty('--bx', '0px');
          line.style.setProperty('--by', '0px');
          line.style.setProperty('--bs', '0deg');
          return;
        }
        const strength = Math.pow(1 - dist / MAX, 1.4);
        const pushX = -dx * 0.18 * strength;
        const pushY = -dy * 0.10 * strength;
        const skew = (dx / MAX) * 2.2 * strength;
        line.style.setProperty('--bx', pushX.toFixed(2) + 'px');
        line.style.setProperty('--by', pushY.toFixed(2) + 'px');
        line.style.setProperty('--bs', skew.toFixed(3) + 'deg');
      });
      rafId = requestAnimationFrame(tick);
    };
    window.addEventListener('mousemove', onMove);
    rafId = requestAnimationFrame(tick);
    return () => {
      window.removeEventListener('mousemove', onMove);
      cancelAnimationFrame(rafId);
    };
  }, []);

  // ── fetch visitor IP for row labels ───────────────────────────────────
  React.useEffect(() => {
    let cancelled = false;
    fetch('https://api.ipify.org?format=json', { cache: 'no-store' })
      .then((r) => r.json())
      .then((j) => { if (!cancelled && j && j.ip) setVisitorIp(j.ip); })
      .catch(() => {});
    return () => { cancelled = true; };
  }, []);

  // ── scroll log on updates ─────────────────────────────────────────────
  React.useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [log, thinking, phase]);

  // ── type a line out, char by char ─────────────────────────────────────
  const typeLine = React.useCallback((raw) => {
    return new Promise((resolve) => {
      if (cancelRef.current) return resolve();
      const line = String(raw);
      if (line === '') {
        setLog((L) => [...L, { who: 'a', text: '', kind: 'spacer' }]);
        return setTimeout(resolve, 240);
      }
      setLog((L) => [...L, { who: 'a', text: '', typing: true }]);
      let j = 0;
      const tick = () => {
        if (cancelRef.current) return resolve();
        j++;
        setLog((L) => {
          const copy = L.slice();
          copy[copy.length - 1] = { who: 'a', text: line.slice(0, j), typing: j < line.length };
          return copy;
        });
        if (j < line.length) setTimeout(tick, paceCfg.speed + Math.random() * 18);
        else resolve();
      };
      setTimeout(tick, 180);
    });
  }, [paceCfg.speed]);

  const wait = (ms) => new Promise((r) => setTimeout(r, cancelRef.current ? 0 : ms));

  // ── play a tree node ──────────────────────────────────────────────────
  const playNode = React.useCallback(async (id) => {
    const n = getNode(lang, id);
    if (!n) return;
    setPhase('playing');
    replies.current = [];

    for (const raw of n.lines) {
      const piece = String(raw).replace(/\$name/g, booking.name || '');
      await typeLine(piece);
      if (cancelRef.current) return;
      await wait(paceCfg.gap);
    }
    if (cancelRef.current) return;

    if (n.trigger) {
      setPhase(n.trigger);
      return;
    }
    if (n.auto && n.next) {
      await wait(2500);
      if (cancelRef.current) return;
      return playNode(n.next);
    }
    if (n.showForm) {
      setPhase('form');
      return;
    }
    if (n.ask) {
      setAskState({ key: n.ask.key, next: n.ask.next });
      setPhase('asking');
      setTimeout(() => inputRef.current?.focus(), 200);
      return;
    }
    if (n.replies && n.replies.length > 0) {
      replies.current = n.replies;
      setPhase('waiting');
      setTimeout(() => inputRef.current?.focus(), 200);
    }
  }, [lang, booking.name, typeLine, paceCfg.gap]);

  // ── boot ──────────────────────────────────────────────────────────────
  React.useEffect(() => {
    cancelRef.current = true;
    const id = setTimeout(() => {
      cancelRef.current = false;
      setLog([]);
      setBooking({});
      playNode('intro');
    }, 120);
    return () => clearTimeout(id);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [lang]);

  // ── visitor actions ───────────────────────────────────────────────────
  const pushVisitor = (text) => setLog((L) => [...L, { who: 'u', text }]);

  const choose = async (reply) => {
    if (phase !== 'waiting') return;
    advancePixel();
    pushVisitor(reply.say);
    replies.current = [];
    setPhase('playing');
    await wait(380);
    if (reply.do === 'silence') return enterSilence();
    if (reply.do === 'end') return setPhase('ended');
    if (reply.to) return playNode(reply.to);
  };

  const submitInput = async () => {
    const msg = input.trim();
    if (!msg) return;

    if (phase === 'asking' && askState) {
      pushVisitor(msg);
      setBooking((b) => ({ ...b, [askState.key]: msg }));
      setInput('');
      const next = askState.next;
      const key = askState.key;
      setAskState(null);
      setPhase('playing');
      await wait(420);
      if (key === 'name') {
        await typeLine(`tack, ${msg.split(/\s+/)[0]}.`);
        await wait(paceCfg.gap);
      } else if (key === 'phone' || key === 'email') {
        await typeLine(lang === 'sv' ? 'noterat.' : 'noted.');
        await wait(paceCfg.gap);
      }
      return playNode(next);
    }

    if (phase !== 'waiting') return;
    pushVisitor(msg);
    setInput('');
    replies.current = [];
    setPhase('playing');
    setThinking(true);

    const userMsg = lang === 'sv'
      ? `besökaren skrev: "${msg}"\n\nsvara som alexander, max 35 ord, lowercase, 1-3 meningar. avsluta gärna med en motfråga.`
      : `the visitor wrote: "${msg}"\n\nanswer as alexander, max 35 words, lowercase, 1-3 sentences. end with a counter-question if natural.`;

    try {
      const res = await fetch('https://coachholmberg-api.alexander-894.workers.dev/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          system: window.ALEXANDER_CANON,
          messages: [{ role: 'user', content: userMsg }],
        }),
      });
      const data = await res.json();
      const reply = data.content && data.content[0] && data.content[0].text;
      const clean = String(reply || '').trim().replace(/^["\u201c]|["\u201d]$/g, '');
      setThinking(false);
      await typeLine(clean);
      await wait(paceCfg.gap);
      replies.current = lang === 'sv' ? [
        { say: 'jag tror jag vill prata med dig', to: 'wants_contact' },
        { say: 'berätta mer', to: 'how' },
        { say: 'låt det bli tyst', do: 'silence' },
      ] : [
        { say: 'i think i want to talk to you', to: 'wants_contact' },
        { say: 'tell me more', to: 'how' },
        { say: 'let it be silent', do: 'silence' },
      ];
      setPhase('waiting');
    } catch (e) {
      setThinking(false);
      await typeLine(t.aiLost);
      setPhase('waiting');
    }
  };

  const enterSilence = async () => {
    setPhase('silent');
    setSilenceCountdown(20);
    const start = performance.now();
    const tick = () => {
      const left = 20 - Math.floor((performance.now() - start) / 1000);
      if (left <= 0) {
        setSilenceCountdown(0);
        setPhase('playing');
        setTimeout(async () => {
          await typeLine(lang === 'sv' ? 'där.' : 'there.');
          await wait(560);
          await typeLine(lang === 'sv' ? 'tack för att du höll det med mig.' : 'thank you for holding it with me.');
          await wait(paceCfg.gap);
          replies.current = lang === 'sv' ? [
            { say: 'jag är redo att prata', to: 'wants_contact' },
            { say: 'berätta mer om hur du arbetar', to: 'how' },
            { say: 'en gång till', do: 'silence' },
          ] : [
            { say: 'i\u2019m ready to talk', to: 'wants_contact' },
            { say: 'tell me more about how you work', to: 'how' },
            { say: 'once more', do: 'silence' },
          ];
          setPhase('waiting');
        }, 200);
        return;
      }
      setSilenceCountdown(left);
      setTimeout(tick, 250);
    };
    tick();
  };

  const interrupt = () => {
    cancelRef.current = true;
    setTimeout(() => { cancelRef.current = false; }, 100);
  };

  // ── render ────────────────────────────────────────────────────────────
  const showInput = phase === 'waiting' || phase === 'asking';
  const showSuggested = phase === 'waiting' && replies.current.length > 0;
  const inSubpage = modal && modal !== 'menu' && modal !== 'settings';

  return (
    <div key={lang} className={'room' + (inSubpage ? ' room-hidden' : '')}>
      {/* Pixelated portrait background */}
      {phase !== 'SHOW_9VECKOR' && (
        <canvas ref={canvasRef} className="pixel-bg" />
      )}
      {/* The conversation — everything lives in this column */}
      <div className="stage" ref={stageRef}>
        <div className="stream" ref={scrollRef}>
          <div className="marker">
              <span>{sessionKod.datum}</span>
              <span className="sep">/</span>
              <span>{sessionKod.kod}</span>
              <span className="sep">/</span>
              <span>{navigator.userAgent.includes('Firefox') ? 'firefox' : navigator.userAgent.includes('Edg') ? 'edge' : navigator.userAgent.includes('Chrome') ? 'chrome' : navigator.userAgent.includes('Safari') ? 'safari' : 'browser'}</span>
              <span className="sep">/</span>
              <a href="https://www.instagram.com/coachholmberg" target="_blank" style={{color:'inherit', textDecoration:'none'}}>/coachholmberg</a>
              <span className="sep">/</span>
              <a href="/articles/" style={{textDecoration:'none', color:'#f4f1ec'}}>artiklar</a>
            </div>

          {log.map((line, i) => (
            <LogLine key={i} line={line} lang={lang} visitorIp={visitorIp}
                     bendRef={registerBendable} unbendRef={unregisterBendable} />
          ))}

          {thinking && (
            <div className="line line-a">
              <div className="text">
                <span className="thinking"><i/><i/><i/></span>
              </div>
            </div>
          )}

          {phase === 'silent' && <SilenceBlock count={silenceCountdown} lang={lang} onSkip={interrupt} />}
        </div>

        {showSuggested && (
          <div className="suggest">
            <div className="suggest-hint">{lang === 'sv' ? '— eller säg själv:' : '— or say it yourself:'}</div>
            <div className="suggest-list">
              {replies.current.map((r, i) => (
                <button key={i} className="suggest-btn bendable"
                        ref={registerBendable}
                        style={{ animationDelay: `${100 + i * 70}ms` }}
                        onClick={() => choose(r)}>
                  <span className="suggest-q">›</span>
                  <span>{r.say}</span>
                </button>
              ))}
            </div>
          </div>
        )}

        {showInput && (
          <div className="input-row">
            <div className="input-hint">{lang === 'sv' ? 'eller skriv fritt —' : 'or type freely —'}</div>
            <div className="input-tag">{visitorIp}</div>
            <input
              ref={inputRef}
              value={input}
              onChange={(e) => setInput(e.target.value)}
              onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); submitInput(); } }}
              placeholder={
                phase === 'asking'
                  ? (askState?.key === 'name' ? t.placeholderName
                     : askState?.key === 'phone' ? t.placeholderPhone
                     : askState?.key === 'email' ? t.placeholderEmail
                     : t.placeholder)
                  : t.placeholder
              }
              spellCheck={false}
              autoComplete="off"
            />
          </div>
        )}

        {phase === 'form' && <ContactForm onSent={() => setPhase('ended')} />}

        {phase === 'SHOW_9VECKOR' && <NioVeckorScene onDone={() => setPhase('ended')} />}

        {phase === 'ended' && (
          <div className="ended">
            <button onClick={() => { setLog([]); playNode('start'); }}>
              {lang === 'sv' ? 'börja om' : 'start again'}
            </button>
          </div>
        )}
      </div>

      {/* Modal: the menu */}
      {modal === 'menu' && (
        <Overlay onClose={() => setModal(null)}>
          <button className="menu-close" onClick={() => setModal(null)} aria-label="stäng">×</button>
          <nav className="menu">
            <button onClick={() => { flashImage(); setModal('coaching'); }}>{t.coaching}</button>
            <button onClick={() => { flashImage(); setModal('hire'); }}>{t.hire}</button>
            <button onClick={() => { flashImage(); setModal('contact'); }}>{t.contact}</button>
            <button onClick={() => { flashImage(); window.location.href='/podd'; }}>ett litet mellanrum</button>
            <button onClick={() => { flashImage(); window.location.href='/manifest'; }}>manifest</button>
            <div className="menu-sep" />
            <button className="menu-small" onClick={() => setModal('settings')}>
              {lang === 'sv' ? 'inställningar' : 'settings'}
            </button>
          </nav>
        </Overlay>
      )}

      {modal === 'settings' && (
        <Overlay onClose={() => setModal('menu')}>
          <div className="settings">
            <div className="settings-row">
              <div className="settings-lab">{t.settingsLang}</div>
              <div className="settings-opts">
                <button data-on={lang === 'sv' ? '1' : '0'} onClick={() => setLang('sv')}>sv</button>
                <button data-on={lang === 'en' ? '1' : '0'} onClick={() => setLang('en')}>en</button>
                <button data-on={lang === 'es' ? '1' : '0'} onClick={() => setLang('es')}>es</button>
              </div>
            </div>
            <div className="settings-row">
              <div className="settings-lab">{t.settingsPace}</div>
              <div className="settings-opts">
                {['slow','natural','quick'].map((p) => (
                  <button key={p} data-on={pace === p ? '1' : '0'} onClick={() => setPace(p)}>
                    {t['pace_' + p]}
                  </button>
                ))}
              </div>
            </div>
            <div className="settings-row">
              <div className="settings-lab">{lang === 'sv' ? 'toning' : lang === 'es' ? 'tono' : 'tint'}</div>
              <div className="settings-opts">
                <button data-on={tint === 'none' ? '1' : '0'} onClick={() => setTint('none')}>{lang === 'sv' ? 'ingen' : lang === 'es' ? 'ninguno' : 'none'}</button>
                <button data-on={tint === 'green' ? '1' : '0'} onClick={() => setTint('green')} style={{color:'#4AFF8C'}}>●</button>
                <button data-on={tint === 'red' ? '1' : '0'} onClick={() => setTint('red')} style={{color:'#FF4A4A'}}>●</button>
                <button data-on={tint === 'blue' ? '1' : '0'} onClick={() => setTint('blue')} style={{color:'#4A9EFF'}}>●</button>
              </div>
            </div>
          </div>
        </Overlay>
      )}

      {/* Subpages — dynamic takeover */}
      {modal === 'coaching' && <Subpage title={t.coaching} onBack={() => setModal(null)}>
        <SubpageCoaching lang={lang} />
      </Subpage>}
      {modal === 'hire' && <Subpage title={t.hire} onBack={() => setModal(null)}>
        <SubpageHire lang={lang} />
      </Subpage>}
      {modal === 'contact' && <Subpage title={t.contact} onBack={() => setModal(null)}>
        <SubpageContact lang={lang} />
      </Subpage>}

      {/* Persistent trigger bottom-right — always visible */}
      <button className="trigger" onClick={() => { if (modal !== 'menu') flashImage(); setModal(modal === 'menu' ? null : 'menu'); }}
              aria-label="meny" title="meny">
        <span className="trigger-plus">{lang === 'sv' ? 'meny' : 'menu'}</span>
      </button>

      <a className="easter-egg" href="https://www.holmbergfriends.com" target="_blank">holmberg &amp; friends / design andreas holmström</a>
    </div>
  );
}

// ── 9 veckor — fullscreen animated scene ───────────────────────────────
function NioVeckorScene({ onDone }) {
  const [step, setStep] = React.useState(0); // 0=fade, 1=nine, 2=grid, 3=principles, 4=form
  const [formData, setFormData] = React.useState({ epost: '', mal: '' });
  const [sending, setSending] = React.useState(false);
  const [sent, setSent] = React.useState(false);

  React.useEffect(() => {
    const timers = [
      setTimeout(() => setStep(1), 2500),   // fade done → show 9
      setTimeout(() => setStep(2), 4500),   // 9 → grid
      setTimeout(() => setStep(3), 7000),   // grid → principles
      setTimeout(() => setStep(4), 10000),  // principles → form
    ];
    return () => timers.forEach(clearTimeout);
  }, []);

  const [error, setError] = React.useState(false);

  const submit = async () => {
    if (!formData.epost) return;
    setSending(true);
    setError(false);
    try {
      const res = await fetch('https://9veckor.pages.dev/api/anmal', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ epost: formData.epost, fornamn: '', efternamn: '', mal_1: formData.mal, mal_2: '', mal_3: '' })
      });
      if (!res.ok) throw new Error();
      setSent(true);
    } catch(e) {
      setError(true);
    }
    setSending(false);
  };

  const principles = [
    ['acceptans', 'självmedvetenhet', 'stillhet'],
    ['insikt', 'ansvar', 'sårbarhet'],
    ['integritet', 'syfte', 'handling'],
  ];

  return (
    <div className="nio-scene">
      {/* Phase 0: black → white fade */}
      <div className={'nio-fade' + (step >= 1 ? ' nio-fade-white' : '')} />

      {/* Phase 1: big 9 */}
      {step >= 1 && (
        <div className={'nio-nine' + (step >= 2 ? ' nio-nine-shrink' : '')}>
          <span>9</span>
        </div>
      )}

      {/* Phase 2: 3 rows of 21 blocks */}
      {step >= 2 && (
        <div className={'nio-grid' + (step >= 3 ? ' nio-grid-done' : '')}>
          {[0,1,2].map(row => (
            <div key={row} className="nio-row">
              {Array.from({length: 21}).map((_, i) => (
                <div key={i} className="nio-block"
                     style={{ animationDelay: `${row * 300 + i * 60}ms` }} />
              ))}
            </div>
          ))}
        </div>
      )}

      {/* Phase 3: principles */}
      {step >= 3 && (
        <div className={'nio-principles' + (step >= 4 ? ' nio-principles-up' : '')}>
          {principles.map((row, ri) => (
            <div key={ri} className="nio-p-row"
                 style={{ animationDelay: `${ri * 400}ms` }}>
              {row.map((p, pi) => (
                <span key={pi} className="nio-p">{p}</span>
              ))}
            </div>
          ))}
        </div>
      )}

      {/* Phase 4: commit form */}
      {step >= 4 && !sent && (
        <div className="nio-form">
          <p className="nio-form-title">jag är redo</p>
          <input
            placeholder="din epost"
            value={formData.epost}
            onChange={e => setFormData(d => ({...d, epost: e.target.value}))}
          />
          <input
            placeholder="vad vill du ha sagt till dig om 9 veckor?"
            value={formData.mal}
            onChange={e => setFormData(d => ({...d, mal: e.target.value}))}
          />
          <button onClick={submit} disabled={sending || !formData.epost}>
            {sending ? '...' : 'börja'}
          </button>
          {error && <p className="nio-form-error">något gick fel. försök igen.</p>}
        </div>
      )}

      {sent && (
        <div className="nio-form">
          <p className="nio-form-title">programmet startar.</p>
          <p className="nio-form-title" style={{opacity:0.6}}>kolla din inkorg.</p>
          <p className="nio-form-title" style={{opacity:0.6}}>63 dagar börjar nu.</p>
        </div>
      )}
    </div>
  );
}

// ── log line — left-aligned, no indent, IP/AH as small label above ─────
function LogLine({ line, lang, visitorIp, bendRef, unbendRef }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (el && bendRef) bendRef(el);
    return () => { if (el && unbendRef) unbendRef(el); };
  }, [bendRef, unbendRef]);
  if (line.kind === 'spacer') return <div className="line line-spacer" />;
  const isVisitor = line.who === 'u';
  return (
    <div className={'line bendable ' + (isVisitor ? 'line-u' : 'line-a')} ref={ref}>
      {isVisitor && <div className="line-tag">{visitorIp}</div>}
      <div className="text">
        {line.text}
        {line.typing && <span className="caret" />}
      </div>
    </div>
  );
}

// ── silence inline ─────────────────────────────────────────────────────
function SilenceBlock({ count, lang, onSkip }) {
  return (
    <div className="silence">
      <div className="silence-orb" />
      <div className="silence-cap">{count}</div>
      <button className="silence-skip" onClick={onSkip}>{lang === 'sv' ? '— bryt tystnaden' : '— break the silence'}</button>
    </div>
  );
}

// ── Overlay — soft backdrop, dismiss on backdrop click ─────────────────
function Overlay({ children, onClose }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);
  return (
    <div className="overlay" onClick={onClose}>
      <div className="overlay-inner" onClick={(e) => e.stopPropagation()}>
        {children}
      </div>
      <button className="overlay-close" onClick={onClose} aria-label="stäng">×</button>
    </div>
  );
}

// ── Subpage shell — full takeover ─────────────────────────────────────
function Subpage({ title, onBack, children }) {
  React.useEffect(() => {
    window.scrollTo(0, 0);
    const onKey = (e) => { if (e.key === 'Escape') onBack(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onBack]);
  return (
    <div className="subpage">
      <div className="subpage-content">
        <h1 className="subpage-title">{title}</h1>
        {children}
      </div>
    </div>
  );
}

// ── Flash utility — subtle fullscreen image flash ─────────────────────
function flashImage() {
  const img = document.createElement('img');
  img.src = '/img/alexander-studio.jpg';
  img.style.cssText = 'position:fixed;top:0;left:0;width:100vw;height:100vh;object-fit:cover;opacity:0;filter:grayscale(100%) contrast(1.2);pointer-events:none;z-index:50;transition:opacity 150ms ease;';
  document.body.appendChild(img);
  requestAnimationFrame(() => { img.style.opacity = '0.07'; });
  setTimeout(() => { img.style.transition = 'opacity 300ms ease'; img.style.opacity = '0'; }, 450);
  setTimeout(() => { img.remove(); }, 800);
}

// ── LineMeet — vertical curtain lines, fixed fullscreen SVG ───────────
function LineMeet({ variant }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const svg = ref.current;
    if (!svg) return;
    const w = svg.clientWidth, h = svg.clientHeight;
    svg.setAttribute('viewBox', `0 0 ${w} ${h}`);
    let animId;
    let parallaxCleanup = null;

    function addParallax() {
      const onMove = (e) => {
        const px = (e.clientX / window.innerWidth - 0.5) * 120;
        const py = (e.clientY / window.innerHeight - 0.5) * 80;
        svg.style.transform = `translate(${px}px, ${py}px)`;
      };
      window.addEventListener('mousemove', onMove);
      parallaxCleanup = () => window.removeEventListener('mousemove', onMove);
    }

    if (variant === 'kontakt') {
      const left = document.createElementNS('http://www.w3.org/2000/svg','line');
      const right = document.createElementNS('http://www.w3.org/2000/svg','line');
      [left, right].forEach(l => {
        l.setAttribute('stroke', 'rgba(244,241,236,0.6)');
        l.setAttribute('stroke-width', '0.5');
        l.setAttribute('y1', '0'); l.setAttribute('y2', String(h));
        svg.appendChild(l);
      });
      let progress = 0;
      let flashed = false;
      const mid = w / 2;
      function draw() {
        progress = Math.min(progress + 0.012, 1);
        const ease = 1 - Math.pow(1 - progress, 3);
        left.setAttribute('x1', ease * mid); left.setAttribute('x2', ease * mid);
        right.setAttribute('x1', w - ease * mid); right.setAttribute('x2', w - ease * mid);
        if (progress >= 0.98 && !flashed) {
          flashed = true;
          flashImage();
        }
        if (progress < 1) { animId = requestAnimationFrame(draw); }
        else { addParallax(); }
      }
      animId = requestAnimationFrame(draw);
    }

    if (variant === 'coaching') {
      const left = document.createElementNS('http://www.w3.org/2000/svg','line');
      const right = document.createElementNS('http://www.w3.org/2000/svg','line');
      [left, right].forEach(l => {
        l.setAttribute('stroke', 'rgba(244,241,236,0.6)');
        l.setAttribute('stroke-width', '0.5');
        l.setAttribute('y1', '0'); l.setAttribute('y2', String(h));
        svg.appendChild(l);
      });
      let progress = 0;
      let flashed = false;
      const mid = w / 2;
      function draw() {
        progress = Math.min(progress + 0.010, 1);
        const ease = 1 - Math.pow(1 - progress, 3);
        if (ease <= 0.5) {
          const p = ease * 2;
          left.setAttribute('x1', p * mid); left.setAttribute('x2', p * mid);
          right.setAttribute('x1', w - p * mid); right.setAttribute('x2', w - p * mid);
          if (ease >= 0.48 && !flashed) { flashed = true; flashImage(); }
        } else {
          const spread = (ease - 0.5) * 2;
          const lx = mid - 40 * spread;
          const rx = mid + 40 * spread;
          left.setAttribute('x1', lx); left.setAttribute('x2', lx);
          right.setAttribute('x1', rx); right.setAttribute('x2', rx);
        }
        if (progress < 1) { animId = requestAnimationFrame(draw); }
        else { addParallax(); }
      }
      animId = requestAnimationFrame(draw);
    }

    if (variant === 'anlita') {
      const leftLine = document.createElementNS('http://www.w3.org/2000/svg','line');
      leftLine.setAttribute('stroke', 'rgba(244,241,236,0.6)');
      leftLine.setAttribute('stroke-width', '0.5');
      leftLine.setAttribute('y1', '0'); leftLine.setAttribute('y2', String(h));
      const targetX = w / 3;
      svg.appendChild(leftLine);

      const basePositions = [0.55, 0.65, 0.72, 0.80, 0.90];
      const lines = basePositions.map((pos, i) => {
        const l = document.createElementNS('http://www.w3.org/2000/svg','line');
        l.setAttribute('stroke', 'rgba(244,241,236,' + (0.3 + i * 0.08) + ')');
        l.setAttribute('stroke-width', '0.5');
        l.setAttribute('y1', '0'); l.setAttribute('y2', String(h));
        svg.appendChild(l);
        return { el: l, base: pos * w };
      });

      let mouseY = h / 2;
      let progress = 0;

      document.addEventListener('mousemove', e => {
        mouseY = e.clientY;
        // Parallax on top of line logic
        const px = (e.clientX / window.innerWidth - 0.5) * 120;
        const py = (e.clientY / window.innerHeight - 0.5) * 80;
        svg.style.transform = `translate(${px}px, ${py}px)`;
      });

      function drawLines() {
        progress = Math.min(progress + 0.012, 1);
        const ease = 1 - Math.pow(1 - progress, 3);
        leftLine.setAttribute('x1', ease * targetX);
        leftLine.setAttribute('x2', ease * targetX);

        const spread = 1 - (mouseY / h);
        lines.forEach(({ el, base }, i) => {
          const offset = (spread - 0.5) * 30 * (i - 2);
          const x = ease * (base + offset);
          el.setAttribute('x1', x); el.setAttribute('x2', x);
        });
        animId = requestAnimationFrame(drawLines);
      }
      animId = requestAnimationFrame(drawLines);
    }

    return () => {
      if (animId) cancelAnimationFrame(animId);
      if (parallaxCleanup) parallaxCleanup();
    };
  }, [variant]);

  return <svg ref={ref} className="linemeet-svg" />;
}

// ── Subpage content ────────────────────────────────────────────────────
function ContentPage({ section, lang }) {
  const key = (lang && lang !== 'sv') ? section + '_' + lang : section;
  const c = window.CONTENT && (window.CONTENT[key] || window.CONTENT[section]);
  if (!c) return <div className="prose"><p>...</p></div>;
  const lineVariant = section === 'contact' ? 'kontakt' : section === 'hire' ? 'anlita' : section;
  return (
    <div className="prose">
      {c.paragraphs.map((p, i) => <p key={i}>{p}</p>)}
      {c.cta && <p className="dim">{c.cta.text}</p>}
      {c.dim && c.dim.map((d, i) => <p key={'d'+i} className="dim">{d}</p>)}
      <LineMeet variant={lineVariant} />
      <ContactForm />
    </div>
  );
}

const SubpageCoaching = ({ lang }) => <ContentPage section="coaching" lang={lang} />;
const SubpageHire = ({ lang }) => <ContentPage section="hire" lang={lang} />;
const SubpageContact = ({ lang }) => <ContentPage section="contact" lang={lang} />;


const samtalRoot = ReactDOM.createRoot(document.getElementById('root'));
samtalRoot.render(<SamtalSite />);
