// landing-story.jsx — ScrollStorySection (scrubbed pin animation)
// Loaded after landing-mockups.jsx and landing-sections.jsx.
// React hooks (useEffect/useRef/useState) are already destructured upstream;
// Icon component comes from landing-mockups.jsx.

// i18n bridge — uses window.Wisbid.i18n if present, otherwise no-op.
const __i18nStory = (typeof window !== "undefined" && window.Wisbid && window.Wisbid.i18n) || null;
const __tStory = (key, fallback) => __i18nStory ? (__i18nStory.t(key) === key ? fallback : __i18nStory.t(key)) : fallback;
const useStoryT = () => {
  const i18n = (typeof window !== "undefined" && window.Wisbid && window.Wisbid.i18n) || null;
  const [, force] = useState(0);
  useEffect(() => {
    if (!i18n) return;
    return i18n.subscribe(() => force((v) => v + 1));
  }, [i18n]);
  return (key, fallback) => {
    if (!i18n) return fallback;
    const v = i18n.t(key);
    return v === key ? fallback : v;
  };
};

/* ---------- progress + interpolation helpers ---------- */
function useSectionProgress(ref) {
  const [p, setP] = useState(0);
  useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    let raf = 0;
    let ticking = false;
    const update = () => {
      const rect = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const total = rect.height - vh;
      const scrolled = -rect.top;
      const progress = total > 0 ? Math.max(0, Math.min(1, scrolled / total)) : 0;
      setP(progress);
      ticking = false;
    };
    const onScroll = () => {
      if (!ticking) {
        ticking = true;
        raf = requestAnimationFrame(update);
      }
    };
    update();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      cancelAnimationFrame(raf);
    };
  }, [ref]);
  return p;
}

const _clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
const _lerp = (a, b, t) => a + (b - a) * t;
const _easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
const _easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
const _map = (p, p0, p1, v0, v1, ease) => {
  if (p1 === p0) return p < p0 ? v0 : v1;
  const t = _clamp((p - p0) / (p1 - p0), 0, 1);
  return _lerp(v0, v1, ease ? ease(t) : t);
};

/* ---------- responsive / reduced-motion detection ---------- */
function useStaticFallback() {
  const [isStatic, setIsStatic] = useState(false);
  useEffect(() => {
    const mqMobile = window.matchMedia("(max-width: 900px)");
    const mqReduced = window.matchMedia("(prefers-reduced-motion: reduce)");
    const update = () => setIsStatic(mqMobile.matches || mqReduced.matches);
    update();
    mqMobile.addEventListener("change", update);
    mqReduced.addEventListener("change", update);
    return () => {
      mqMobile.removeEventListener("change", update);
      mqReduced.removeEventListener("change", update);
    };
  }, []);
  return isStatic;
}

/* ---------- the four cards (reused for both modes) ---------- */
const LOOP_CARDS = [
{
  kind: "connected",
  num: "01",
  labelKey: "loop.card1.label",
  titleKey: "loop.card1.title",
  descKey: "loop.card1.desc"
},
{
  kind: "skills",
  num: "02",
  labelKey: "loop.card3.label",
  titleKey: "loop.card3.title",
  descKey: "loop.card3.desc"
},
{
  kind: "mobile",
  num: "03",
  labelKey: "loop.card4.label",
  titleKey: "loop.card4.title",
  descKey: "loop.card4.desc"
}];


