/* ============================================================
   LPM shared components, loaded on every page before the
   page-specific script. Exposes components on window.
   ============================================================ */
const { useState, useEffect, useRef } = React;

const VIDEO_SRC = "assets/launch-video.mp4?v=2";
const HOME = "index.html";
// The waitlist POSTs to the platform API (different origin, CORS is handled there).
const WAITLIST_API = "https://app.liftportfoliomanagement.com.au/api/waitlist";
const PAGES = [
  { href: "contracts.html", label: "Contracts & Pricing" },
  { href: "invoicing.html", label: "Invoice Audit" },
  { href: "maintenance-tender.html", label: "Run a Tender" },
  { href: "activity.html", label: "Activity & Planning" },
  { href: "enterprise.html", label: "Portfolio" },
  { href: "ai.html", label: "Ask Lola" },
];
const NAV_PAGES = [
  { href: "contracts.html", label: "Contracts & Pricing" },
  { href: "invoicing.html", label: "Invoice Audit" },
  { href: "activity.html", label: "Activity & Planning" },
  { href: "enterprise.html", label: "Portfolio" },
  { href: "ai.html", label: "Ask Lola" },
];

/* ---------- brand mark (dot-matrix from favicon) ---------- */
function Mark({ size = 26 }) {
  const cols = [18, 34, 50, 66, 82];
  const r = {
    18: [1.4, 2.1, 2.45, 2.1, 1.4],
    34: [2.1, 3.85, 4.55, 3.85, 2.1],
    50: [2.8, 5.25, 7, 5.25, 2.8],
    66: [2.1, 3.85, 4.55, 3.85, 2.1],
    82: [1.4, 2.1, 2.45, 2.1, 1.4],
  };
  return (
    <svg className="brand__mark" width={size} height={size} viewBox="0 0 100 100" style={{ width: size, height: size }}>
      <rect width="100" height="100" rx="22" fill="#1b1b1f" stroke="rgba(255,255,255,0.09)" />
      {cols.map((cx) =>
        [18, 34, 50, 66, 82].map((cy, i) => (
          <circle key={cx + "-" + cy} cx={cx} cy={cy} r={r[cx][i]} fill="#f4f4f5" />
        ))
      )}
    </svg>
  );
}

/* ---------- AI mark (accent mini dot-grid) ---------- */
function AIMark({ size = 14 }) {
  const pos = [5, 12, 19];
  const r = [[1.1, 1.5, 1.1], [1.5, 2.2, 1.5], [1.1, 1.5, 1.1]];
  return (
    <span className="aimark" style={{ width: size, height: size }}>
      <svg width={size} height={size} viewBox="0 0 24 24">
        {pos.map((cx, i) => pos.map((cy, j) => (
          <circle key={i + "-" + j} cx={cx} cy={cy} r={r[j][i]} fill="currentColor" />
        )))}
      </svg>
    </span>
  );
}

