/* global React, ReactDOM */
const { useEffect, useRef, useState } = React;

function Wordmark({ size = 28 }) {
  // Use the real brand wordmark PNG
  return (
    <img
      src="assets/knora-wordmark.png"
      alt="knora."
      style={{ height: size, width: "auto", display: "block" }}
    />
  );
}

function Nav() {
  return (
    <header className="nav">
      <Wordmark />
      <nav>
        <a href="#product">Product</a>
        <a href="#for-schools">For schools</a>
        <a href="#curricula">Curricula</a>
        <a href="#pricing">Pricing</a>
        <a href="#about">About</a>
      </nav>
      <div className="right">
        <a className="login" href="/login" target="_top">Log in</a>
        <a className="nav-cta" href="mailto:cristian@knora.es?subject=Quiero%20conocer%20Knora" target="_top">Book a demo</a>
      </div>
    </header>
  );
}

function Chips() {
  return (
    <div className="chips" aria-label="Curricula">
      <span className="chip">IB</span>
      <span className="chip">LOMLOE</span>
      <span className="chip">IGCSE</span>
      <span className="chip">AP</span>
      <span className="chip">+ any syllabus</span>
    </div>
  );
}

function TrustStrip() {
  return (
    <div className="trust">
      <span className="dot" aria-hidden="true" />
      <span><strong style={{ color: "#fff", fontWeight: 600 }}>Built for schools</strong></span>
      <span className="sep">·</span>
      <span>Tutoring programs</span>
      <span className="sep">·</span>
      <span>Education partners</span>
    </div>
  );
}