/* ---------- visuals for each card ---------- */
const LoopVisual = ({ kind }) => {
  const t = useStoryT();
  if (kind === "connected") {
    const tools = [
    { k: "gm", c: "#EA4335", m: "M" }, // Gmail-style
    { k: "wa", c: "#25D366", m: "\u2713" }, // WhatsApp-style
    { k: "ms", c: "#4A4DA8", m: "T" }, // Teams
    { k: "gw", c: "#4285F4", m: "G" }, // Google
    { k: "ol", c: "#0078D4", m: "O" }, // Outlook
    { k: "xl", c: "#217346", m: "X" }, // Excel
    { k: "pd", c: "#B30B00", m: "P" }, // PDF
    { k: "jr", c: "#2684FF", m: "J" } // Jira
    ];
    return (
      <div className="lv lv--connected">
        <div className="lv-orbit">
          <div className="lv-orbit-ring lv-orbit-ring--a" />
          <div className="lv-orbit-ring lv-orbit-ring--b" />
          <div className="lv-orbit-core"><img src="assets/wisbid-icon.png" alt="" /></div>
          {tools.map((t, i) =>
          <span
            key={t.k}
            className="lv-orbit-chip"
            style={{
              background: t.c,
              animationDelay: `${-(i * 1.5)}s`,
              '--i': i,
              '--n': tools.length
            }}>
            {t.m}</span>
          )}
        </div>
        <div className="lv-email">
          <div className="lv-email-row"><span className="k">{t("loop.visual.subject", "Subject")}</span><span className="v">{t("loop.visual.waterLeak", "Water leak at Unit 12-04")}</span></div>
          <div className="lv-email-divider">
            <span className="chip">{t("loop.visual.extracted", "WisAI extracted")}</span>
          </div>
          <div className="lv-email-row"><span className="k">{t("loop.visual.location", "Location")}</span><span className="v">{t("loop.visual.blockA", "Block A · Tower One")}</span></div>
          <div className="lv-email-row"><span className="k">{t("loop.visual.category", "Category")}</span><span className="v">{t("loop.visual.plumbing", "Plumbing")}</span></div>
          <div className="lv-email-row"><span className="k">{t("loop.visual.priority", "Priority")}</span><span className="v v--warn">{t("loop.visual.medium", "Medium")}</span></div>
          <div className="lv-email-action">{t("loop.visual.createRequest", "Create request")} \u2192</div>
        </div>
      </div>);

  }

  if (kind === "governance") {
    const rows = [
    [t("loop.gov.draft", "Draft request from email"), "approval"],
    [t("loop.gov.summarise", "Summarise work order"), "allowed"],
    [t("loop.gov.assign", "Assign technician"), "approval"],
    [t("loop.gov.update", "Send resident update"), "approval"],
    [t("loop.gov.vendor", "Auto-assign vendor"), "disabled"]];

    const tiers = [
    ["disabled", t("loop.gov.disabled", "Disabled")],
    ["approval", t("loop.gov.requireApproval", "Require approval")],
    ["allowed", t("loop.gov.allowed", "Allowed")]];

    return (
      <div className="lv lv--governance">
        <div className="lv-gov-head">{t("loop.gov.head", "Action policies")}</div>
        {rows.map(([label, state]) =>
        <div key={label} className="lv-gov-row">
            <span className="lv-gov-label">{label}</span>
            <div className="lv-gov-toggle">
              {tiers.map(([k, lbl]) =>
            <span key={k} className={`lv-gov-tier ${state === k ? "is-on" : ""} lv-gov-tier--${k}`}>{lbl}</span>
            )}
            </div>
          </div>
        )}
      </div>);

  }

  if (kind === "skills") {
    const skills = [
    t("loop.skills.requestTriage", "Request triage"),
    t("loop.skills.workOrderSummary", "Work order summary"),
    t("loop.skills.vendorMatching", "Vendor matching"),
    t("loop.skills.evidenceCheck", "Evidence check"),
    t("loop.skills.auditPack", "Audit pack builder"),
    t("loop.skills.residentUpdate", "Resident update"),
    t("loop.skills.slaRisk", "SLA risk detection"),
    t("loop.skills.missingEvidence", "Missing evidence finder")];

    return (
      <div className="lv lv--skills">
        <div className="lv-skills-head">
          <span className="lbl">{t("loop.skills.library", "Skill library")}</span>
          <span className="count">{t("loop.skills.count", "8 of 24 installed")}</span>
        </div>
        <div className="lv-skills-grid">
          {skills.map((s, i) =>
          <span key={s} className={`lv-skill-chip ${i < 6 ? "is-on" : ""}`}>
              <span className="dot" />{s}
            </span>
          )}
        </div>
      </div>);

  }

  if (kind === "mobile") {
    return (
      <div className="lv lv--mobile">
        <div className="lv-phone lv-phone--back">
          <div className="lv-phone-time">9:41</div>
          <div className="lv-phone-eyebrow">{t("loop.mobile.today", "TODAY")}</div>
          <div className="lv-phone-stat">5</div>
          <div className="lv-phone-stat-label">{t("loop.mobile.assignedJobs", "assigned jobs")}</div>
          <div className="lv-phone-urgent">
            <span className="dot" />{t("loop.mobile.urgent", "2 urgent")}
          </div>
          <div className="lv-phone-rows">
            <div className="lv-phone-row"><span /><span /></div>
            <div className="lv-phone-row"><span /><span /></div>
            <div className="lv-phone-row"><span /><span /></div>
          </div>
        </div>
        <div className="lv-phone lv-phone--front">
          <div className="lv-phone-eyebrow">{t("loop.mobile.workOrder", "WORK ORDER")}</div>
          <div className="lv-phone-title">{t("loop.mobile.waterSeepage", "Water seepage")}</div>
          <div className="lv-phone-sub">{t("loop.mobile.unit", "Unit 12-04")}</div>
          <div className="lv-phone-card">
            <div className="lv-phone-card-row">
              <span className="k">{t("loop.mobile.checklist", "Checklist")}</span>
              <span className="chip chip--need">{t("loop.mobile.required", "Required")}</span>
            </div>
            <div className="lv-phone-card-row">
              <span className="k">{t("loop.mobile.evidence", "Evidence")}</span>
              <span className="v">{t("loop.mobile.photos", "6 photos")}</span>
            </div>
          </div>
          <div className="lv-phone-cta">{t("loop.mobile.ready", "Ready for closure")}</div>
        </div>
      </div>);

  }

  return null;
};