/* ---------- icons ---------- */
const ico = {
  file: <><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M5 22V4a2 2 0 0 1 2-2h7l6 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2Z"/></>,
  receipt: <><path d="M4 2v20l2-1.5L8 22l2-1.5L12 22l2-1.5L16 22l2-1.5L20 22V2l-2 1.5L16 2l-2 1.5L12 2l-2 1.5L8 2 6 3.5 4 2Z"/><path d="M8 8h8M8 12h6"/></>,
  wrench: <path d="M14.7 6.3a4 4 0 0 0-5.4 5.3L3 18v3h3l6.4-6.4a4 4 0 0 0 5.3-5.4l-2.6 2.6-2.1-.5-.5-2.1 2.6-2.6Z"/>,
  phone: <path d="M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L20 13l2 5v4a2 2 0 0 1-2 2A18 18 0 0 1 3 6a2 2 0 0 1 2-2Z"/>,
  trend: <><path d="M22 7 13.5 15.5l-4-4L2 19"/><path d="M16 7h6v6"/></>,
  shield: <><path d="M12 2 4 5v6c0 5 3.4 8.5 8 11 4.6-2.5 8-6 8-11V5l-8-3Z"/><path d="m9 12 2 2 4-4"/></>,
  check: <path d="m20 6-11 11-5-5"/>,
  arrow: <path d="M5 12h14M13 6l6 6-6 6"/>,
  clipboard: <><rect width="8" height="4" x="8" y="2" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></>,
  clipboardList: <><rect width="8" height="4" x="8" y="2" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4M12 16h4M8 11h.01M8 16h.01"/></>,
  fileCheck: <><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M5 22V4a2 2 0 0 1 2-2h7l6 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2Z"/><path d="m9 15 2 2 4-4"/></>,
  fileSearch: <><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M5 22V4a2 2 0 0 1 2-2h7l6 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2Z"/><circle cx="11.4" cy="13.4" r="2.4"/><path d="m15 17-1.7-1.7"/></>,
  book: <><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2Z"/></>,
  scan: <><path d="M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2"/><path d="M7 12h10"/></>,
  coins: <><circle cx="8" cy="8" r="5"/><path d="M18.09 10.37A5 5 0 1 1 10.34 18M7 6h1v4M16.71 13.88l.7.71-2.82 2.82"/></>,
  menu: <path d="M3 12h18M3 6h18M3 18h18"/>,
  close: <path d="M18 6 6 18M6 6l12 12"/>,
  alert: <><path d="m10.3 3.3-8 14A1.9 1.9 0 0 0 4 20h16a1.9 1.9 0 0 0 1.7-2.7l-8-14a1.9 1.9 0 0 0-3.4 0Z"/><path d="M12 9v4M12 17h.01"/></>,
  scale: <><path d="M12 3v18"/><path d="M6 21h12"/><path d="M3 8h18"/><path d="m6 8-3 6a3 3 0 0 0 6 0L6 8Z"/><path d="m18 8-3 6a3 3 0 0 0 6 0l-3-6Z"/></>,
  sparkles: <><path d="M12 3l1.6 4.4L18 9l-4.4 1.6L12 15l-1.6-4.4L6 9l4.4-1.6L12 3Z"/><path d="M19 14l.7 1.9L22 17l-2.3.6L19 20l-.7-1.9L16 17l2.3-.6L19 14Z"/></>,
  upload: <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v12"/></>,
  award: <><circle cx="12" cy="8" r="5"/><path d="M8.2 12.2 7 22l5-3 5 3-1.2-9.8"/></>,
  gauge: <><path d="M12 13l4-3.5"/><path d="M3.5 16a9 9 0 1 1 17 0"/></>,
  calendar: <><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></>,
  layers: <><path d="m12 2 9 5-9 5-9-5 9-5Z"/><path d="m3 12 9 5 9-5"/><path d="m3 17 9 5 9-5"/></>,
  search: <><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></>,
  swap: <><path d="M17 3l4 4-4 4"/><path d="M21 7H3"/><path d="M7 21l-4-4 4-4"/><path d="M3 17h18"/></>,
  building: <><rect x="4" y="2" width="16" height="20" rx="1.5"/><path d="M9 22v-4h6v4"/><path d="M8 6h.01M12 6h.01M16 6h.01M8 10h.01M12 10h.01M16 10h.01M8 14h.01M12 14h.01M16 14h.01"/></>,
  lock: <><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></>,
  bell: <><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></>,
  mail: <><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 7 10 6 10-6"/></>,
  doc: <><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M5 22V4a2 2 0 0 1 2-2h7l6 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2Z"/><path d="M9 13h6M9 17h4"/></>,
  sound: <><path d="M11 5 6 9H2v6h4l5 4V5Z"/><path d="M15.5 8.5a5 5 0 0 1 0 7"/><path d="M18.8 5.8a9 9 0 0 1 0 12.4"/></>,
  mute: <><path d="M11 5 6 9H2v6h4l5 4V5Z"/><path d="m22 9-6 6M16 9l6 6"/></>,
  play: <path d="m7 4 13 8-13 8V4Z"/>,
  pause: <path d="M8 5v14M16 5v14"/>,
};
function Icon({ name, stroke = 1.6 }) {
  return (
    <svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round" style={{ flex: "none" }}>
      {ico[name] || null}
    </svg>
  );
}