function ArrowRight() {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
      <path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

/* OrbVideo — chroma-keys the black background of the mp4 to transparent.
   WebGL2 fragment shader runs the luminance ramp on the GPU, which lets us
   render at the canvas's true display size × devicePixelRatio (the previous
   2d-canvas version capped at 360px and CSS-upscaled, hence the pixelation
   the user reported). The chroma-key threshold matches the original: linear
   ramp from luminance 24 to 70. */
function OrbVideo({ src = "/video/orb-720.mp4", className = "orb-mask" }) {
  const videoRef = useRef(null);
  const canvasRef = useRef(null);
  // El chroma-key WebGL (recorta el negro del video → transparencia limpia)
  // corre SIEMPRE. El ofensor de performance no era el WebGL, era el PESO del
  // video: orb-loop.mp4 = 4.8MB subido a textura cada frame (1.5fps en móvil
  // con throttle 4x). En móvil/pointer-coarse usamos orb-sm.mp4 (~102KB, 256px)
  // — el upload de textura escala con los píxeles del frame, así que es ~8x más
  // barato — más el frame-skip de abajo. Así el orb queda recortado y nítido en
  // todas las plataformas, sin el recuadro negro que dejaba el <video> directo.
  const isMobileOrb = React.useMemo(
    () => window.matchMedia("(max-width: 768px), (pointer: coarse)").matches,
    []
  );
  const effectiveSrc = isMobileOrb ? "/video/orb-sm.mp4" : src;
  useEffect(() => {
    const video = videoRef.current;
    const canvas = canvasRef.current;
    if (!video || !canvas) return;

    const gl = canvas.getContext("webgl2", {
      premultipliedAlpha: false,
      alpha: true,
      antialias: true,
      preserveDrawingBuffer: false,
    });
    if (!gl) return; // No WebGL2 fallback · video stays hidden, canvas blank.

    const VERT = `#version 300 es
      in vec2 a_pos;
      out vec2 v_uv;
      void main() {
        v_uv = (a_pos + 1.0) * 0.5;
        gl_Position = vec4(a_pos, 0.0, 1.0);
      }`;
    const FRAG = `#version 300 es
      precision mediump float;
      in vec2 v_uv;
      uniform sampler2D u_tex;
      out vec4 outColor;
      void main() {
        vec4 c = texture(u_tex, v_uv);
        float lum = (0.299 * c.r + 0.587 * c.g + 0.114 * c.b) * 255.0;
        // Wider alpha ramp = soft anti-aliased edge instead of a hard jagged cutoff.
        float a = smoothstep(14.0, 80.0, lum);
        outColor = vec4(c.rgb, a);
      }`;
    function compile(type, src) {
      const sh = gl.createShader(type);
      gl.shaderSource(sh, src);
      gl.compileShader(sh);
      if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) { gl.deleteShader(sh); return null; }
      return sh;
    }
    const vs = compile(gl.VERTEX_SHADER, VERT);
    const fs = compile(gl.FRAGMENT_SHADER, FRAG);
    if (!vs || !fs) return;
    const prog = gl.createProgram();
    gl.attachShader(prog, vs);
    gl.attachShader(prog, fs);
    gl.linkProgram(prog);
    if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) return;
    gl.useProgram(prog);

    const quad = new Float32Array([-1,-1, 1,-1, -1,1, -1,1, 1,-1, 1,1]);
    const vbo = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
    gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);
    const aPos = gl.getAttribLocation(prog, "a_pos");
    gl.enableVertexAttribArray(aPos);
    gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);

    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
    const tex = gl.createTexture();
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.uniform1i(gl.getUniformLocation(prog, "u_tex"), 0);

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.clearColor(0, 0, 0, 0);

    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    const setSize = () => {
      const rect = canvas.getBoundingClientRect();
      const w = Math.max(1, Math.round(rect.width * dpr));
      const h = Math.max(1, Math.round(rect.height * dpr));
      if (canvas.width !== w || canvas.height !== h) {
        canvas.width = w;
        canvas.height = h;
        gl.viewport(0, 0, w, h);
      }
    };
    setSize();
    const ro = new ResizeObserver(setSize);
    ro.observe(canvas);

    // Pause draw work when the orb is scrolled off-screen.
    let visible = true;
    const io = new IntersectionObserver((entries) => {
      for (const e of entries) visible = e.isIntersecting;
    }, { rootMargin: "100px" });
    io.observe(canvas);

    let raf = 0;
    let mounted = true;
    let textureReady = false;
    let lastFrameTime = -1;
    const tryPlay = () => {
      const p = video.play();
      if (p && p.catch) p.catch(() => {});
    };
    const tick = () => {
      if (!mounted) return;
      // Frame-skip: el video corre a ~24-30fps pero el rAF a 60 — subir la
      // textura solo cuando el frame cambió ahorra la mitad de los uploads.
      if (visible && video.readyState >= 2 && video.videoWidth && video.currentTime !== lastFrameTime) {
        lastFrameTime = video.currentTime;
        try {
          if (!textureReady) {
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
            textureReady = true;
          } else {
            gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, video);
          }
          gl.clear(gl.COLOR_BUFFER_BIT);
          gl.drawArrays(gl.TRIANGLES, 0, 6);
        } catch (e) { /* CORS taint or driver hiccup — keep looping */ }
      }
      raf = requestAnimationFrame(tick);
    };
    video.addEventListener("loadeddata", tryPlay);
    if (video.readyState >= 2) tryPlay();
    raf = requestAnimationFrame(tick);

    return () => {
      mounted = false;
      cancelAnimationFrame(raf);
      ro.disconnect();
      io.disconnect();
      video.removeEventListener("loadeddata", tryPlay);
      gl.deleteTexture(tex);
      gl.deleteBuffer(vbo);
      gl.deleteProgram(prog);
      gl.deleteShader(vs);
      gl.deleteShader(fs);
    };
  }, [effectiveSrc]);
  return (
    <>
      <video ref={videoRef} src={effectiveSrc} autoPlay loop muted playsInline crossOrigin="anonymous" style={{ display: "none" }} />
      <canvas ref={canvasRef} className={className} />
    </>
  );
}