const LoopCard = ({ c, i }) => {
  const t = useStoryT();
  const label = t(c.labelKey, "");
  return (
<article className="lp-story-tile">
    <div className="lp-story-tile-body">
      <div className="lp-story-tile-head">
        <span className="num">{c.num}</span>
        <span className="lbl">{label}</span>
      </div>
      <h3>{t(c.titleKey, "")}</h3>
      <p>{t(c.descKey, "")}</p>
    </div>
    <div className="lp-story-tile-vis">
      <LoopVisual kind={c.kind} />
    </div>
    <span className="lp-story-tile-pill" aria-hidden="true">
      <span className="num">{c.num}</span>
      <span className="lbl">{label}</span>
    </span>
  </article>);
};


/* ---------- title block ---------- */
const StoryTitle = ({ style }) => {
  const t = useStoryT();
  return (
<h2 className="lp-story-title" style={style}>
    <span className="line">{t("loop.titleLine1", "The Wisbid")}</span>
    <span className="line">
      <span className="lp-story-glyph" aria-hidden="true">
        <span className="dot" />
      </span>
      {t("loop.titleLine2", "Loop")}
    </span>
  </h2>);
};


/* ---------- floating card ---------- */
const StoryCard = ({ style }) => {
  const t = useStoryT();
  return (
    <aside className="lp-story-card" style={style}>
      <div className="lp-story-card-eyebrow">
        <span className="tick" />
        {t("loop.kicker", "The Wisbid loop")}
      </div>
      <p className="lp-story-card-lead">
        {t("loop.lead", "One platform from request to closure — with WisAI doing the work in between.")}
      </p>
      <p className="lp-story-card-sub">
        {t("loop.sub", "Four intelligence layers, one operational loop. Wisbid replaces the spreadsheets, group chats and PDF email chains FM teams use today.")}
      </p>
      <div className="lp-story-card-meta">
        <div className="dots">
          <span /><span /><span /><span />
        </div>
        <div className="legend">{t("loop.legend", "Intake · Dispatch · Evidence · Audit")}</div>
      </div>
    </aside>
  );
};


/* ---------- main content (revealed at the end) ---------- */
const StoryMain = ({ style }) => {
  const t = useStoryT();
  return (
<div className="lp-story-main" style={style}>
    <div className="lp-story-main-inner">
      <header className="lp-story-main-head">
        <span className="eyebrow">
          <span className="num">01</span>{t("loop.mainEyebrow", "The Wisbid Operating Loop").replace(/^01/, "")}
        </span>
        <div className="lp-story-main-headcol">
          <h3>{t("loop.mainHeadline", "One platform for maintenance, AI agents and field work.")}</h3>
          <p>{t("loop.mainDesc", "Wisbid connects the tools your team already uses, turns messy intake into structured work, governs what AI can do, and brings maintenance execution to the field.")}</p>
        </div>
      </header>
      <div className="lp-story-grid lp-story-grid--3">
        {LOOP_CARDS.map((c, i) => <LoopCard key={c.kind} c={c} i={i} />)}
      </div>
    </div>
  </div>);
};


/* ============================================================
   ScrollStorySection
   ============================================================ */