/* ---------- badge ---------- */
function Badge({ children = "Launching soon · AU & NZ" }) {
  return <div className="badge"><span className="badge__dot"></span>{children}</div>;
}

/* ---------- waitlist ---------- */
function Waitlist({ center = false, variant = "accent", cta = "Join the waitlist" }) {
  const [name, setName] = useState("");
  const [company, setCompany] = useState("");
  const [email, setEmail] = useState("");
  const [state, setState] = useState("idle");
  const [errorMsg, setErrorMsg] = useState("");
  const clearError = () => { if (state === "error") { setState("idle"); setErrorMsg(""); } };
  async function submit(e) {
    e.preventDefault();
    if (!name.trim()) { setErrorMsg("Please enter your name."); setState("error"); return; }
    if (!company.trim()) { setErrorMsg("Please enter your business or organisation."); setState("error"); return; }
    if (!email.includes("@")) { setErrorMsg("Please enter a valid email address."); setState("error"); return; }
    setState("loading");
    try {
      const res = await fetch(WAITLIST_API, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name: name.trim(), company: company.trim(), email: email.trim() }),
      });
      const data = await res.json().catch(() => ({}));
      if (res.ok) { if (window.gtag) window.gtag("event", "waitlist_signup"); setState("success"); return; }
      setErrorMsg(data.error || "Something went wrong. Please try again.");
      setState("error");
    } catch {
      setErrorMsg("Couldn't reach the server. Please try again in a moment.");
      setState("error");
    }
  }
  if (state === "success") {
    return (
      <div className={"waitlist" + (center ? " waitlist--center" : "")}>
        <div className="wl__success">
          <div className="wl__success-title">
            <span style={{ color: "var(--accent)", display: "inline-flex" }}><Icon name="check" stroke={2.2} /></span>
            You're on the list.
          </div>
          <p className="wl__success-sub">We'll reach out before we open the doors. Something good is coming.</p>
        </div>
      </div>
    );
  }
  return (
    <form className={"waitlist" + (center ? " waitlist--center" : "")} onSubmit={submit}>
      <input className="wl__input" type="text" placeholder="Your name" value={name}
        onChange={(e) => { setName(e.target.value); clearError(); }} aria-label="Your name"
        style={{ marginBottom: 8, width: "100%" }} />
      <input className="wl__input" type="text" placeholder="Business or organisation" value={company}
        onChange={(e) => { setCompany(e.target.value); clearError(); }} aria-label="Business or organisation"
        style={{ marginBottom: 8, width: "100%" }} />
      <div className="wl__row">
        <input className="wl__input" type="email" placeholder="you@company.com.au" value={email}
          onChange={(e) => { setEmail(e.target.value); clearError(); }} aria-label="Email address" />
        <button className={"btn btn--" + variant} type="submit" disabled={state === "loading"}>
          {state === "loading" ? "Joining…" : <>{cta}<span className="arr"><Icon name="arrow" /></span></>}
        </button>
      </div>
      {state === "error"
        ? <p className="wl__err">{errorMsg}</p>
        : <p className="wl__note"><span style={{ color: "var(--accent)" }}>·</span> Free 30-day trial at launch. No card required.</p>}
      <p className="wl__note" style={{ marginTop: 4, fontSize: 12 }}>
        By joining, you agree to our <a href="https://app.liftportfoliomanagement.com.au/privacy" style={{ color: "inherit", textDecoration: "underline" }}>Privacy Policy</a>.
      </p>
    </form>
  );
}

/* ---------- motion: reveal-on-scroll, cursor glow, scroll tilt ---------- */
const MOTION_OK = !(window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches);