function PlayDot() {
  return (
    <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
      <circle cx="7" cy="7" r="6.25" stroke="currentColor" strokeWidth="1.2" opacity="0.6"/>
      <path d="M5.5 4.5v5l4-2.5-4-2.5z" fill="currentColor"/>
    </svg>
  );
}

function WaveBars({ count = 18, mint = true }) {
  const heights = React.useMemo(
    () => Array.from({ length: count }, () => 0.4 + Math.random() * 0.6),
    [count]
  );
  return (
    <div className="wave" aria-hidden="true">
      {heights.map((h, i) => (
        <i
          key={i}
          style={{
            height: `${h * 28}px`,
            background: mint ? "var(--iris-mint)" : "rgba(255,255,255,0.7)",
            animation: `wave ${0.9 + (i % 5) * 0.12}s ease-in-out infinite`,
            animationDelay: `${i * 0.06}s`,
            transformOrigin: "bottom",
          }}
        />
      ))}
    </div>
  );
}

/* =============================================================
   V1 — Static premium institutional
   ============================================================= */
function HeroV1() {
  return (
    <div className="hero v1">
      <div className="bg" />
      <div className="grain" />
      <Nav />

      <div className="stage">
        <div className="copy">
          <div className="eyebrow">Voice-first AI tutor</div>
          <h1>
            The AI <br/>
            that <span className="grad">teaches.</span>
          </h1>
          <p className="sub">
            Knora's voice tutor guides every student through active recall, Socratic
            questioning and spaced review — aligned to{" "}
            <span style={{ color: "#fff", fontWeight: 600 }}>IB</span>,{" "}
            <span style={{ color: "#fff", fontWeight: 600 }}>LOMLOE</span> and{" "}
            <span style={{ color: "#fff", fontWeight: 600 }}>Cambridge IGCSE</span>.
          </p>
          <div className="chips" style={{ marginBottom: 36 }}>
            <span className="chip">IB</span>
            <span className="chip">LOMLOE</span>
            <span className="chip">Cambridge IGCSE</span>
          </div>
          <div className="ctas">
            <a className="cta-primary" href="mailto:cristian@knora.es?subject=Quiero%20conocer%20Knora" target="_top">
              See Knora in action <ArrowRight />
            </a>
            <a className="cta-ghost" href="mailto:cristian@knora.es?subject=Quiero%20conocer%20Knora" target="_top">
              <PlayDot /> 90-second tour
            </a>
          </div>
          <TrustStrip />
        </div>

        <div className="orb-wrap">
          <div className="orb-glow" />
          <div className="orb-img">
            <OrbVideo />
          </div>

          <div className="voice-card" aria-hidden="true">
            <WaveBars count={14} />
            <div>
              <div className="label">Nora · live</div>
              <div className="text">"Let's start with what you already know."</div>
            </div>
          </div>

          <div className="stat-card" aria-hidden="true">
            <div className="lbl">Avg. mastery gain · 8 weeks</div>
            <div className="num">+34%</div>
            <div className="meta">Across 1,200 IB Chemistry HL students</div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* =============================================================
   Hero — matches reference: split layout, real orb, floating cards
   ============================================================= */
function HeroV2() {
  return (
    <div className="hero v2">
      <div className="bg" />
      <div className="vignette" />
      <div className="grain" />
      <Nav />

      <div className="stage">
        <div className="copy">
          <div className="eyebrow">Voice-first AI tutor</div>
          <h1 className="hero-h1">
            The AI every<br/>
            classroom<br/>
            <span className="grad">runs on.</span>
          </h1>
          <p className="sub">
            Knora is the voice tutor schools standardize on: active recall, Socratic
            questioning and spaced review, aligned to{" "}
            <span className="bold-w">any curriculum</span> you teach.
            Upload a syllabus and Knora turns it into structured lessons.
          </p>
          <Chips />
          <div className="ctas">
            <a className="cta-primary" href="mailto:cristian@knora.es?subject=Quiero%20conocer%20Knora" target="_top">
              See Knora in action <ArrowRight />
            </a>
            <a className="cta-ghost" href="mailto:cristian@knora.es?subject=Quiero%20conocer%20Knora" target="_top">
              <PlayDot /> 90-second tour
            </a>
          </div>
          <TrustStrip />
        </div>

        <div className="orb-wrap">
          {/* Soft halo behind the orb */}
          <div className="orb-halo" />
          <div className="orb-img-real">
            <OrbVideo />
          </div>

          {/* Top-right floating card: Knora · Live */}
          <div className="float-card live-card" aria-hidden="true">
            <WaveBars count={14} />
            <div className="content">
              <div className="label">KNORA · LIVE</div>
              <div className="text">"Let's start with what you already know."</div>
            </div>
          </div>

          {/* Bottom-left floating card: Mastery gain */}
          <div className="float-card stat-card-real" aria-hidden="true">
            <div className="lbl">ROOTED IN LEARNING SCIENCE</div>
            <div className="num grad-num">+50%</div>
            <div className="meta">Active recall vs. re-reading on one-week retention — Roediger and Karpicke, 2006</div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* =============================================================
   V3 — Bold typographic with iris ring as standalone mark
   ============================================================= */
function HeroV3() {
  return (
    <div className="hero v3">
      <div className="bg" />
      <div className="grain" />
      <Nav />

      <div className="top-meta">
        <div className="eyebrow">Knora · 2026</div>
      </div>

      <div className="edge-tag">Voice-first AI tutor</div>

      <div className="ring-mark" aria-hidden="true">
        <OrbVideo />
      </div>

      <div className="stage">
        <h1 className="mega">
          The AI<br/>
          <span className="line2">that <span className="teaches">teaches.</span></span>
        </h1>

        <div className="below">
          <div className="left">
            <p className="sub">
              Curriculum-aligned for IB, LOMLOE and Cambridge IGCSE. A voice
              tutor that adapts, questions and corrects — built for schools,
              tutoring programs and education partners.
            </p>
            <div className="ctas">
              <a className="cta-primary" href="mailto:cristian@knora.es?subject=Quiero%20conocer%20Knora" target="_top">
                See Knora in action <ArrowRight />
              </a>
              <a className="cta-ghost" href="mailto:cristian@knora.es?subject=Quiero%20conocer%20Knora" target="_top">
                <PlayDot /> 90-second tour
              </a>
            </div>
          </div>
          <div className="right">
            <Chips />
            <TrustStrip />
          </div>
        </div>
      </div>
    </div>
  );
}

/* =============================================================
   Expose hero parts for sections.jsx (which uses OrbVideo)
   ============================================================= */
Object.assign(window, { OrbVideo });

/* =============================================================
   LiveCompanion — B2B landing assistant.
   Triages visitors by persona (school director / tutoring program /
   education partner / curious), then surfaces persona-specific FAQs
   with pre-baked answers. Free-text input falls through to a generic
   "I'll connect you with sales" reply for now; a `/api/knora-chat`
   endpoint can be wired later for real Claude streaming.
   ============================================================= */

const LC_PERSONAS = [
  { id: "school",   label: "School director",      hint: "🏫" },
  { id: "tutoring", label: "Tutoring program",     hint: "📚" },
  { id: "partner",  label: "Education partner",    hint: "🤝" },
  { id: "curious",  label: "Just exploring",       hint: "✨" },
];

const LC_INTROS = {
  school:   "Got it — schools. Here's what other directors typically ask before a pilot:",
  tutoring: "Welcome — for tutoring teams, these are the most common questions:",
  partner:  "Great — let's talk distribution. Top questions partners ask:",
  curious:  "Of course. Quick rundown of what most people want to know:",
};

const LC_FAQS = {
  school: [
    {
      q: "How does Knora align with my curriculum?",
      a: "Every Knora session tags to your curriculum's descriptors — IB DP/MYP/CP, LOMLOE competencias clave, or Cambridge IGCSE/AS/A Level. Subject, topic, and assessment objective are all logged, so progress flows cleanly into your existing reports. Custom alignments take 2–4 weeks during onboarding.",
    },
    {
      q: "What does deployment look like?",
      a: "30-minute setup. Knora runs in the browser — no installs, no plugins. SSO via Google Workspace or Microsoft 365. GDPR, ROPA, and DPIA documentation provided. Most pilots are live within 5 business days.",
    },
    {
      q: "How does pricing work?",
      a: "Per-seat licensing that scales with school size. Pilots (up to 30 students, 30 days) are free. Beyond that we set pricing per engagement, with volume rates as you grow — we'll walk you through it on a call.",
    },
    {
      q: "What does the pilot include?",
      a: "Free 30-day pilot for up to 30 students. Onboarding, two teacher training sessions, pedagogical support via shared Slack, and a closing impact report.",
    },
  ],
  tutoring: [
    {
      q: "How does Knora fit a tutoring workflow?",
      a: "Tutors upload their materials (syllabus, past sessions, slides). Knora turns it into 10–15 min voice practice for students between live sessions — same voice, same questions, same pacing. The tutor sees what stuck before the next session.",
    },
    {
      q: "Can I white-label it?",
      a: "Yes — at the Partner tier (50+ active tutors). Custom domain, your logo, your colors, your tone-of-voice for the AI tutor. Branding setup takes ~2 weeks.",
    },
    {
      q: "How does pricing work?",
      a: "Per-tutor licensing that scales with your program, with volume rates as you grow. No setup fees. We'll tailor it to your team on a quick call.",
    },
    {
      q: "Do you integrate with booking platforms?",
      a: "Native integrations with FindATutor.ch, Tutorful, and Calendly. REST API + webhooks for everything else. Average integration: 2 days.",
    },
  ],
  partner: [
    {
      q: "What partnership tiers do you offer?",
      a: "Three tiers: Referral (15% recurring), Co-sell (25% + co-marketing), and White-label (40% + setup fee). Geographic exclusivity from Co-sell up.",
    },
    {
      q: "How does revenue share work?",
      a: "Recurring 15–40% depending on tier, paid monthly. Partners closing 5+ schools/year unlock a co-marketing budget and a dedicated CSM.",
    },
    {
      q: "Is geographic exclusivity available?",
      a: "Yes — for Co-sell and White-label tiers. Per-country exclusivity for first-mover partners. 12-month minimum term.",
    },
    {
      q: "What sales materials do I get?",
      a: "Decks, demo videos, ROI calculators, GDPR/DPIA templates, case studies, and a partner portal. Materials refreshed quarterly.",
    },
  ],
  curious: [
    {
      q: "How does Knora actually work?",
      a: "Voice-first AI tutor. Student speaks to Knora about a topic — Knora asks Socratic questions, finds the gaps, and reinforces via spaced repetition. No typing, no quizzes, no passive video. Just conversation that adapts in real time.",
    },
    {
      q: "Is there a free trial?",
      a: "Schools and tutoring programs get a free 30-day pilot. Knora is sold B2B — we don't currently offer individual student plans.",
    },
    {
      q: "Which subjects work best?",
      a: "Deepest content in IB Chemistry HL, Mathematics, Biology, and Physics. Growing libraries for History, Literature, Economics. Custom subjects via your syllabus upload — 2-week ingestion.",
    },
    {
      q: "Where can I see it in action?",
      a: "Scroll up to the &ldquo;See it in action&rdquo; section for a recorded session, or book a 15-minute live demo with our team.",
    },
  ],
};

const LC_CTAS = [
  { label: "Book a 15-min demo", href: "mailto:hello@knora.com?subject=Book%20a%20demo", primary: true },
  { label: "Email partnerships",  href: "mailto:partnerships@knora.com",                  primary: false },
];

function LiveCompanion() {
  const [persona, setPersona] = useState(null);
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState("");
  const [thinking, setThinking] = useState(false);
  const [visible, setVisible] = useState(false);
  // Arranca colapsado (pastilla mínima: orbe + "Knora · ready") para no tapar
  // las tarjetas de las secciones. Se expande al tocar el orbe o el chevron.
  const [collapsed, setCollapsed] = useState(true);
  const feedRef = useRef(null);

  // Show after hero, hide near final CTA
  useEffect(() => {
    const onScroll = () => {
      const y = window.scrollY;
      const max = document.documentElement.scrollHeight - window.innerHeight;
      const past = y > 720;
      const nearEnd = max - y < 700;
      setVisible(past && !nearEnd);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  // Auto-scroll feed
  useEffect(() => {
    if (feedRef.current) feedRef.current.scrollTop = feedRef.current.scrollHeight;
  }, [messages, thinking]);

  function pickPersona(id) {
    setPersona(id);
    setMessages([]);
  }

  function pickFaq(faq) {
    setMessages((m) => [...m, { who: "student", text: faq.q }]);
    setThinking(true);
    setTimeout(() => {
      setMessages((m) => [...m, { who: "knora", text: faq.a }]);
      setThinking(false);
    }, 700 + Math.random() * 400);
  }

  function reset() {
    setPersona(null);
    setMessages([]);
    setInput("");
  }

  async function handleSubmit(e) {
    e && e.preventDefault();
    const text = input.trim();
    if (!text || thinking) return;
    setMessages((m) => [...m, { who: "student", text }]);
    setInput("");
    setThinking(true);

    let reply;
    try {
      const res = await fetch("/api/knora-chat", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ persona, messages, text }),
      });
      const data = await res.json();
      reply = data.reply || (persona
        ? "I hit a hiccup — try emailing partnerships@knora.com and we'll route this to the right specialist."
        : "I hit a hiccup — try emailing hello@knora.com and we'll get back to you.");
    } catch {
      reply = persona
        ? "Connection issue on my side. Email partnerships@knora.com and the team will pick this up."
        : "Connection issue on my side. Email hello@knora.com and we'll route you to the right person.";
    }
    setMessages((m) => [...m, { who: "knora", text: reply }]);
    setThinking(false);
  }

  const status = thinking ? "thinking" : (messages.length === 0 && !persona ? "ready" : "listening");
  const currentFaqs = persona ? LC_FAQS[persona] : null;
  const lastIsKnora = messages.length > 0 && messages[messages.length - 1].who === "knora" && !thinking;

  return (
    <div
      className={`live-companion${visible ? " in" : ""}${collapsed ? " collapsed" : ""}`}
      role="complementary"
      aria-label="Knora assistant"
    >
      <button
        className="lc-toggle"
        onClick={() => setCollapsed((c) => !c)}
        aria-label={collapsed ? "Expand" : "Collapse"}
      >
        {collapsed ? "▴" : "▾"}
      </button>

      <div
        className="lc-orb"
        onClick={collapsed ? () => setCollapsed(false) : undefined}
        style={collapsed ? { cursor: "pointer" } : undefined}
        role={collapsed ? "button" : undefined}
        aria-label={collapsed ? "Open Knora assistant" : undefined}
      >
        <OrbVideo />
        <div className="lc-pulse" />
      </div>

      <div className="lc-body lc-chat">
        <div className="lc-meta">
          <span className="lc-who knora">Knora</span>
          {persona && (
            <button className="lc-reset" onClick={reset} aria-label="Start over">
              ← restart
            </button>
          )}
          <span className="lc-status">
            <span className="lc-status-dot" />
            {status}
          </span>
        </div>

        {/* PHASE 1 — Triage: no persona yet, no messages */}
        {!persona && messages.length === 0 && (
          <div className="lc-greeting">
            <div className="lc-greet-text">
              Hi — I&rsquo;m Knora&rsquo;s assistant. What brings you here?
            </div>
            <div className="lc-suggestions">
              {LC_PERSONAS.map((p) => (
                <button
                  key={p.id}
                  type="button"
                  className="lc-suggest"
                  onClick={() => pickPersona(p.id)}
                >
                  <span className="lc-hint">{p.hint}</span> {p.label}
                </button>
              ))}
            </div>
          </div>
        )}

        {/* PHASE 2 — Persona picked, no messages: show intro + FAQ chips */}
        {persona && messages.length === 0 && (
          <div className="lc-greeting">
            <div className="lc-greet-text">{LC_INTROS[persona]}</div>
            <div className="lc-suggestions">
              {currentFaqs.map((f, i) => (
                <button
                  key={i}
                  type="button"
                  className="lc-suggest"
                  onClick={() => pickFaq(f)}
                >
                  {f.q}
                </button>
              ))}
            </div>
          </div>
        )}

        {/* PHASE 3 — Conversation in progress */}
        {messages.length > 0 && (
          <div className="lc-feed" ref={feedRef}>
            {messages.map((m, i) => (
              <div key={i} className={`lc-msg ${m.who} in`}>
                {m.text}
              </div>
            ))}
            {thinking && (
              <div className="lc-msg knora typing">
                <span className="d" /><span className="d" /><span className="d" />
              </div>
            )}

            {/* After Knora replies, show next-step chips */}
            {lastIsKnora && (
              <div className="lc-after">
                <div className="lc-suggestions">
                  {persona && currentFaqs.slice(0, 2).map((f, i) => (
                    <button
                      key={i}
                      type="button"
                      className="lc-suggest"
                      onClick={() => pickFaq(f)}
                    >
                      {f.q}
                    </button>
                  ))}
                </div>
                <div className="lc-cta-row">
                  {LC_CTAS.map((c, i) => (
                    <a
                      key={i}
                      href={c.href}
                      className={`lc-cta ${c.primary ? "primary" : "ghost"}`}
                    >
                      {c.label}
                    </a>
                  ))}
                </div>
              </div>
            )}
          </div>
        )}

        <form className="lc-input-row" onSubmit={handleSubmit}>
          <input
            type="text"
            className="lc-input"
            placeholder={persona ? "Or ask anything else…" : "Type your question…"}
            value={input}
            onChange={(e) => setInput(e.target.value)}
            disabled={thinking}
            aria-label="Ask Knora"
          />
          <button
            type="submit"
            className="lc-send"
            disabled={!input.trim() || thinking}
            aria-label="Send"
          >
            <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
              <path d="M2 7h10M8 3l4 4-4 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
            </svg>
          </button>
        </form>
      </div>
    </div>
  );
}

Object.assign(window, { LiveCompanion });

/* =============================================================
   Landing page composition
   ============================================================= */
function LandingPage() {
  // Background is now a single fixed gradient image; no per-scroll palette needed.
  const {
    S1Social, S2Why, S3How, S4Demo, S5Science, S6Stats, S7Curricula, S8CTA, Footer,
  } = window;

  return (
    <>
      <div className="lp-bg" />
      <div className="lp-aurora" aria-hidden>
        <i /><i /><i />
      </div>

      <div className="lp">
        <HeroV2 />
        <S1Social />
        <S2Why />
        <S3How />
        <S4Demo />
        <S5Science />
        <S6Stats />
        <S7Curricula />
        <S8CTA />
        <Footer />
      </div>

      {/* Persistent live tutor companion — unique conversational strip */}
      <LiveCompanion />
    </>
  );
}

Object.assign(window, {
  HeroV1, HeroV2, HeroV3, OrbVideo, Wordmark, Nav, Chips, TrustStrip, WaveBars,
  ArrowRight, PlayDot, LandingPage,
});