const WisbidLoop = () => {
  const sectionRef = useRef(null);
  const t = useStoryT();
  const p = useSectionProgress(sectionRef);
  const isStatic = useStaticFallback();

  // ----- compute scrubbed styles -----
  // Title and card drift downward as they fade so the pinned story follows scroll.
  const titleScale = _map(p, 0.22, 0.62, 1, 0.96, _easeInOutCubic);
  const titleOpacity = _map(p, 0.22, 0.66, 1, 0, _easeInOutCubic);
  const titleColorT = _map(p, 0.22, 0.66, 0, 1, _easeInOutCubic);
  const titleY = _map(p, 0.22, 0.66, -10, 54, _easeInOutCubic);

  // Card: rises from below, lingers, then exits downward + fades.
  const cardEnter = _map(p, 0.05, 0.30, 1, 0, _easeOutCubic); // 1 = below, 0 = centered
  const cardExit = _map(p, 0.58, 0.82, 0, 1, _easeInOutCubic); // 0 = centered, 1 = floated down + faded
  const cardY = cardEnter * 64 + cardExit * 38; // vh units
  const cardOpacity =
  _map(p, 0.05, 0.22, 0, 1, _easeOutCubic) * (1 - _map(p, 0.60, 0.84, 0, 1, _easeInOutCubic));
  const cardScale = 1 - cardExit * 0.025;

  // Main: slides up while fading in, taking over after the card has softened out.
  const mainY = _map(p, 0.56, 0.94, 94, 0, _easeInOutCubic); // vh units
  const mainOpacity = _map(p, 0.52, 0.76, 0, 1, _easeInOutCubic);
  const mainShadow = _map(p, 0.56, 0.94, 0, 1, _easeOutCubic);

  // ----- styles -----
  const navy900 = [11, 31, 59];
  const ink250 = [196, 202, 210];
  const blend = (t) =>
  navy900.map((v, i) => Math.round(_lerp(v, ink250[i], t))).join(", ");
  const titleStyle = {
    transform: `translate(-50%, calc(-50% + ${titleY}px)) scale(${titleScale})`,
    opacity: titleOpacity,
    color: `rgb(${blend(titleColorT)})`
  };

  const cardStyle = {
    transform: `translate(-50%, calc(-50% + ${cardY}vh)) scale(${cardScale})`,
    opacity: cardOpacity
  };

  const mainStyle = {
    transform: `translateY(${mainY}vh)`,
    opacity: mainOpacity,
    boxShadow: `0 -32px 80px -16px rgba(11,31,59,${(0.20 * mainShadow).toFixed(3)})`
  };

  // ----- static / mobile fallback (no pin, no scrub) -----
  if (isStatic) {
    return (
      <section className="lp-story is-static" id="strategy">
        <div className="lp-shell">
          <h2 className="lp-story-title is-static">
            <span className="line">{t("loop.titleLine1", "The Wisbid")}</span>
            <span className="line">
              <span className="lp-story-glyph" aria-hidden="true"><span className="dot" /></span>
              {t("loop.titleLine2", "Loop")}
            </span>
          </h2>
          <aside className="lp-story-card is-static">
            <div className="lp-story-card-eyebrow"><span className="tick" />{t("loop.kicker", "The Wisbid loop")}</div>
            <p className="lp-story-card-lead">
              {t("loop.lead", "One platform from request to closure — with WisAI doing the work in between.")}
            </p>
            <p className="lp-story-card-sub">
              {t("loop.sub", "Four intelligence layers, one operational loop. Wisbid replaces the spreadsheets, group chats and PDF email chains FM teams use today.")}
            </p>
          </aside>
          <div className="lp-story-main is-static">
            <div className="lp-story-main-inner">
              <header className="lp-story-main-head">
                <span className="eyebrow"><span className="num">01</span>{t("loop.mainEyebrow", "The Wisbid Operating Loop").replace(/^01/, "")}</span>
                <div className="lp-story-main-headcol">
                  <h3>{t("loop.mainHeadline", "One platform for maintenance, AI agents and field work.")}</h3>
                  <p>{t("loop.mainDesc", "Wisbid connects the tools your team already uses, turns messy intake into structured work, governs what AI can do, and brings maintenance execution to the field.")}</p>
                </div>
              </header>
              <div className="lp-story-grid lp-story-grid--3">
                {LOOP_CARDS.map((c, i) => <LoopCard key={c.kind} c={c} i={i} />)}
              </div>
            </div>
          </div>
        </div>
      </section>);

  }

  // ----- pinned / scrubbed mode -----
  return (
    <section ref={sectionRef} className="lp-story" id="strategy">
      <div className="lp-story-pin">
        <StoryTitle style={titleStyle} />
        <StoryCard style={cardStyle} />
        <StoryMain style={mainStyle} />
      </div>
    </section>);

};

window.WisbidLoop = WisbidLoop;

/* ============================================================
   Features — second scroll-story section (same scrub mechanic)
   Three feature tiles: WisAI Co-pilot, WisAI Intake Agent,
   Cross-platform Workspace OS.
   ============================================================ */

const FEATURE_CARDS = [
{
  kind: "copilot",
  num: "01",
  labelKey: "feature.card1.label",
  titleKey: "feature.card1.title",
  descKey: "feature.card1.desc"
},
{
  kind: "intake",
  num: "02",
  labelKey: "feature.card2.label",
  titleKey: "feature.card2.title",
  descKey: "feature.card2.desc"
},
{
  kind: "platform",
  num: "03",
  labelKey: "feature.card3.label",
  titleKey: "feature.card3.title",
  descKey: "feature.card3.desc"
}];