/* Renders nothing, attaches the page-wide motion behaviours after mount. */
function MotionFX() {
  // scroll reveal with sibling stagger
  useEffect(() => {
    if (!MOTION_OK || !("IntersectionObserver" in window)) return;
    const SEL = [
      ".section__head", ".fcard", ".aud__card", ".step", ".gpoint",
      ".guard__copy", ".drow__copy", ".drow__visual", ".fstep", ".stat",
      ".cta__inner", ".belief__q", ".founder__portrait", ".tl", ".cmethod",
      ".ledger", ".phero__inner", ".stage",
    ].join(",");
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); }
      });
    }, { threshold: 0.12, rootMargin: "0px 0px -7% 0px" });
    const vh = window.innerHeight || 800;
    const els = Array.from(document.querySelectorAll(SEL));
    els.forEach((el) => {
      const idx = el.parentElement ? Array.prototype.indexOf.call(el.parentElement.children, el) : 0;
      el.style.setProperty("--d", Math.min(idx, 6) * 80 + "ms");
      el.classList.add("reveal");
      // anything already in the first viewport reveals immediately, no
      // waiting on the observer's first async tick
      if (el.getBoundingClientRect().top < vh) el.classList.add("in");
      else io.observe(el);
    });
    return () => io.disconnect();
  }, []);

  // cursor-tracked glow position on cards
  useEffect(() => {
    const SEL = ".fcard,.aud__card,.step,.cmethod";
    const move = (e) => {
      const card = e.target && e.target.closest ? e.target.closest(SEL) : null;
      if (!card) return;
      const r = card.getBoundingClientRect();
      card.style.setProperty("--mx", e.clientX - r.left + "px");
      card.style.setProperty("--my", e.clientY - r.top + "px");
    };
    document.addEventListener("pointermove", move, { passive: true });
    return () => document.removeEventListener("pointermove", move);
  }, []);

  return null;
}

/* 3D tilt that flattens out as the element scrolls into view */
function ScrollTilt({ max = 16, children }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!MOTION_OK || !ref.current) return;
    const el = ref.current;
    let raf = 0;
    const update = () => {
      raf = 0;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight || 1;
      const p = Math.min(1, Math.max(0, (vh - r.top) / (vh * 0.7)));
      if (p >= 0.999) {
        // fully settled: drop the transform so text re-renders pixel-crisp
        el.style.transform = "none";
        el.style.willChange = "auto";
      } else {
        el.style.transform = `perspective(1100px) rotateX(${((1 - p) * max).toFixed(2)}deg) scale(${(0.96 + p * 0.04).toFixed(3)})`;
        el.style.willChange = "transform";
      }
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); };
    update();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [max]);
  return <div className="tilt" ref={ref}>{children}</div>;
}

/* count-up number that animates when scrolled into view */
function CountUp({ to, prefix = "", suffix = "", duration = 1600 }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const final = prefix + to.toLocaleString() + suffix;
    if (!MOTION_OK || !("IntersectionObserver" in window)) { el.textContent = final; return; }
    let raf = 0;
    const io = new IntersectionObserver((entries) => {
      if (!entries[0].isIntersecting) return;
      io.disconnect();
      const t0 = performance.now();
      const tick = (t) => {
        const p = Math.min(1, (t - t0) / duration);
        const eased = 1 - Math.pow(1 - p, 3);
        el.textContent = prefix + Math.round(to * eased).toLocaleString() + suffix;
        if (p < 1) raf = requestAnimationFrame(tick);
      };
      raf = requestAnimationFrame(tick);
    }, { threshold: 0.5 });
    io.observe(el);
    return () => { io.disconnect(); if (raf) cancelAnimationFrame(raf); };
  }, [to, prefix, suffix, duration]);
  return <span ref={ref}>{prefix}{to.toLocaleString()}{suffix}</span>;
}

/* ---------- video frame ---------- */
/* The launch film has no audio track yet (VO pending), so the control is a
   replay button. When the voiced render lands, restore the sound toggle. */
