// Canaisay Landing v2 — hero module.
// Real talking-head video of the founder. As he names a reference
// (Zoom / EBITDA / Kant), Canaisay context cards surface in sync — driven by
// the video's own timeline.
(function () {
  const { useState, useEffect, useRef } = React;
  const { Logo, Brand, AppleMark, demoHref, openWaitlist, trackSalesClick } = window;

  const VIDEO_SRC = "assets/hero-call.mp4";

  // —————————————————————————————————————————————
  // Context cards, pinned to spoken moments (seconds).
  // Timings derived from the audio waveform of the hero video.
  // Each card becomes visible once currentTime passes `at`, and stays
  // until the video loops (stateless → loop-safe).
  // —————————————————————————————————————————————
  const CTX_CARDS = [
    {
      at: 2.2, id: "canaisay", eyebrow: "Product",
      title: "CAN-AI-SAY",
      body: "Live context, not post-call notes: one small card when a reference would otherwise knock you out of the conversation.",
      full: "CAN-AI-SAY is a Mac-first ambient context layer for calls and video. It notices jargon, people, concepts, and references as they happen, then shows the smallest useful explanation so you can keep listening instead of searching.",
    },
    {
      at: 10.2, id: "zoom", eyebrow: "Surface",
      title: "Zoom",
      body: "A live video-call room. CAN-AI-SAY listens locally while the conversation is happening; no bot has to join.",
      full: "Zoom is shorthand for the modern video meeting: live, fast, full of names, acronyms, and side references. CAN-AI-SAY is built for that exact surface — it listens on your Mac and brings up context without waiting for a post-call summary.",
    },
    {
      at: 14.4, id: "ebitda", eyebrow: "Metric",
      title: "EBITDA",
      body: "Earnings before interest, taxes, depreciation, and amortization — a rough view of operating profitability.",
      full: "EBITDA is often used to compare companies because it strips out taxes, interest costs, and some non-cash accounting charges. Useful as a quick operating lens, but not the same as free cash flow.",
    },
    {
      at: 16.5, id: "to-the-moon", eyebrow: "Slang",
      title: "“To the moon”",
      body: "Market slang for “this is going way up” — optimistic, hype-heavy, and usually not a sober forecast.",
      full: "To the moon means someone expects a stock, crypto asset, metric, or business result to rise dramatically. In a call, it is a confidence signal more than a precise financial claim.",
    },
    {
      at: 24.8, id: "apperception", eyebrow: "Concept",
      title: "Transcendental apperception",
      body: "Kant’s idea that experience feels like “my experience” because the mind unifies perceptions under a single self.",
      full: "In Kant, transcendental apperception is the mind’s basic “I think” that can accompany all experiences. It is not a memory or personality trait; it is the structure that lets scattered perceptions become one coherent experience.",
    },
    {
      at: 28.0, id: "kant", eyebrow: "Person",
      title: "Immanuel Kant",
      body: "18th-century philosopher who asked how the mind makes experience possible.",
      full: "Immanuel Kant (1724–1804) argued that we do not passively receive the world exactly as it is. The mind structures experience through forms like space, time, and categories such as causality. His work sits behind a lot of modern epistemology.",
    },
  ];

  // How long each card stays on screen after it surfaces, then fades out
  // (unless the user has interacted with it — then it stays pinned).
  const CARD_HOLD = 4.6;

  // Show each caption line this many seconds early, so it doesn't trail the
  // speech. Bump up if subtitles still feel late.
  const CAPTION_LEAD = 0.8;

  // —————————————————————————————————————————————
  // Captions, synced to the speech (seconds). Timings come from the audio
  // waveform of the hero video. Shown while muted, so silent viewers follow.
  // —————————————————————————————————————————————
  const CAPTIONS = [
    { s: 0.6,   e: 5.2,   t: "CAN-AI-SAY is indispensable during calls and while watching video podcasts." },
    { s: 5.3,   e: 9.0,   t: "It helps you stay in the conversation right now — not after the call is over." },
    { s: 9.2,   e: 13.2,  t: "Every time someone on Zoom starts throwing around fancy business jargon like:" },
    { s: 13.3,  e: 16.5,  t: "“Our EBITDA is going to the moon...”" },
    { s: 16.7,  e: 20.8,  t: "CAN-AI-SAY is already there, quietly explaining what they actually mean." },
    { s: 21.0,  e: 23.4,  t: "Or maybe you're listening to a philosophy podcast..." },
    { s: 23.5,  e: 26.6,  t: "And suddenly realize you've forgotten what transcendental apperception is..." },
    { s: 26.7,  e: 28.8,  t: "Or who exactly Kant was." },
    { s: 29.0,  e: 33.4,  t: "No problem. CAN-AI-SAY fills in the gaps instantly." },
  ];

  function captionAt(t) {
    const tt = t + CAPTION_LEAD; // show each line a touch before it's spoken
    for (const c of CAPTIONS) if (tt >= c.s && tt < c.e) return c.t;
    return "";
  }

  // —————————————————————————————————————————————
  // Stream the video directly so playback can start before the whole file
  // downloads. Some preview sandboxes dislike relative video srcs, so keep
  // blob loading only as an error fallback instead of the primary path.
  // —————————————————————————————————————————————
  function useStreamingVideo(src) {
    const ref = useRef(null);
    useEffect(() => {
      const v = ref.current;
      if (!v) return;
      let blobUrl = null;
      let fallbackStarted = false;
      const tryPlay = () => v.play().catch(() => {});
      const onCanPlay = () => tryPlay();
      const onError = () => {
        if (fallbackStarted) return;
        fallbackStarted = true;
        fetch(src)
          .then((r) => {
            if (!r.ok) throw new Error("Video fetch failed");
            return r.blob();
          })
          .then((b) => {
            blobUrl = URL.createObjectURL(b);
            v.src = blobUrl;
            v.load();
            tryPlay();
          })
          .catch(() => {});
      };
      v.addEventListener("canplay", onCanPlay);
      v.addEventListener("error", onError);
      tryPlay();
      return () => {
        v.removeEventListener("canplay", onCanPlay);
        v.removeEventListener("error", onError);
        if (blobUrl) URL.revokeObjectURL(blobUrl);
      };
    }, [src]);
    return ref;
  }

  // —————————————————————————————————————————————
  // Listening equalizer (idle state, before the first reference)
  // —————————————————————————————————————————————
  function Equalizer() {
    return (
      <span className="eq" aria-hidden="true">
        <i /><i /><i /><i /><i />
      </span>
    );
  }

  // Each reference pins to a corner of the video (centre stays clear of the speaker)
  const CORNERS = ["corner-tl", "corner-tr", "corner-bl", "corner-tl", "corner-tr", "corner-bl"];

  // —————————————————————————————————————————————
  // A single context card — close (×) + expand to full definition.
  // Interacting (expand/close) pins it so it won't auto-dismiss.
  // —————————————————————————————————————————————
  function ContextCard({ card, corner, shown, expanded, onExpand, onClose }) {
    return (
      <div className={`cs-card cs-card--corner ${corner} ${shown ? "is-in" : ""} ${expanded ? "is-expanded" : ""}`}>
        <div className="cs-card-head">
          <Logo size={18} />
          <span className="cs-card-brand">CANAISAY</span>
          <span className="cs-card-eyebrow">{card.eyebrow}</span>
          <button className="cs-card-x" onClick={onClose} aria-label="Dismiss" type="button">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
              <path d="M6 6l12 12M18 6 6 18" />
            </svg>
          </button>
        </div>
        <div className="cs-card-title">{card.title}</div>
        <div className="cs-card-body">{expanded ? card.full : card.body}</div>
        <button className="cs-card-expand" onClick={onExpand} type="button">
          {expanded ? "Show less" : "Full definition"}
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
            <path d={expanded ? "M18 15l-6-6-6 6" : "M6 9l6 6 6-6"} />
          </svg>
        </button>
      </div>
    );
  }

  // —————————————————————————————————————————————
  // The call window (de-branded — no Zoom). Holds the founder video,
  // a Canaisay "listening" pill, a sound toggle, and the corner cards
  // that surface as references are spoken.
  // —————————————————————————————————————————————
  function CallWindow({
    muted, onToggleMute, videoRef, videoSrc, videoReady, videoError,
    onVideoReady, onVideoError, caption, cards, onExpand, onClose,
  }) {
    return (
      <div className="call-win">
        <div className="call-bar">
          <span className="call-traffic"><i /><i /><i /></span>
          <span className="call-pill">
            <Logo size={15} />
            <span className="call-pill-k"><Brand /></span>
            <span className="call-listen"><Equalizer /> Listening</span>
          </span>
          <span className="call-rec"><i /> REC</span>
        </div>

        <div className="call-stage">
          <video
            ref={videoRef}
            className="call-video"
            src={videoSrc}
            crossOrigin="anonymous"
            muted={muted}
            loop
            autoPlay
            playsInline
            preload="auto"
            onLoadedData={onVideoReady}
            onCanPlay={onVideoReady}
            onPlaying={onVideoReady}
            onError={onVideoError}
          />

          {!videoReady && (
            <div className={`call-video-state ${videoError ? "is-slow" : ""}`} aria-live="polite">
              <Logo size={22} />
              <span>{videoError ? "Video is taking longer than usual" : "Loading video"}</span>
            </div>
          )}

          {/* Context cards — one per corner, surfaced in sync with the speech */}
          {CTX_CARDS.map((c, i) => (
            <ContextCard
              key={c.id}
              card={c}
              corner={CORNERS[i]}
              shown={cards[i].shown}
              expanded={cards[i].expanded}
              onExpand={() => onExpand(i)}
              onClose={() => onClose(i)}
            />
          ))}

          <button
            className={`call-mute ${muted ? "is-muted" : ""}`}
            onClick={onToggleMute}
            aria-label={muted ? "Unmute video" : "Mute video"}
            type="button"
          >
            {muted ? (
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round">
                <path d="M11 5 6 9H2v6h4l5 4z" /><path d="m23 9-6 6M17 9l6 6" />
              </svg>
            ) : (
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round">
                <path d="M11 5 6 9H2v6h4l5 4z" /><path d="M15.5 8.5a5 5 0 0 1 0 7M18.5 5.5a9 9 0 0 1 0 13" />
              </svg>
            )}
            <span className="call-mute-tip">{muted ? "Sound off" : "Sound on"}</span>
          </button>
        </div>

        {muted && (
          <div className="call-cc" aria-live="off">
            <span>{caption}</span>
          </div>
        )}
      </div>
    );
  }

  // —————————————————————————————————————————————
  // Hero copy
  // —————————————————————————————————————————————
  function HeroCopy({ centered }) {
    return (
      <div className={`hero-copy ${centered ? "is-centered" : ""}`}>
        <div className="eyebrow">Ambient research for live calls</div>
        <h1 className="h-headline">
          Stay in the conversation <br />
          while references fly past.
        </h1>
        <p className="h-sub">
          <Brand /> explains the terms, people, and concepts the moment they come up —
          on Zoom, in podcasts, or anywhere audio is playing.
          <br />No full copiloting, no bot in your meeting. Audio stays on device.
        </p>
        <div className="h-ctas">
          <a href="#cta" className="btn-primary" onClick={(e) => openWaitlist(e, "hero")}>
            <AppleMark /> Download for Mac
          </a>
          <a href="mailto:first@canaisay.com?subject=Talk%20to%20Sales%20%E2%80%94%20Canaisay" className="btn-ghost" onClick={() => trackSalesClick("hero")}>
            <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg>
            Talk to Sales
          </a>
        </div>
        <div className="h-trust">
          <span>Mac-first private beta</span>
          <span className="sep">·</span>
          <span>No bot joins the call</span>
          <span className="sep">·</span>
          <span>Audio stays local</span>
        </div>
      </div>
    );
  }

  // —————————————————————————————————————————————
  // Hero
  // —————————————————————————————————————————————
  function Hero() {
    const [muted, setMuted] = useState(true);
    // Per-card UI state. mode: 'auto' follows the video clock; 'pinned' stays
    // open after the user interacts; 'dismissed' is hidden until the loop.
    const [cards, setCards] = useState(() =>
      CTX_CARDS.map(() => ({ inWindow: false, mode: "auto", expanded: false }))
    );
    const [caption, setCaption] = useState("");
    const [videoReady, setVideoReady] = useState(false);
    const [videoError, setVideoError] = useState(false);
    const videoRef = useStreamingVideo(VIDEO_SRC);
    const lastT = useRef(0);

    // Keep the video element's muted prop honest (autoplay needs muted=true)
    useEffect(() => {
      if (videoRef.current) videoRef.current.muted = muted;
    }, [muted]);

    // Drive card visibility from the video's own clock
    useEffect(() => {
      const v = videoRef.current;
      if (!v) return;
      const onTime = () => {
        const t = v.currentTime;
        const looped = t < lastT.current - 0.5; // video wrapped back to the start
        lastT.current = t;
        setCards((prev) => {
          let changed = looped;
          const next = prev.map((cd, i) => {
            const inWindow = t >= CTX_CARDS[i].at && t < CTX_CARDS[i].at + CARD_HOLD;
            // On loop, reset any user interaction so the demo replays fresh.
            const mode = looped ? "auto" : cd.mode;
            const expanded = looped ? false : cd.expanded;
            if (inWindow !== cd.inWindow || mode !== cd.mode || expanded !== cd.expanded) changed = true;
            return { inWindow, mode, expanded };
          });
          return changed ? next : prev;
        });
        setCaption((prev) => { const c = captionAt(t); return c === prev ? prev : c; });
      };
      v.addEventListener("timeupdate", onTime);
      return () => v.removeEventListener("timeupdate", onTime);
    }, []);

    // User pins the card open / expands the full definition
    const expandCard = (i) =>
      setCards((prev) => prev.map((cd, j) =>
        j === i ? { ...cd, mode: "pinned", expanded: !cd.expanded } : cd));
    // User dismisses the card for the rest of this loop
    const closeCard = (i) =>
      setCards((prev) => prev.map((cd, j) =>
        j === i ? { ...cd, mode: "dismissed", expanded: false } : cd));

    // Resolve what actually shows: dismissed → hidden, pinned → shown, else clock
    const cardView = cards.map((cd) => ({
      shown: cd.mode === "dismissed" ? false : cd.mode === "pinned" ? true : cd.inWindow,
      expanded: cd.expanded,
    }));

    const toggleMute = () => {
      const v = videoRef.current;
      setMuted((m) => {
        const nm = !m;
        if (v) { v.muted = nm; if (!nm) v.play().catch(() => {}); }
        return nm;
      });
    };

    const shared = {
      muted, onToggleMute: toggleMute, videoRef, videoSrc: VIDEO_SRC,
      videoReady,
      videoError,
      onVideoReady: () => { setVideoReady(true); setVideoError(false); },
      onVideoError: () => setVideoError(true),
      caption,
      cards: cardView, onExpand: expandCard, onClose: closeCard,
    };

    return (
      <section className="hero hv-a" id="top">
        <HeroCopy />
        <div className="call-zone">
          <div className="zoom-orb a" aria-hidden="true" />
          <div className="zoom-orb b" aria-hidden="true" />
          <CallWindow {...shared} />
        </div>
      </section>
    );
  }

  Object.assign(window, { Hero });
})();