/* ---------- visuals for each feature card (Framer Motion, auto-loop) ---------- */
const _M = (typeof window !== "undefined" && window.Motion) || {};
const _motion = _M.motion;
const _AnimatePresence = _M.AnimatePresence;
const _EASE = [0.22, 1, 0.36, 1];

/* Hook: 0 → cycleMs ticker. Updates at boundaries only — sampling every
   ~100ms is enough for staged state transitions and avoids the 60fps
   re-render storm that caused visible flicker. */
function useLoopTick(cycleMs) {
  const [step, setStep] = useState(0);
  useEffect(() => {
    const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduced) return;
    const start = performance.now();
    let cancelled = false;
    const id = setInterval(() => {
      if (cancelled) return;
      setStep((performance.now() - start) % cycleMs);
    }, 120);
    return () => { cancelled = true; clearInterval(id); };
  }, [cycleMs]);
  return step;
}

/* ---- Visual 1 — WisAI Co-pilot prompt → streamed reply → suggested actions ---- */
const CopilotVisual = () => {
  const tr = useStoryT();
  const t = useLoopTick(8400);
  // Cycle:
  //   0 – 1500ms : user types prompt (caret + characters)
  //   1500 – 2400 : WisAI thinking (3-dot)
  //   2400 – 4800 : reply streams in (word by word)
  //   4500 – 6000 : suggested action chips slide in (stagger)
  //   6000 – 7100 : user accepts → chip highlights → confirmation pill
  //   7100 – 8400 : fade out → loop
  const PROMPT = tr("feature.visual.copilotPrompt", "What's blocking REQ-08821?");
  const REPLY  = tr("feature.visual.copilotReply", "Evidence is missing — 2 photos and a moisture reading are required for closure. Vendor is under DLP.");

  const promptChars = Math.max(0, Math.min(PROMPT.length, Math.floor((t - 100) / 55)));
  const promptDone  = promptChars >= PROMPT.length;
  const thinking    = t > 1500 && t < 2400;
  const replyStart  = 2400;
  const replyWords  = REPLY.split(" ");
  const wordsShown  = Math.max(0, Math.min(replyWords.length, Math.floor((t - replyStart) / 110)));
  const showChips   = t > 4500;
  const accepted    = t > 6000;
  const confirmed   = t > 6500;
  const fadeOut     = t > 7400 ? Math.min(1, (t - 7400) / 700) : 0;

  const M  = _motion;
  const AP = _AnimatePresence;

  const SUGGESTIONS = [
    { k: "draft",    label: tr("feature.visual.suggestionDraft", "Draft message to vendor"),  tone: "navy" },
    { k: "dispatch", label: tr("feature.visual.suggestionDispatch", "Dispatch technician"),       tone: "blue" },
    { k: "escalate", label: tr("feature.visual.suggestionEscalate", "Escalate to FM director"),   tone: "amber" },
  ];

  const wrap = (content, key) => M ? (
    <M.div key={key} initial={{ opacity: 0, y: 6 }} animate={{ opacity: 1 - fadeOut, y: 0 }} exit={{ opacity: 0 }} transition={{ duration: 0.32, ease: _EASE }}>
      {content}
    </M.div>
  ) : <div key={key}>{content}</div>;

  return (
    <div className="fv2 fv2--copilot" style={{ opacity: 1 - fadeOut * 0.4 }}>
      {/* Prompt row — feels like a real input */}
      <div className="fv2-cp-prompt" aria-label={tr("feature.visual.userPrompt", "User prompt")}>
        <span className="fv2-cp-caret-wrap">
          <span className="fv2-cp-text">{PROMPT.slice(0, promptChars)}</span>
          {!promptDone && <span className="fv2-cp-caret" />}
        </span>
        <span className={`fv2-cp-send ${promptDone ? "is-on" : ""}`} aria-hidden="true">
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="m4 12 16-8-6 18-3-8z"/></svg>
        </span>
      </div>

      {/* Thinking buffer */}
      {AP && M ? (
        <AP>
          {thinking && (
            <M.div key="think" className="fv2-cp-thinking" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.25 }}>
              <span className="fv2-cp-mark"><img src="assets/wisbid-icon.png" alt="" /></span>
              <span className="fv2-typing"><i /><i /><i /></span>
            </M.div>
          )}
        </AP>
      ) : (thinking && (
        <div className="fv2-cp-thinking">
          <span className="fv2-cp-mark"><img src="assets/wisbid-icon.png" alt="" /></span>
          <span className="fv2-typing"><i /><i /><i /></span>
        </div>
      ))}

      {/* AI reply — words stream in */}
      {wordsShown > 0 && wrap((
        <div className="fv2-cp-reply">
          <span className="fv2-cp-mark"><img src="assets/wisbid-icon.png" alt="" /></span>
          <div className="fv2-cp-reply-body">
            <span className="fv2-cp-reply-tag">WisAI</span>
            <p>
              {replyWords.slice(0, wordsShown).map((w, i) => (
                <span key={i} className="fv2-cp-word">{w}{" "}</span>
              ))}
              {wordsShown < replyWords.length && <span className="fv2-cp-caret fv2-cp-caret--ai" />}
            </p>
          </div>
        </div>
      ), "reply")}

      {/* Suggested actions */}
      {showChips && (
        <div className="fv2-cp-chips" role="group" aria-label={tr("feature.visual.suggestedActions", "Suggested actions")}>
          {SUGGESTIONS.map((s, i) => {
            const isAccepted = accepted && i === 1; // dispatch is the accepted action
            const inner = (
              <span className={`fv2-cp-chip fv2-cp-chip--${s.tone} ${isAccepted ? "is-accepted" : ""}`}>
                <span className="dot" />
                {s.label}
                {isAccepted && (
                  <span className="fv2-cp-chip-tick" aria-hidden="true">
                    <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12l4 4 10-10"/></svg>
                  </span>
                )}
              </span>
            );
            return M ? (
              <M.div key={s.k}
                initial={{ opacity: 0, y: 8, scale: 0.97 }}
                animate={{ opacity: 1 - fadeOut, y: 0, scale: 1 }}
                transition={{ delay: 0.12 * i, duration: 0.36, ease: _EASE }}
              >{inner}</M.div>
            ) : <div key={s.k}>{inner}</div>;
          })}
        </div>
      )}

      {/* Confirmation strip — slides up when the action is accepted */}
      {confirmed && wrap((
        <div className="fv2-cp-confirm">
          <span className="fv2-cp-confirm-tick">
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12l4 4 10-10"/></svg>
          </span>
          <span>{tr("feature.visual.confirmDispatch", "Technician dispatched · ETA 14 min")}</span>
        </div>
      ), "confirm")}
    </div>
  );
};