function VideoFrame() {
  const ref = useRef(null);
  useEffect(() => { if (ref.current) { ref.current.muted = true; ref.current.play().catch(() => {}); } }, []);
  function replay() {
    const v = ref.current;
    if (!v) return;
    v.currentTime = 0;
    v.play().catch(() => {});
  }
  return (
    <div className="vframe">
      <div className="vframe__media">
        <video ref={ref} autoPlay muted loop playsInline><source src={VIDEO_SRC} type="video/mp4" /></video>
        <div className="vframe__sheen"></div>
        <button className="vframe__sound" type="button" onClick={replay}>
          <Icon name="play" stroke={1.8} />
          Replay
        </button>
      </div>
    </div>
  );
}

/* ---------- nav ---------- */
function Nav({ current }) {
  const [scrolled, setScrolled] = useState(false);
  const [open, setOpen] = useState(false);
  useEffect(() => {
    const f = () => setScrolled(window.scrollY > 24);
    f(); window.addEventListener("scroll", f, { passive: true });
    return () => window.removeEventListener("scroll", f);
  }, []);
  return (
    <>
      <nav className="nav" data-scrolled={scrolled}>
        <a className="brand" href={HOME}><Mark size={26} /><span className="brand__name">LPM</span></a>
        <div className="nav__menu">
          {NAV_PAGES.map((p) => (
            <a key={p.href} className="nav__menu-link" data-current={current === p.href} href={p.href}>{p.label}</a>
          ))}
        </div>
        <div className="nav__right">
          <a className="nav__signin" href="https://app.liftportfoliomanagement.com.au/sign-in" onClick={() => { if (window.gtag) window.gtag("event", "sign_in_click"); }}>Sign in</a>
          <a className="nav__link" href={HOME + "#waitlist"}>Get early access</a>
          <button className="nav__burger" onClick={() => setOpen((o) => !o)} aria-label="Menu"><Icon name={open ? "close" : "menu"} /></button>
        </div>
      </nav>
      <div className={"nav__mobile" + (open ? " open" : "")}>
        {NAV_PAGES.map((p) => <a key={p.href} href={p.href}>{p.label}</a>)}
        <a className="nav__mobile-signin" href="https://app.liftportfoliomanagement.com.au/sign-in" onClick={() => { if (window.gtag) window.gtag("event", "sign_in_click"); }}>Sign in</a>
        <a className="btn btn--accent nav__mobile-cta" style={{ display: "flex" }} href={HOME + "#waitlist"}>Get early access</a>
      </div>
    </>
  );
}

/* ---------- footer ---------- */
function Footer() {
  return (
    <footer className="footer">
      <div className="wrap">
        <div className="footer__row">
          <div style={{ maxWidth: 268 }}>
            <a className="brand" href={HOME}><Mark size={24} /><span className="brand__name">LPM</span></a>
            <p style={{ fontSize: 13, color: "var(--muted)", lineHeight: 1.55, marginTop: 14 }}>
              A complete, independent view of your lift portfolio. Built for building owners and managers across AU &amp; NZ.
            </p>
          </div>
          <div className="footer__cols">
            <div className="footer__col">
              <h4>Platform</h4>
              {PAGES.map((p) => <a key={p.href} href={p.href}>{p.label}</a>)}
            </div>
            <div className="footer__col">
              <h4>Company</h4>
              <a href="about.html">About</a>
              <a href="contact.html">Contact</a>
              <a href="faq.html">FAQ</a>
              <a href={HOME + "#waitlist"}>Join waitlist</a>
              <a href={HOME}>Overview</a>
            </div>
            <div className="footer__col">
              <h4>Legal</h4>
              <a href="https://app.liftportfoliomanagement.com.au/terms">Terms</a>
              <a href="https://app.liftportfoliomanagement.com.au/privacy">Privacy</a>
              <a href="https://app.liftportfoliomanagement.com.au/disclaimer">Disclaimer</a>
              <a href="https://app.liftportfoliomanagement.com.au/cookies">Cookies</a>
              <a href="https://app.liftportfoliomanagement.com.au/refunds">Refunds &amp; Billing</a>
              <a href="https://app.liftportfoliomanagement.com.au/acceptable-use">Acceptable Use</a>
            </div>
          </div>
        </div>
        <div className="footer__bottom">
          <span>© {new Date().getFullYear()} Lift Portfolio Management Pty Ltd · ABN 55 699 041 391</span>
          <span>Independent by design. Built for the people who run buildings.</span>
        </div>
      </div>
    </footer>
  );
}