/* ---- Visual 2 — WisAI Intake form fills ---- */
const IntakeVisual = () => {
  const tText = useStoryT();
  const t = useLoopTick(6500);
  const showMsg = t > 200;
  const arrowOn = t > 1500;
  const filled = [t > 2000, t > 2500, t > 3000, t > 3500];
  const reset = t > 5800 ? Math.min(1, (t - 5800) / 700) : 0;
  const M = _motion;

  return (
    <div className="fv2 fv2--intake">
      <div className={`fv2-msg ${showMsg ? "is-in" : ""}`} style={{ opacity: 1 - reset }}>
        <div className="fv2-msg-head">
          <span className="fv2-ch fv2-ch--wa">W</span>
          <span className="who">+65 9xxx</span>
          <span className="t">02:48</span>
        </div>
        <div className="fv2-msg-body">"{tText("feature.visual.intakeMessage", "AC at unit 12-04 is leaking again, urgent please")}"</div>
      </div>
      <div className={`fv2-arrow ${arrowOn ? "is-in" : ""}`} aria-hidden="true">→</div>
      <div className="fv2-form" style={{ opacity: 1 - reset }}>
        <div className="fv2-form-head">REQ-08821 · {tText("feature.visual.draft", "draft")}</div>
        {[
          [tText("feature.visual.asset", "Asset"), "AHU · 12-04"],
          [tText("feature.visual.category", "Category"), "HVAC"],
          [tText("feature.visual.priority", "Priority"), tText("feature.visual.medium", "Medium"), "warn"],
          [tText("feature.visual.vendor", "Vendor"), "Cool-Air SG"],
        ].map(([k, v, tone], i) => {
          const inFill = filled[i];
          const inner = (
            <>
              <span className="k">{k}</span>
              <span className={`v ${tone === "warn" ? "v--warn" : ""}`}>{inFill ? v : "—"}</span>
            </>
          );
          return M ? (
            <M.div key={k} className="fv2-row" animate={{ background: inFill ? "rgba(22,69,122,0.04)" : "rgba(0,0,0,0)" }} transition={{ duration: 0.35 }}>
              {inner}
            </M.div>
          ) : <div key={k} className="fv2-row">{inner}</div>;
        })}
      </div>
    </div>
  );
};

/* ---- Visual 3 — Agent Task status flow ---- */
const TASK_STAGES = [
  { key: "triage",     pct: 0.10, tone: "navy" },
  { key: "dispatched", pct: 0.40, tone: "blue" },
  { key: "inProgress",pct: 0.72, tone: "amber" },
  { key: "closed",     pct: 1.00, tone: "ok" },
];

const AgentTaskVisual = () => {
  const tText = useStoryT();
  const t = useLoopTick(6800);
  const idx = Math.min(TASK_STAGES.length - 1, Math.floor(t / 1500));
  const stage = TASK_STAGES[idx];
  const stageLabel = (s) => tText(`feature.visual.stage.${s.key}`, s.key);
  const reset = t > 6200 ? Math.min(1, (t - 6200) / 600) : 0;
  const M = _motion;
  const AP = _AnimatePresence;

  return (
    <div className="fv2 fv2--task">
      <div className="fv2-task-head">
        <span className="fv2-task-id">WO-2026-BBBA2C</span>
        {M && AP ? (
          <AP mode="wait">
            <M.span key={stage.key} className={`fv2-task-pill fv2-task-pill--${stage.tone}`}
              initial={{ opacity: 0, y: 4, scale: 0.96 }} animate={{ opacity: 1, y: 0, scale: 1 }} exit={{ opacity: 0, y: -4 }} transition={{ duration: 0.35, ease: _EASE }}>
              {stageLabel(stage)}
            </M.span>
          </AP>
        ) : <span className={`fv2-task-pill fv2-task-pill--${stage.tone}`}>{stageLabel(stage)}</span>}
      </div>
      <div className="fv2-task-title">{tText("feature.visual.waterSeepage", "Water seepage · Unit 12-04")}</div>
      <div className="fv2-task-bar">
        {M ? (
          <M.span animate={{ width: `${stage.pct * 100}%` }} transition={{ duration: 0.7, ease: _EASE }} />
        ) : <span style={{ width: `${stage.pct * 100}%` }} />}
      </div>
      <ul className="fv2-task-steps">
        {TASK_STAGES.map((s, i) => (
          <li key={s.key} className={i <= idx ? "is-done" : ""}>
            <span className="dot" />{stageLabel(s)}
          </li>
        ))}
      </ul>
      <div className="fv2-task-meta" style={{ opacity: 1 - reset }}>
        <span><b>{tText("feature.visual.asset", "Asset")}</b> AHU · 12-04</span>
        <span><b>{tText("feature.visual.vendor", "Vendor")}</b> Cool-Air SG</span>
      </div>
    </div>
  );
};

const FeatureVisual = ({ kind }) => {
  if (kind === "copilot")  return <CopilotVisual />;
  if (kind === "intake")   return <IntakeVisual />;
  if (kind === "platform") return <AgentTaskVisual />;
  return null;
};

const FeatureCard = ({ c }) => {
  const t = useStoryT();
  return (
<article className="lp-feat-card">
    <div className="lp-feat-card-vis">
      <FeatureVisual kind={c.kind} />
    </div>
    <div className="lp-feat-card-body">
      <h3>{t(c.titleKey, "")}</h3>
      <p>{t(c.descKey, "")}</p>
    </div>
  </article>);
};


const FeaturesTitle = ({ style }) => {
  const t = useStoryT();
  return (
<h2 className="lp-story-title" style={style}>
    <span className="line">{t("feat.title", "Features")}</span>
  </h2>);
};


const FeaturesCard = ({ style }) => {
  const t = useStoryT();
  return (
    <aside className="lp-story-card" style={style}>
      <div className="lp-story-card-eyebrow">
        <span className="tick" />
        {t("feat.eyebrow", "Wisbid features")}
      </div>
      <p className="lp-story-card-lead">
        {t("feat.lead", "Three building blocks. One operating layer for facilities work.")}
      </p>
      <p className="lp-story-card-sub">
        {t("feat.sub", "A co-pilot for every operator, an intake agent that listens to every channel, and a workspace that moves with your team across desktop and mobile (Android & iOS).")}
      </p>
      <div className="lp-story-card-meta">
        <div className="dots"><span /><span /><span /></div>
        <div className="legend">{t("feat.legend", "Co-pilot · Intake · Platform")}</div>
      </div>
    </aside>
  );
};