/* ---------- page hero ---------- */
function PageHero({ icon, kicker, crumb, title, sub, pills, actions }) {
  return (
    <section className="phero">
      <div className="wrap">
        <div className="phero__crumb"><a href={HOME}>LPM</a><span>/</span>{crumb}</div>
        <div className="phero__inner">
          <div className="phero__eyebrow">
            {icon && <span className="phero__ico"><Icon name={icon} /></span>}
            <span className="phero__kicker">{kicker}</span>
          </div>
          <h1 className="phero__title">{title}</h1>
          <p className="phero__sub">{sub}</p>
          <div className="phero__cta">
            <a className="btn btn--accent" href={HOME + "#waitlist"}>Get early access <span className="arr"><Icon name="arrow" /></span></a>
            {actions}
          </div>
          {pills && <div className="phero__pills">{pills.map((p, i) => <span className="pill" key={i}>{p}</span>)}</div>}
        </div>
      </div>
    </section>
  );
}

/* ---------- deep row (alternating feature block) ---------- */
function DeepRow({ kicker, title, desc, points, visual, rev }) {
  return (
    <div className={"drow" + (rev ? " drow--rev" : "")}>
      <div className="drow__copy">
        {kicker && <div className="drow__kicker"><span className="dot"></span>{kicker}</div>}
        <h2 className="drow__title">{title}</h2>
        <p className="drow__desc">{desc}</p>
        {points && (
          <div className="blist">
            {points.map((p, i) => (
              <div className="bitem" key={i}><span className="bitem__ico"><Icon name="check" stroke={2.2} /></span><span className="bitem__t">{p}</span></div>
            ))}
          </div>
        )}
      </div>
      <div className="drow__visual glow-pad">{visual}</div>
    </div>
  );
}

/* ---------- AI impartial verdict ---------- */
function Verdict({ tag, title, children, inPanel }) {
  return (
    <div className={"verdict" + (inPanel ? " verdict--in-panel" : "")}>
      <div className="verdict__head">
        <span className="verdict__ai"><AIMark size={12} /> Lola</span>
        {tag && <span className="verdict__tag">{tag}</span>}
      </div>
      {title && <div className="verdict__title">{title}</div>}
      <div className="verdict__body">{children}</div>
    </div>
  );
}

/* ---------- CTA section ---------- */
function CTASection({ title = "Be first to know.", sub = "LPM is launching soon. Join the waitlist and we'll reach out before we open the doors." }) {
  return (
    <section className="section" id="waitlist">
      <div className="wrap">
        <div className="cta">
          <div className="cta__inner">
            <span className="eyebrow">Get early access</span>
            <h2 className="section__title" style={{ marginTop: 0 }}>{title}</h2>
            <p className="section__sub" style={{ marginTop: 0 }}>{sub}</p>
            <Waitlist center variant="accent" />
            <div className="cta__actions">
              <a className="btn btn--ghost" href="contact.html">Book a walkthrough</a>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ---------- page shell ---------- */
function PageShell({ current, children }) {
  useEffect(() => { window.scrollTo(0, 0); }, []);
  return (
    <div className="page">
      <div className="aurora" aria-hidden="true"><i></i><i></i><i></i></div>
      <div className="grain"></div>
      <div className="shell">
        <Nav current={current} />
        {children}
        <Footer />
      </div>
      <MotionFX />
    </div>
  );
}

Object.assign(window, {
  VIDEO_SRC, HOME, PAGES, NAV_PAGES,
  Mark, AIMark, ico, Icon, Badge, Waitlist, VideoFrame,
  Nav, Footer, PageHero, DeepRow, Verdict, CTASection, PageShell,
  MotionFX, ScrollTilt, CountUp, MOTION_OK,
});