const FeaturesMain = ({ style }) => {
  const t = useStoryT();
  return (
<div className="lp-story-main" style={style}>
    <div className="lp-story-main-inner">
      <header className="lp-story-main-head">
        <span className="eyebrow">
          <span className="num">03</span>{t("feat.mainEyebrow", "Features").replace(/^03/, "")}
        </span>
        <div className="lp-story-main-headcol">
          <h3>{t("feat.mainHeadline", "Three features, one connected operating layer.")}</h3>
          <p>{t("feat.mainDesc", "Each feature stands on its own — but they sit in the same workspace, share the same context, and travel with the same governance.")}</p>
        </div>
      </header>
      <div className="lp-story-grid lp-story-grid--3">
        {FEATURE_CARDS.map((c) => <FeatureCard key={c.kind} c={c} />)}
      </div>
    </div>
  </div>);
};


const Features = () => {
  const sectionRef = useRef(null);
  const t = useStoryT();
  const p = useSectionProgress(sectionRef);
  const isStatic = useStaticFallback();

  const titleScale = _map(p, 0.22, 0.62, 1, 0.96, _easeInOutCubic);
  const titleOpacity = _map(p, 0.22, 0.66, 1, 0, _easeInOutCubic);
  const titleColorT = _map(p, 0.22, 0.66, 0, 1, _easeInOutCubic);
  const titleY = _map(p, 0.22, 0.66, -10, 54, _easeInOutCubic);

  const cardEnter = _map(p, 0.05, 0.30, 1, 0, _easeOutCubic);
  const cardExit = _map(p, 0.58, 0.82, 0, 1, _easeInOutCubic);
  const cardY = cardEnter * 64 + cardExit * 38;
  const cardOpacity =
  _map(p, 0.05, 0.22, 0, 1, _easeOutCubic) * (1 - _map(p, 0.60, 0.84, 0, 1, _easeInOutCubic));
  const cardScale = 1 - cardExit * 0.025;

  const mainY = _map(p, 0.56, 0.94, 94, 0, _easeInOutCubic);
  const mainOpacity = _map(p, 0.52, 0.76, 0, 1, _easeInOutCubic);
  const mainShadow = _map(p, 0.56, 0.94, 0, 1, _easeOutCubic);

  const navy900 = [11, 31, 59];
  const ink250 = [196, 202, 210];
  const blend = (t) => navy900.map((v, i) => Math.round(_lerp(v, ink250[i], t))).join(", ");
  const titleStyle = {
    transform: `translate(-50%, calc(-50% + ${titleY}px)) scale(${titleScale})`,
    opacity: titleOpacity,
    color: `rgb(${blend(titleColorT)})`
  };
  const cardStyle = {
    transform: `translate(-50%, calc(-50% + ${cardY}vh)) scale(${cardScale})`,
    opacity: cardOpacity
  };
  const mainStyle = {
    transform: `translateY(${mainY}vh)`,
    opacity: mainOpacity,
    boxShadow: `0 -32px 80px -16px rgba(11,31,59,${(0.20 * mainShadow).toFixed(3)})`
  };

  if (isStatic) {
    return (
      <section className="lp-story is-static" id="features">
        <div className="lp-shell">
          <h2 className="lp-story-title is-static">
            <span className="line">{t("feat.title", "Features")}</span>
          </h2>
          <aside className="lp-story-card is-static">
            <div className="lp-story-card-eyebrow"><span className="tick" />{t("feat.eyebrow", "Wisbid features")}</div>
            <p className="lp-story-card-lead">{t("feat.lead", "Three building blocks. One operating layer for facilities work.")}</p>
            <p className="lp-story-card-sub">{t("feat.sub", "A co-pilot for every operator, an intake agent that listens to every channel, and a workspace that moves with your team across desktop and mobile (Android & iOS).")}</p>
          </aside>
          <div className="lp-story-main is-static">
            <div className="lp-story-main-inner">
              <header className="lp-story-main-head">
                <span className="eyebrow"><span className="num">03</span>{t("feat.mainEyebrow", "Features").replace(/^03/, "")}</span>
                <div className="lp-story-main-headcol">
                  <h3>{t("feat.mainHeadline", "Three features, one connected operating layer.")}</h3>
                  <p>{t("feat.mainDesc", "Each feature stands on its own — but they sit in the same workspace, share the same context, and travel with the same governance.")}</p>
                </div>
              </header>
              <div className="lp-story-grid lp-story-grid--3">
                {FEATURE_CARDS.map((c) => <FeatureCard key={c.kind} c={c} />)}
              </div>
            </div>
          </div>
        </div>
      </section>);

  }

  return (
    <section ref={sectionRef} className="lp-story" id="features">
      <div className="lp-story-pin">
        <FeaturesTitle style={titleStyle} />
        <FeaturesCard style={cardStyle} />
        <FeaturesMain style={mainStyle} />
      </div>
    </section>);

};

window.Features = Features;
