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

// =====================================================================
// LumitecHomepage — single-page homepage for lumitec.ai
// =====================================================================

const LOGO_TILES = [
  { x: 22, y: 22, fill: "#0A3C5E" },
  { x: 50, y: 22, fill: "#1E88C7" },
  { x: 22, y: 50, fill: "#21B5E8" },
  { x: 50, y: 50, fill: "#7DD0E8" },
];

function LogoMark({ size = 38 }) {
  return (
    <svg
      width={size}
      height={size}
      viewBox="0 0 100 100"
      xmlns="http://www.w3.org/2000/svg"
      aria-hidden="true"
      style={{ flex: "none" }}
    >
      <g transform="rotate(45 50 50)">
        {LOGO_TILES.map((t, i) => (
          <rect key={i} x={t.x} y={t.y} width="28" height="28" fill={t.fill} rx="1.5" />
        ))}
      </g>
    </svg>
  );
}

function NavLogo({ dark }) {
  return (
    <a href="#top" className="nav-logo" style={{ color: dark ? "#0A3C5E" : "#fff" }}>
      <LogoMark size={36} />
      <span className="word">
        LUMITEC<span className="ai">AI</span>
      </span>
    </a>
  );
}

// ---------------------------------------------------------------------
// Hero Canvas — 70×38 grid, two layers, oblique projection, capped 30fps
// ---------------------------------------------------------------------

function HeroCanvas({ opacity = 1 }) {
  const canvasRef = useRef(null);
  const rafRef = useRef(0);
  const motion = useMotionPref();

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    if (motion === "off") {
      canvas.style.display = "none";
      return;
    }
    canvas.style.display = "";
    const ctx = canvas.getContext("2d");
    let w = 0;
    let h = 0;
    const dpr = Math.min(window.devicePixelRatio || 1, 1.5);

    const resize = () => {
      const parent = canvas.parentElement;
      if (!parent) return;
      w = parent.clientWidth;
      h = parent.clientHeight;
      canvas.style.width = w + "px";
      canvas.style.height = h + "px";
      canvas.width = w * dpr;
      canvas.height = h * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    if (canvas.parentElement) ro.observe(canvas.parentElement);

    // -----------------------------------------------------------
    // 3D mesh wave — regular grid of dots on a tilted plane,
    // displaced by sine waves to form a flowing sheet that sweeps
    // across the lower half of the hero.
    // -----------------------------------------------------------

    // Quality tiers — pick one based on device, then drop if frames are slow.
    // Software-rendered Canvas (VMs, low-end GPUs) hates 22k arc()+lighter ops
    // per frame; tiers below keep the wave shape but thin the dot grid and
    // skip the additive blend on weak hardware.
    const tiers = [
      { cols: 140, rows: 78, d: 31, useRect: true,  lighter: true,  sizeMul: 1.15 },
      { cols: 100, rows: 55, d: 44, useRect: true,  lighter: false, sizeMul: 1.40 },
      { cols: 70,  rows: 38, d: 63, useRect: true,  lighter: false, sizeMul: 1.70 },
      { cols: 44,  rows: 24, d: 95, useRect: true,  lighter: false, sizeMul: 2.20 },
    ];
    let tIdx = 0;
    const cores = navigator.hardwareConcurrency || 8;
    // Be more conservative — start sparser if hardware looks weak so we don't
    // hammer the user for the first ~1s of adaptive sampling.
    if (cores <= 8) tIdx = 1;
    if (cores <= 4) tIdx = 2;
    if (cores <= 2) tIdx = 3;
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) tIdx = 3;
    let q = tiers[tIdx];

    let cols = q.cols;
    let rows = q.rows;
    let dx = q.d;
    let dz = q.d;
    let planeW = (cols - 1) * dx;
    let planeD = (rows - 1) * dz;
    let sizeMul = q.sizeMul;
    let useRect = q.useRect;
    let useLighter = q.lighter;

    // Adaptive: sample first ~30 frames, drop a tier if avg dt is too high.
    const frameSamples = [];
    const adaptIfSlow = () => {
      if (tIdx >= tiers.length - 1 || frameSamples.length < 30) return;
      const avg = frameSamples.reduce((a, b) => a + b, 0) / frameSamples.length;
      frameSamples.length = 0;
      if (avg > 45) {
        tIdx += 1;
        q = tiers[tIdx];
        cols = q.cols; rows = q.rows; dx = q.d; dz = q.d;
        planeW = (cols - 1) * dx; planeD = (rows - 1) * dz;
        sizeMul = q.sizeMul; useRect = q.useRect; useLighter = q.lighter;
      }
    };

    // Camera tilt: how much we look DOWN at the plane.
    // 0 = looking edge-on, pi/2 = looking straight down (top view).
    // ~30deg gives the "low-angle 3D landscape" look.
    // Near-horizontal camera. Small tilt = looking ALONG the plane, so
    // wave height (wy) maps almost 1:1 into screen-Y. This is what makes
    // the surface read as a 3D wave instead of a flat grid of dots.
    const tilt = 0.18;
    const sinT = Math.sin(tilt);
    const cosT = Math.cos(tilt);
    const fov = 1300;
    const camDist = 900;

    let t = 0;
    let last = performance.now();
    const targetFps = 30;
    const minFrame = 1000 / targetFps;

    // Surface displacement. Travelling waves with low spatial frequency
    // so we get LONG ridges, not tight rings.
    // Toned-down amplitude. Still 3D, but quieter and less intense.
    const wavY = (wx, wz, time) => {
      const a1 = Math.sin(wx * 0.0040 + wz * 0.0030 + time * 0.00050) * 170;
      const a2 = Math.cos(wx * 0.0019 - wz * 0.0034 + time * 0.00038) * 85;
      const lift = (wx / 1000) * 55;
      return a1 + a2 + lift;
    };

    const frame = (now) => {
      const dt = now - last;
      if (dt < minFrame) {
        rafRef.current = requestAnimationFrame(frame);
        return;
      }
      last = now;
      t += dt;

      frameSamples.push(dt);
      adaptIfSlow();

      ctx.clearRect(0, 0, w, h);
      ctx.globalCompositeOperation = useLighter ? "lighter" : "source-over";

      // Place the screen-space anchor: where the plane's centre projects to.
      // We want the wave to fill the lower half / right side of the hero.
      // Anchor low in the hero so we look UP at the wave from a low angle.
      const cx = w * 0.5;
      const cy = h * 0.78;

      const breathe = 0.92 + 0.08 * Math.sin(t * 0.0004);

      for (let r = 0; r < rows; r++) {
        for (let c = 0; c < cols; c++) {
          // Plane-local coords centred on origin.
          const wx = c * dx - planeW / 2;
          const wz = r * dz - planeD * 0.5;

          // Vertical displacement from waves.
          const wy = wavY(wx, wz, t) * breathe;

          // Tilt the plane around X-axis (so back lifts up toward camera).
          // After tilt: y' = y*cos - z*sin ; z' = y*sin + z*cos
          const yT = wy * cosT - wz * sinT;
          const zT = wy * sinT + wz * cosT;

          // Push into camera space.
          const camZ = zT + camDist;
          if (camZ < 80) continue;

          const persp = fov / camZ;
          const sx = cx + wx * persp;
          const sy = cy + yT * persp;

          if (sx < -20 || sx > w + 20 || sy < -20 || sy > h + 20) continue;

          // ---- Shading ----
          // Brightness driven by surface "facing camera" — proxy this with
          // wy (height): high crests catch light, troughs go dark.
          const heightNorm = (wy + 150) / 300; // 0..1
          // Depth fade: dots behind us get hazier.
          const depthFade = Math.min(1, persp * 1.4);
          // Atmospheric haze for very-far rows.
          const farFade = 1 - Math.max(0, (r / rows - 0.7) * 2.2);

          // Per-particle subtle shimmer
          const seed = (c * 92821) ^ (r * 689287);
          const sparkPhase = (seed % 1000) / 1000 * Math.PI * 2;
          const spark = 0.88 + 0.12 * Math.sin(t * 0.0012 + sparkPhase);

          // Soft horizontal density bias: fade out particles that fall on the
          // upper-left text area. This is in SCREEN space, so it adapts to
          // any viewport size. We only fade the upper-left quadrant.
          const inUpperLeft =
            sx < w * 0.46 && sy < h * 0.55;
          const textMask = inUpperLeft
            ? Math.max(0, Math.min(1,
                (sx / (w * 0.46)) * 0.4 +
                ((sy - h * 0.20) / (h * 0.35)) * 0.6
              ))
            : 1;

          // Toned-down shading: softer crest highlight, dimmer body.
          const isCrest = heightNorm > 0.62;
          let size, alpha, colour;
          if (isCrest) {
            size = (1.2 + (heightNorm - 0.62) * 3.5) * persp * 0.85 * sizeMul;
            alpha = (0.45 + (heightNorm - 0.62) * 0.7) *
                    depthFade * farFade * spark * textMask * opacity;
            colour = "rgba(150,215,245,";
          } else {
            size = (0.8 + heightNorm * 0.6) * persp * 0.75 * sizeMul;
            alpha = (0.13 + heightNorm * 0.32) *
                    depthFade * farFade * spark * textMask * opacity;
            colour = "rgba(60,165,220,";
          }

          if (alpha < 0.025) continue;
          if (size < 0.3) continue;

          ctx.fillStyle = colour + alpha.toFixed(3) + ")";
          if (useRect && !isCrest) {
            // fillRect is ~2× cheaper than arc()+fill on software-rasterised
            // Canvas. Body dots are tiny so a square reads the same; crest
            // dots stay round for the glow shape.
            const s = size * 2;
            ctx.fillRect(sx - size, sy - size, s, s);
          } else {
            ctx.beginPath();
            ctx.arc(sx, sy, size, 0, Math.PI * 2);
            ctx.fill();
          }
        }
      }

      ctx.globalCompositeOperation = "source-over";
      if (running) rafRef.current = requestAnimationFrame(frame);
    };

    // Run RAF only when (a) the tab is visible AND (b) the canvas is
    // intersecting the viewport. The footer canvas otherwise spins at 30fps
    // burning CPU/GPU while the user is reading the hero — biggest single win
    // for software-rendered Canvas on Windows VMs.
    let running = false;
    let onScreen = false;
    const start = () => {
      if (running || document.hidden || !onScreen) return;
      running = true;
      last = performance.now();
      rafRef.current = requestAnimationFrame(frame);
    };
    const stop = () => {
      running = false;
      cancelAnimationFrame(rafRef.current);
    };

    const io = new IntersectionObserver((entries) => {
      onScreen = entries.some((e) => e.isIntersecting);
      if (onScreen) start(); else stop();
    }, { rootMargin: "100px" });
    io.observe(canvas);

    const onVis = () => {
      if (document.hidden) stop(); else start();
    };
    document.addEventListener("visibilitychange", onVis);

    return () => {
      cancelAnimationFrame(rafRef.current);
      ro.disconnect();
      document.removeEventListener("visibilitychange", onVis);
    };
  }, [opacity, motion]);

  return <canvas ref={canvasRef} className="hero-canvas" aria-hidden="true" />;
}

// Motion preference hook — read/write to localStorage. "auto" = respect OS, "on" = force on, "off" = force off.
function useMotionPref() {
  const [pref, setPref] = useState(() => {
    try { return localStorage.getItem("lumitec-motion") || "on"; } catch (e) { return "on"; }
  });
  useEffect(() => {
    const onChange = (e) => setPref(e.detail);
    window.addEventListener("lumitec-motion-change", onChange);
    return () => window.removeEventListener("lumitec-motion-change", onChange);
  }, []);
  return pref;
}

function MotionToggle() {
  // Replaced by Tweaks panel — kept as a no-op shim for compatibility.
  return null;
}

// ---------------------------------------------------------------------
// Reusable bits
// ---------------------------------------------------------------------

function Reveal({ children, delay = 0, as: Tag = "div", className = "", style = {} }) {
  const ref = useRef(null);
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduce) {
      setVisible(true);
      return;
    }
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            setVisible(true);
            io.disconnect();
          }
        });
      },
      { threshold: 0.15, rootMargin: "0px 0px -10% 0px" }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return (
    <Tag
      ref={ref}
      className={className}
      style={{
        ...style,
        opacity: visible ? 1 : 0,
        transform: visible ? "translateY(0)" : "translateY(40px)",
        transition: `opacity 750ms cubic-bezier(0.16,1,0.3,1) ${delay}ms, transform 750ms cubic-bezier(0.16,1,0.3,1) ${delay}ms`,
      }}
    >
      {children}
    </Tag>
  );
}

function CTAButton({ children, href = "#book", variant = "primary", onClick, type, ariaLabel }) {
  const cls = `cta cta-${variant}`;
  if (type === "submit") {
    return (
      <button type="submit" className={cls} aria-label={ariaLabel}>
        <span className="cta-label">{children}</span>
        <span className="cta-beam" aria-hidden="true" />
      </button>
    );
  }
  return (
    <a href={href} className={cls} onClick={onClick} aria-label={ariaLabel}>
      <span className="cta-label">{children}</span>
      <span className="cta-beam" aria-hidden="true" />
    </a>
  );
}

// ---------------------------------------------------------------------
// 1. NavBar
// ---------------------------------------------------------------------

function NavBar() {
  const navRef = useRef(null);
  const [mobileOpen, setMobileOpen] = useState(false);
  const [scrolled, setScrolled] = useState(false);
  // Continuous 0→1 progress through the hero — drives a smooth crossfade
  // between the dark hero nav and the white scrolled nav.
  useEffect(() => {
    const onScroll = () => {
      const vh = window.innerHeight;
      const start = vh * 0.55;
      const end = vh * 0.95;
      // Read scroll position defensively from whichever container is actually
      // scrolling (window vs body vs html) — body becomes the scroll root in
      // some browsers when overflow-x is set on it.
      const y = window.scrollY
        || document.documentElement.scrollTop
        || document.body.scrollTop
        || 0;
      const p = Math.max(0, Math.min(1, (y - start) / (end - start)));
      // Active flag turns on as soon as we scroll at all — pops in the navy
      // backdrop block so hero content doesn't bleed through the nav.
      const active = y > 4 ? 1 : 0;
      if (navRef.current) {
        navRef.current.style.setProperty("--nav-progress", String(p));
        navRef.current.style.setProperty("--nav-scroll-active", String(active));
      }
      setScrolled(p > 0.55);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    document.addEventListener("scroll", onScroll, { passive: true, capture: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      document.removeEventListener("scroll", onScroll, { capture: true });
      window.removeEventListener("resize", onScroll);
    };
  }, []);

  const links = [
    { label: "Services", href: "#services" },
    { label: "How we work", href: "#how" },
    { label: "Case studies", href: "#cases" },
    { label: "ROI Calculator", href: "#roi" },
    { label: "Insights", href: "#insights" },
    { label: "Team", href: "#team" },
  ];

  return (
    <nav ref={navRef} className={`nav ${scrolled ? "nav-scrolled" : ""}`} aria-label="Primary">
      <NavLogo dark={scrolled} />
      <div className="nav-links" role="navigation">
        {links.map((l) => (
          <a key={l.label} href={l.href}>
            {l.label}
          </a>
        ))}
      </div>
      <div className="nav-right">
        <a href="#book" className="nav-cta">
          Book a call
        </a>
        <button
          className="nav-burger"
          aria-label="Open menu"
          aria-expanded={mobileOpen}
          onClick={() => setMobileOpen((v) => !v)}
        >
          <span /><span /><span />
        </button>
      </div>
      {mobileOpen && (
        <div className="nav-mobile" role="menu">
          {links.map((l) => (
            <a key={l.label} href={l.href} onClick={() => setMobileOpen(false)}>
              {l.label}
            </a>
          ))}
          <a href="#book" onClick={() => setMobileOpen(false)} className="nav-mobile-cta">
            Book a call →
          </a>
        </div>
      )}
    </nav>
  );
}

// ---------------------------------------------------------------------
// 2. Hero
// ---------------------------------------------------------------------

function Hero() {
  return (
    <section className="hero" id="top">
      <HeroCanvas />
      <div className="hero-vignette" aria-hidden="true" />
      <div className="hero-content">
        <h1 className="hero-h1">AI you can trust your business with.</h1>
        <p className="hero-sub">Built safely. Scaled properly. Run by people you can call.</p>
        <p className="hero-lead">
          We design, build and operate production AI systems around your data,
          workflows and commercial goals from focused internal platforms to
          multi-site and international deployments.
        </p>
        <div className="hero-cta-wrap">
          <CTAButton variant="primary" href="#book">
            Book a free 30-min call <span className="arrow">→</span>
          </CTAButton>
        </div>
      </div>
      <div className="hero-scroll" aria-hidden="true">
        <svg width="22" height="34" viewBox="0 0 22 34" fill="none">
          <rect x="1" y="1" width="20" height="32" rx="10" stroke="currentColor" strokeWidth="1.4" />
          <rect x="10" y="7" width="2" height="6" rx="1" fill="currentColor">
            <animate attributeName="opacity" values="1;0.2;1" dur="2.4s" repeatCount="indefinite" />
          </rect>
        </svg>
        <span>SCROLL</span>
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 3. Proof anchor
// ---------------------------------------------------------------------

function ProofBand() {
  const tiers = [
    "Multi-national franchisee group",
    "UK planning campaign",
    "US managed services provider",
    "UK multi-location group",
  ];
  return (
    <section className="proof">
      <div className="proof-inner">
        <Reveal>
          <p className="eyebrow">Current and recent work includes</p>
        </Reveal>
        <Reveal delay={80}>
          <div className="logo-strip">
            {tiers.map((t) => (
              <span key={t} className="client-tier">{t}</span>
            ))}
          </div>
        </Reveal>
        <Reveal delay={160}>
          <p className="partner-line">
            Built across modern AI and cloud platforms including
            {" "}<b>OpenAI</b>, <b>Anthropic</b>, <b>Microsoft</b>,{" "}
            <b>Google</b>, <b>AWS</b> and <b>Cloudflare</b>.
          </p>
        </Reveal>
        <Reveal delay={220}>
          <div className="stat-cards">
            <div className="stat-card">
              <div className="num">10+ years</div>
              <div className="label">
                Historic business data turned into searchable, usable operational
                intelligence for teams that need faster access to organisational knowledge.
              </div>
            </div>
            <div className="stat-card">
              <div className="num">Multi-source</div>
              <div className="label">
                AI workflows built around documents, CRM data, calls, emails,
                screenshots, tickets and existing business systems.
              </div>
            </div>
            <div className="stat-card">
              <div className="num">50+ years</div>
              <div className="label">
                Combined founder experience across IT, infrastructure and
                business systems — including 6+ years working specifically in
                applied AI.
              </div>
            </div>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 4. Value prop
// ---------------------------------------------------------------------

function ValueProp() {
  const bullets = [
    {
      icon: (
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
          <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
          <path d="m9 12 2 2 4-4" />
        </svg>
      ),
      title: "Designed around your data.",
      body:
        "We build around your existing systems, identity and permissions — Microsoft 365, Google Workspace, CRM, PSA, document stores and databases. Data handling is defined up front, with clear boundaries on access, storage and retention.",
    },
    {
      icon: (
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
          <path d="M3 12h18" />
          <path d="M12 3v18" />
          <circle cx="12" cy="12" r="9" />
          <path d="M12 3a13 13 0 0 1 4 9 13 13 0 0 1-4 9 13 13 0 0 1-4-9 13 13 0 0 1 4-9z" />
        </svg>
      ),
      title: "Multi-model, no lock-in.",
      body:
        "We choose the right model and architecture for the workload — not the provider with the loudest marketing. Systems can be designed to move between models as quality, cost and capability change.",
    },
    {
      icon: (
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
          <path d="M22 16.92V21a1 1 0 0 1-1.1 1 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 3.18 4.1 1 1 0 0 1 4.18 3h4.09a1 1 0 0 1 1 .75 12.84 12.84 0 0 0 .7 2.81 1 1 0 0 1-.23 1L7.91 9.91a16 16 0 0 0 6 6l2.35-1.83a1 1 0 0 1 1-.23 12.84 12.84 0 0 0 2.81.7 1 1 0 0 1 .75 1z" />
        </svg>
      ),
      title: "Built for production.",
      body:
        "We design for monitoring, testing, permissions, reliability and support from the start. The aim is not a clever prototype — it is a system your business can depend on.",
    },
  ];

  return (
    <section className="value" id="services">
      <div className="value-inner">
        <Reveal>
          <div className="value-left">
            <p className="eyebrow eyebrow-light">What we do</p>
            <h2>AI succeeds when it is built around the business — not the demo.</h2>
            <p className="lead">
              The hard part is not finding an AI model. It is knowing what is
              worth building, keeping data safe, integrating with existing
              systems, and making sure the result still works when the tools,
              models and business requirements change. That is where Lumitec AI
              operates: production systems, real workflows, measurable value.
            </p>
          </div>
        </Reveal>
        <div className="value-bullets">
          {bullets.map((b, i) => (
            <Reveal key={b.title} delay={120 + i * 80}>
              <article className="bullet">
                <div className="b-icon" aria-hidden="true">
                  {b.icon}
                </div>
                <h3>{b.title}</h3>
                <p>{b.body}</p>
              </article>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 5. How we work
// ---------------------------------------------------------------------

function HowWeWork() {
  const steps = [
    {
      n: "01",
      verb: "DIAGNOSE",
      meta: "1–2 weeks · fixed fee",
      body:
        "A paid AI Readiness Diagnostic. We sit with you for a couple of weeks, learn how your business actually works, and map the highest-value AI opportunities. You get a 15-page plain-English diagnostic — yours to keep, whether or not we ever work together again.",
    },
    {
      n: "02",
      verb: "BUILD",
      meta: "4–12 weeks",
      body:
        "A small embedded team — tech lead, senior engineer, part-time AI ops engineer — shipping production AI on a 2-week cadence. Working in your tools, on your stack, behind your auth.",
    },
    {
      n: "03",
      verb: "OPERATE",
      meta: "Ongoing · optional",
      body:
        "Managed AI operations — for businesses that don't want to staff this internally. We monitor, update models when better ones come out, run evals before changes go live, and we're on-call for production issues.",
    },
  ];

  return (
    <section className="how" id="how">
      <div className="how-inner">
        <Reveal>
          <p className="eyebrow eyebrow-light">How we work</p>
        </Reveal>
        <Reveal delay={80}>
          <h2>Three steps. One team. No handovers.</h2>
        </Reveal>
        <Reveal delay={140}>
          <p className="how-sub">
            We do all three steps in-house — so you do not get one consultancy that
            sells you the strategy and a different one that fails to ship it.
          </p>
        </Reveal>
        <div className="how-grid">
          {steps.map((s, i) => (
            <Reveal key={s.n} delay={200 + i * 100}>
              <article className="step">
                <div className="step-head">
                  <span className="step-n">{s.n}</span>
                  <span className="step-meta">{s.meta}</span>
                </div>
                <h3 className="step-verb">{s.verb}</h3>
                <p className="step-body">{s.body}</p>
                <div className="step-rule" aria-hidden="true" />
              </article>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 6. Case studies
// ---------------------------------------------------------------------

function CaseStudies() {
  const cases = [
    {
      tier: "Multi-national franchisee group · multi-site operations",
      headline:
        "Custom AI applications for a complex multi-site franchise network.",
      summary:
        "Lumitec AI is developing scalable custom applications for a large multi-national franchisee group, supporting data categorisation, cost analysis, operational insight and CRM-linked coaching workflows within Microsoft Dynamics.",
      quote:
        "Darren, Operations Director: \"Lumitec understood the complexity quickly and turned it into practical systems our teams can actually use.\"",
    },
    {
      tier: "UK planning campaign · high-volume document intelligence",
      headline:
        "Planning documents, submissions and evidence made searchable, usable and actionable.",
      summary:
        "Lumitec AI supported the Norwich to Tilbury action group by aggregating Planning Inspectorate materials, public submissions and supporting evidence into a database-driven workflow for AI-assisted querying, analysis and document retrieval.",
      quote:
        "Rosie, Campaign Lead: \"Lumitec gave us a way to work with a huge volume of planning material without losing the thread.\"",
    },
    {
      tier: "US managed services provider · PSA and CMS workflow",
      headline:
        "AI-assisted ticket creation and reporting across complex service desk systems.",
      summary:
        "Lumitec AI built scalable front-end interfaces into complex PSA and CMS platforms, allowing tickets to be created from call recordings, screenshots, emails and text input, while unlocking deep reporting from more than 20 years of service data.",
      quote:
        "MSP Leadership Team: \"The system turns fragmented information into structured tickets and usable insight for our helpdesk.\"",
    },
    {
      tier: "UK multi-location group · bid and tender intelligence",
      headline:
        "Ten years of bid history turned into a practical RFP response engine.",
      summary:
        "Lumitec AI built a dedicated workflow to ingest more than a decade of successful and unsuccessful sales bid data, giving RFP teams a practical interface to draft stronger tender responses using historic knowledge that traditional systems could not surface.",
      quote:
        "Commercial Director: \"Lumitec helped us unlock years of bid knowledge and turn it into something our teams can use at speed.\"",
    },
  ];
  return (
    <section className="cases" id="cases">
      <div className="cases-inner">
        <Reveal>
          <p className="eyebrow eyebrow-light">Case studies</p>
        </Reveal>
        <Reveal delay={80}>
          <h2>Real systems. Real operational value.</h2>
        </Reveal>
        <Reveal delay={140}>
          <p className="cases-sub">
            From planning intelligence and service desk automation to CRM-linked
            coaching workflows and bid-response platforms, Lumitec AI builds
            systems around real business data, real users and real operational
            constraints.
          </p>
        </Reveal>
        <div className="case-grid">
          {cases.map((c, i) => (
            <Reveal key={c.tier} delay={200 + i * 80}>
              <article className="case">
                <h3 className="case-headline">{c.headline}</h3>
                <p className="case-summary">{c.summary}</p>
                <p className="case-quote"><em>{c.quote}</em></p>
              </article>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 7. Team
// ---------------------------------------------------------------------

function Team() {
  const team = [
    {
      name: "Andrew Lee",
      role: "Co-Founder",
      prior: [
        "Andrew has worked in IT since 1998, later founding and exiting a successful Managed Service Provider at the end of 2021. His background spans infrastructure, cybersecurity, managed services, client success and commercial technology delivery.",
        "At Lumitec AI, Andrew focuses on turning complex AI requirements into production-ready platforms that can support ambitious SMEs, multi-site businesses and international organisations.",
      ],
      ethos:
        "AI has to be useful, scalable and commercially real — otherwise it's just noise.",
      initials: "AL",
      photo: "assets/team-andrew-lee.png",
    },
    {
      name: "Andrew Walters",
      role: "Co-Founder",
      prior: [
        "Andrew brings decades of experience across ERP, consultancy, industry and large-scale business systems. His work has centred on designing and delivering technology that supports complex operations and real business change.",
        "At Lumitec AI, Andrew focuses on robust architecture, scalable platforms and practical systems that turn complex operational needs into usable business capability.",
      ],
      ethos:
        "Good technology should not just support the business — it should expand what the business is capable of.",
      initials: "AW",
      photo: "assets/andrew-walters.png",
      photoPosition: "center 28%",
    },
  ];

  return (
    <section className="team" id="team">
      <div className="team-inner">
        <Reveal>
          <p className="eyebrow eyebrow-light">Team</p>
        </Reveal>
        <Reveal delay={80}>
          <h2>The team you'll actually work with.</h2>
        </Reveal>
        <Reveal delay={140}>
          <p className="team-sub">
            No layered consultancies. No "principal architect" bait-and-switch. The senior
            people who scope your project are the same people who design, build, deliver
            and support it — from first conversation to production system.
          </p>
        </Reveal>
        <div className="team-grid">
          {team.map((m, i) => (
            <Reveal key={m.name} delay={200 + i * 80}>
              <article className="member">
                <div className={`member-photo ${m.photo ? "member-photo-img" : ""}`} aria-hidden={m.photo ? undefined : "true"}>
                  {m.photo ? (
                    <img src={m.photo} alt={`Portrait of ${m.name}`} loading="lazy" style={m.photoPosition ? { objectPosition: m.photoPosition } : undefined} />
                  ) : (
                    <span>{m.initials}</span>
                  )}
                </div>
                <h3 className="member-name">{m.name}</h3>
                <p className="member-role">{m.role}</p>
                {(Array.isArray(m.prior) ? m.prior : [m.prior]).map((para, j) => (
                  <p key={j} className="member-prior">{para}</p>
                ))}
                <p className="member-ethos"><em>"{m.ethos}"</em></p>
              </article>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 8. Mid-page CTA
// ---------------------------------------------------------------------

function MidCTA() {
  return (
    <section className="midcta">
      <div className="midcta-glow" aria-hidden="true" />
      <div className="midcta-inner">
        <Reveal>
          <h2>Let's talk about the system your business actually needs.</h2>
        </Reveal>
        <Reveal delay={100}>
          <p>
            Tell us what you are trying to improve, automate, rescue or scale. We
            will spend 30 minutes with you, ask practical questions, and tell you
            honestly where AI can help — and where it cannot.
          </p>
        </Reveal>
        <Reveal delay={180}>
          <CTAButton variant="primary" href="#book">
            Book a free 30-min call <span className="arrow">→</span>
          </CTAButton>
        </Reveal>
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 8b. ROI Calculator
// ---------------------------------------------------------------------

// Non-linear team-size scale: every integer 1–10, then every 5 to 100, then every 25 to 500.
const TEAM_STEPS = (() => {
  const out = [];
  for (let i = 1; i <= 10; i++) out.push(i);
  for (let i = 15; i <= 100; i += 5) out.push(i);
  for (let i = 125; i <= 500; i += 25) out.push(i);
  return out;
})();

function ROICalculator() {
  const [teamIdx, setTeamIdx] = useState(TEAM_STEPS.indexOf(10));
  const team = TEAM_STEPS[teamIdx];
  const [hours, setHours] = useState(4);
  const [rate, setRate] = useState(45);
  const [adoption, setAdoption] = useState(60);
  // Currency: GBP (default) or USD. When switching currency we bump the
  // hourly-cost slider to a sensible default for the new market — but only
  // if it's still sitting on the *previous* default, so we don't override
  // a user who has already adjusted it.
  const [currency, setCurrency] = useState("GBP");
  const symbol = currency === "GBP" ? "£" : "$";
  const locale = currency === "GBP" ? "en-GB" : "en-US";
  const switchCurrency = (next) => {
    if (next === currency) return;
    if (next === "USD" && rate === 45) setRate(60);
    if (next === "GBP" && rate === 60) setRate(45);
    setCurrency(next);
  };

  const weekly = Math.round(team * (adoption / 100) * hours * rate);
  const annual = weekly * 48;
  const fmt = (n) =>
    symbol + n.toLocaleString(locale, { maximumFractionDigits: 0 });

  const activeUsers = Math.round(team * (adoption / 100));

  return (
    <section className="roi" id="roi">
      <div className="roi-inner">
        <Reveal>
          <p className="eyebrow eyebrow-light">ROI Calculator</p>
        </Reveal>
        <Reveal delay={80}>
          <h2>Model the commercial opportunity.</h2>
        </Reveal>
        <Reveal delay={140}>
          <p className="roi-sub">
            A simple starting point, not a quote. Use the sliders to estimate
            potential capacity, efficiency or cost impact. We will pressure-test
            the assumptions properly during the Diagnostic.
          </p>
        </Reveal>
        <Reveal delay={200}>
          <div className="roi-card">
            <div className="roi-controls">
              <div className="roi-field">
                <div className="roi-field-row">
                  <label htmlFor="roi-team">People in scope</label>
                  <span className="roi-value">{team}</span>
                </div>
                <input
                  id="roi-team"
                  type="range"
                  min="0"
                  max={TEAM_STEPS.length - 1}
                  step="1"
                  value={teamIdx}
                  onChange={(e) => setTeamIdx(+e.target.value)}
                />
                <p className="roi-help">Knowledge workers whose week the system would touch.</p>
              </div>

              <div className="roi-field">
                <div className="roi-field-row">
                  <label htmlFor="roi-hours">Hours saved per person, per week</label>
                  <span className="roi-value">{hours}h</span>
                </div>
                <input
                  id="roi-hours"
                  type="range"
                  min="1"
                  max="12"
                  step="1"
                  value={hours}
                  onChange={(e) => setHours(+e.target.value)}
                />
                <p className="roi-help">Drafting, searching, summarising, ticket triage, reporting.</p>
              </div>

              <div className="roi-field">
                <div className="roi-field-row">
                  <label htmlFor="roi-rate">Fully-loaded hourly cost ({symbol})</label>
                  <span className="roi-value">{symbol}{rate}</span>
                </div>
                <input
                  id="roi-rate"
                  type="range"
                  min="20"
                  max="120"
                  step="5"
                  value={rate}
                  onChange={(e) => setRate(+e.target.value)}
                />
                <p className="roi-help">Salary plus on-costs, divided by working hours. {currency === "GBP" ? "£45 is typical for UK SME knowledge staff." : "$60–$80 is typical for US SME knowledge staff."}</p>
              </div>

              <div className="roi-field">
                <div className="roi-field-row">
                  <label htmlFor="roi-adopt">Realistic adoption</label>
                  <span className="roi-value">{adoption}%</span>
                </div>
                <input
                  id="roi-adopt"
                  type="range"
                  min="20"
                  max="100"
                  step="5"
                  value={adoption}
                  onChange={(e) => setAdoption(+e.target.value)}
                />
                <p className="roi-help">Share of the team genuinely using the system each week. We rarely model above 70% in year one.</p>
              </div>
            </div>

            <div className="roi-output">
              <div className="roi-currency-toggle" role="group" aria-label="Currency">
                <button
                  type="button"
                  className={currency === "GBP" ? "active" : ""}
                  onClick={() => switchCurrency("GBP")}
                  aria-pressed={currency === "GBP"}
                >£ GBP</button>
                <button
                  type="button"
                  className={currency === "USD" ? "active" : ""}
                  onClick={() => switchCurrency("USD")}
                  aria-pressed={currency === "USD"}
                >$ USD</button>
              </div>
              <p className="roi-out-label">Indicative annual capacity value</p>
              <p className="roi-out-num">{fmt(annual)}</p>
              <p className="roi-out-sub">
                ≈ {fmt(weekly)} per week. Of {team} {team === 1 ? "person" : "people"} in scope, ~{activeUsers} actively using the system at {adoption}% adoption.
              </p>
              <ul className="roi-assumptions">
                <li>48 working weeks per year</li>
                <li>Value = people × adoption × hours × hourly cost</li>
                <li>Excludes revenue upside, error reduction and customer impact</li>
              </ul>
              <div className="roi-cta-row">
                <CTAButton variant="primary" href="#book">
                  Pressure-test this with us <span className="arrow">→</span>
                </CTAButton>
              </div>
            </div>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 9. FAQ
// ---------------------------------------------------------------------

function FAQ() {
  const faqs = [
    {
      q: "I'm not even sure what AI can do for my business. Should I still book?",
      a: "Yes — that is exactly the kind of call we like. Many strong AI opportunities start with a business problem, not a technical brief. We will spend 30 minutes asking about your business and tell you honestly whether AI is worth exploring, and where to start if it is.",
    },
    {
      q: "How is this different from hiring an AI engineer in-house?",
      a: "Hiring internally can take months before anything useful ships. Lumitec AI gives you senior AI, software, data and delivery capability without having to build the whole team yourself. We can help you move quickly, then support, operate or hand over depending on what your business needs.",
    },
    {
      q: "Will my data be safe?",
      a: "Data handling is defined at the start of every engagement. We build around your existing systems, identity and permissions, including Microsoft 365, Google Workspace and other business platforms. Access, storage, retention and residency requirements are agreed before anything goes live.",
    },
    {
      q: "What if a model provider raises prices or quality drops?",
      a: "We design systems to avoid unnecessary model lock-in. Where appropriate, workloads can be routed between different models or providers as cost, quality and capability change. The architecture matters as much as the model.",
    },
    {
      q: "Are you going to try to sell me something we don't need?",
      a: "No. The Diagnostic is designed to identify what is genuinely worth building and what is not. If the right answer is \"do not do AI yet,\" that is what we will tell you.",
    },
    {
      q: "How fast can you start?",
      a: "Diagnostics typically start within 2 weeks. Build timelines depend on scope, integrations and data readiness, but we work in focused phases so clients can see progress early rather than waiting months for a big reveal.",
    },
    {
      q: "Can you fix an AI project that's already gone sideways?",
      a: "Yes. We can review stalled or underperforming AI projects, identify what is salvageable, and give you a clear route forward. If the project needs to be stopped or rebuilt properly, we will say so.",
    },
    {
      q: "What's the smallest engagement you'll take?",
      a: "Most clients start with a Diagnostic. Build projects are scoped around the value, complexity and level of integration required, with smaller production builds typically starting in the tens of thousands.",
    },
  ];
  const [open, setOpen] = useState(0);
  return (
    <section className="faq" id="insights">
      <div className="faq-inner">
        <Reveal>
          <p className="eyebrow eyebrow-light">FAQ</p>
        </Reveal>
        <Reveal delay={80}>
          <h2>The questions owners actually ask.</h2>
        </Reveal>
        <div className="faq-list">
          {faqs.map((f, i) => {
            const isOpen = open === i;
            return (
              <Reveal key={i} delay={120 + i * 30}>
                <div className={`faq-item ${isOpen ? "open" : ""}`}>
                  <button
                    className="faq-q"
                    aria-expanded={isOpen}
                    onClick={() => setOpen(isOpen ? -1 : i)}
                  >
                    <span className="faq-q-text">{f.q}</span>
                    <span className="faq-q-icon" aria-hidden="true">
                      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
                        <path d="m6 9 6 6 6-6" />
                      </svg>
                    </span>
                  </button>
                  <div className="faq-a-wrap" style={{ gridTemplateRows: isOpen ? "1fr" : "0fr" }}>
                    <div className="faq-a-inner">
                      <p>{f.a}</p>
                    </div>
                  </div>
                </div>
              </Reveal>
            );
          })}
        </div>
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 10. Final CTA + form
// ---------------------------------------------------------------------

// Country dial codes — small curated list, "Other" lets people type their own.
const DIAL_CODES = [
  { code: "+44", country: "UK" },
  { code: "+1",  country: "US / Canada" },
  { code: "+353", country: "Ireland" },
  { code: "+33", country: "France" },
  { code: "+49", country: "Germany" },
  { code: "+34", country: "Spain" },
  { code: "+39", country: "Italy" },
  { code: "+31", country: "Netherlands" },
  { code: "+41", country: "Switzerland" },
  { code: "+46", country: "Sweden" },
  { code: "+47", country: "Norway" },
  { code: "+45", country: "Denmark" },
  { code: "+61", country: "Australia" },
  { code: "+64", country: "New Zealand" },
  { code: "+971", country: "UAE" },
  { code: "+852", country: "Hong Kong" },
  { code: "+65", country: "Singapore" },
  { code: "+91", country: "India" },
  { code: "+86", country: "China" },
  { code: "+81", country: "Japan" },
];

// After a successful book-call submit we send people to the calendar booker.
// The exact URL is read from <meta name="lumitec-calendar-url"> so it's
// configurable per-environment without code changes (see index.html).
function getCalendarBookingUrl() {
  if (typeof document === "undefined") return null;
  const m = document.querySelector('meta[name="lumitec-calendar-url"]');
  return m ? m.getAttribute("content") : null;
}

// Turnstile widget — loads the cf-turnstile script lazily on mount, renders
// the (invisible) widget into a target div, and reports the token via callback.
// In dev we don't have a real site key, so we fall back to a dummy token so
// the form is still submittable when running the design locally.
function useTurnstile(siteKeyMetaName, onToken) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const meta = document.querySelector(`meta[name="${siteKeyMetaName}"]`);
    const siteKey = meta ? meta.getAttribute("content") : null;
    // No site key configured (local dev) — skip Turnstile, send a dev token.
    if (!siteKey || siteKey === "TURNSTILE_SITE_KEY_PLACEHOLDER") {
      onToken("dev-no-turnstile");
      return;
    }
    const ensureScript = () => new Promise((resolve) => {
      if (window.turnstile) return resolve();
      const existing = document.getElementById("cf-turnstile-script");
      if (existing) {
        existing.addEventListener("load", () => resolve());
        return;
      }
      const s = document.createElement("script");
      s.id = "cf-turnstile-script";
      s.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
      s.async = true;
      s.defer = true;
      s.onload = () => resolve();
      document.head.appendChild(s);
    });
    let widgetId = null;
    ensureScript().then(() => {
      if (!ref.current || !window.turnstile) return;
      widgetId = window.turnstile.render(ref.current, {
        sitekey: siteKey,
        size: "flexible",
        appearance: "always",
        theme: "auto",
        callback: (token) => onToken(token),
        "error-callback": () => onToken(null),
        "expired-callback": () => onToken(null),
      });
    });
    return () => {
      if (widgetId !== null && window.turnstile) {
        try { window.turnstile.remove(widgetId); } catch (_) {}
      }
    };
  }, [siteKeyMetaName]);
  return ref;
}

function FinalCTA() {
  const [submitted, setSubmitted] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [serverError, setServerError] = useState(null);
  const [form, setForm] = useState({
    name: "",
    email: "",
    dial: "+44",
    phone: "",
    note: "",
  });
  const [errors, setErrors] = useState({});
  const [turnstileToken, setTurnstileToken] = useState(null);
  const turnstileRef = useTurnstile("lumitec-turnstile-sitekey", setTurnstileToken);

  const onSubmit = async (e) => {
    e.preventDefault();
    setServerError(null);
    const errs = {};
    if (!form.name.trim()) errs.name = "Please tell us your name.";
    if (!form.email.trim()) errs.email = "We need an email to reply to.";
    else if (!/^\S+@\S+\.\S+$/.test(form.email)) errs.email = "That email doesn't look right.";
    if (!form.phone.trim()) errs.phone = "Please add a phone number so we can call.";
    else if (form.phone.replace(/\D/g, "").length < 6) errs.phone = "That phone number looks too short.";
    setErrors(errs);
    if (Object.keys(errs).length > 0) return;
    if (!turnstileToken) {
      setServerError("Captcha hasn't loaded yet — give it a second and try again.");
      return;
    }
    setSubmitting(true);
    try {
      const res = await fetch("/api/book-call", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          name: form.name.trim(),
          email: form.email.trim(),
          phone: `${form.dial} ${form.phone.trim()}`,
          note: form.note.trim(),
          turnstileToken,
        }),
      });
      if (!res.ok) {
        const data = await res.json().catch(() => ({}));
        throw new Error(data.error || `Submission failed (${res.status}).`);
      }
      setSubmitted(true);
      // Redirect to the booking calendar after a beat.
      const calUrl = getCalendarBookingUrl();
      if (calUrl) {
        setTimeout(() => { window.location.href = calUrl; }, 1500);
      }
    } catch (err) {
      setServerError(err.message || "Something went wrong. Please try again, or use the contact page.");
    } finally {
      setSubmitting(false);
    }
  };

  // Generate a fake calendar grid for the success state
  return (
    <section className="finalcta" id="book">
      <HeroCanvas opacity={0.5} />
      <div className="finalcta-overlay" aria-hidden="true" />
      <div className="finalcta-inner">
        <Reveal>
          <p className="eyebrow eyebrow-on-dark">Book a call</p>
        </Reveal>
        <Reveal delay={80}>
          <h2>One conversation. Clear next steps.</h2>
        </Reveal>
        <Reveal delay={140}>
          <p className="finalcta-sub">
            Tell us where you are with AI — exploring, stuck, scaling or starting
            again. We will respond within one working day, and usually much sooner
            during UK business hours.
          </p>
        </Reveal>

        {!submitted ? (
          <Reveal delay={220}>
            <form className="book-form" onSubmit={onSubmit} noValidate>
              <div className="field">
                <label htmlFor="f-name">Name <span className="req" aria-hidden="true">*</span></label>
                <input
                  id="f-name"
                  type="text"
                  required
                  value={form.name}
                  onChange={(e) => setForm({ ...form, name: e.target.value })}
                  aria-invalid={!!errors.name}
                  aria-describedby={errors.name ? "f-name-err" : undefined}
                  autoComplete="name"
                />
                {errors.name && <p id="f-name-err" className="field-err" role="alert">{errors.name}</p>}
              </div>
              <div className="field">
                <label htmlFor="f-email">Work email <span className="req" aria-hidden="true">*</span></label>
                <input
                  id="f-email"
                  type="email"
                  required
                  value={form.email}
                  onChange={(e) => setForm({ ...form, email: e.target.value })}
                  aria-invalid={!!errors.email}
                  aria-describedby={errors.email ? "f-email-err" : undefined}
                  autoComplete="email"
                />
                {errors.email && <p id="f-email-err" className="field-err" role="alert">{errors.email}</p>}
              </div>
              <div className="field field-wide">
                <label htmlFor="f-phone">Phone <span className="req" aria-hidden="true">*</span></label>
                <div className="phone-row">
                  <select
                    className="phone-dial"
                    aria-label="Country dial code"
                    value={form.dial}
                    onChange={(e) => setForm({ ...form, dial: e.target.value })}
                  >
                    {DIAL_CODES.map((d) => (
                      <option key={d.code} value={d.code}>{d.code} · {d.country}</option>
                    ))}
                  </select>
                  <input
                    id="f-phone"
                    type="tel"
                    className="phone-num"
                    required
                    placeholder="7700 900123"
                    value={form.phone}
                    onChange={(e) => setForm({ ...form, phone: e.target.value })}
                    aria-invalid={!!errors.phone}
                    aria-describedby={errors.phone ? "f-phone-err" : undefined}
                    autoComplete="tel-national"
                  />
                </div>
                {errors.phone && <p id="f-phone-err" className="field-err" role="alert">{errors.phone}</p>}
              </div>
              <div className="field field-wide">
                <label htmlFor="f-note">What are you trying to do — or worried about — with AI? <span className="optional">(optional)</span></label>
                <textarea
                  id="f-note"
                  rows="4"
                  placeholder="e.g. an AI assistant in our product · saving time on internal admin · figuring out where to start · rescuing a stalled project"
                  value={form.note}
                  onChange={(e) => setForm({ ...form, note: e.target.value })}
                />
              </div>
              <div className="field field-wide turnstile-field">
                <div ref={turnstileRef} className="cf-turnstile" />
              </div>
              {serverError && (
                <div className="field field-wide">
                  <p className="field-err" role="alert">{serverError}</p>
                </div>
              )}
              <div className="form-row">
                <CTAButton variant="primary" type="submit" disabled={submitting}>
                  {submitting ? "Sending…" : <>Book my call <span className="arrow">→</span></>}
                </CTAButton>
                <p className="form-fineprint">
                  We will only use your details to reply about your enquiry. No mailing list.
                </p>
              </div>
            </form>
          </Reveal>
        ) : (
          <div className="success">
            <div className="success-head">
              <span className="success-tick" aria-hidden="true">
                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M5 12l4 4L19 7" />
                </svg>
              </span>
              <h3>Got it — taking you to the calendar</h3>
            </div>
            <p className="success-sub">
              Thanks {form.name.split(" ")[0] || "for getting in touch"}. We will send a
              confirmation to <span className="hl">{form.email || "your inbox"}</span>.
              Redirecting you to pick a slot…
            </p>
            <p className="calendar-note">
              <em>If the calendar doesn't open automatically, <a href={getCalendarBookingUrl() || "#"} className="hl">click here</a>.</em>
            </p>
          </div>
        )}
      </div>
    </section>
  );
}

// ---------------------------------------------------------------------
// 11. Footer
// ---------------------------------------------------------------------

function Footer() {
  const cols = [
    {
      title: "Services",
      links: [
        { label: "AI Readiness Diagnostic", href: "#services" },
        { label: "Build production AI", href: "#services" },
        { label: "Managed AI operations", href: "#services" },
        { label: "Stalled-project rescue", href: "#services" },
      ],
    },
    {
      title: "Company",
      links: [
        { label: "About", href: "#" },
        { label: "Team", href: "#team" },
        { label: "Contact", href: "contact.html" },
        { label: "Careers", href: "#" },
      ],
    },
    {
      title: "Resources",
      links: [
        { label: "Case studies", href: "#cases" },
        { label: "Insights", href: "#" },
        { label: "AI Readiness Checklist", href: "#" },
        { label: "ROI calculator", href: "#roi" },
      ],
    },
    {
      title: "Delivery",
      links: [
        { label: "UK-based", href: "#" },
        { label: "Supporting UK and US clients", href: "#" },
        { label: "Remote-first delivery", href: "#" },
        { label: "Multi-location deployments", href: "#" },
      ],
    },
  ];
  return (
    <footer className="footer">
      <div className="footer-inner">
        <div className="footer-brand">
          <NavLogo dark />
          <p className="footer-tag">
            AI you can trust your business with. Built safely, scaled properly, run by
            people you can call.
          </p>
          <p className="footer-phone">
            <span className="footer-phone-label">UK</span>
            <a href="tel:+441904916700">01904 916700</a>
          </p>
        </div>
        <div className="footer-cols">
          {cols.map((c) => (
            <div key={c.title} className="footer-col">
              <p className="footer-h">{c.title}</p>
              <ul>
                {c.links.map((l) => (
                  <li key={l.label}><a href={l.href}>{l.label}</a></li>
                ))}
              </ul>
            </div>
          ))}
        </div>
      </div>
      <div className="footer-rule" aria-hidden="true" />
      <div className="footer-bar">
        <span>© 2026 Lumitec AI Ltd</span>
        <span className="footer-dot">·</span>
        <span>ICO registered</span>
        <span className="footer-dot">·</span>
        <span>UK-data-residency available</span>
        <span className="footer-spacer" />
        <a href="privacy.html">Privacy</a>
        <a href="terms-website.html">Website T&amp;Cs</a>
        <a href="terms-business.html">Business T&amp;Cs</a>
      </div>
    </footer>
  );
}

// ---------------------------------------------------------------------
// Root
// ---------------------------------------------------------------------

// ---------------------------------------------------------------------
// Tweaks state — exposes hero variant + cinematic bg pref via context
// ---------------------------------------------------------------------

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "cinematicBg": "on"
}/*EDITMODE-END*/;

const TweakCtx = React.createContext({ tweaks: TWEAK_DEFAULTS, setTweak: () => {} });

function LumitecHomepage() {
  const [tweaks, setTweakRaw] = window.useTweaks
    ? window.useTweaks(TWEAK_DEFAULTS)
    : [TWEAK_DEFAULTS, () => {}];
  // Normalize setTweak to accept (key, value) signature used in this file.
  const setTweak = React.useCallback((k, v) => {
    if (typeof k === "object") setTweakRaw(k);
    else setTweakRaw({ [k]: v });
  }, [setTweakRaw]);

  // Push cinematic pref into the same channel HeroCanvas listens on
  useEffect(() => {
    try { localStorage.setItem("lumitec-motion", tweaks.cinematicBg); } catch (e) {}
    window.dispatchEvent(new CustomEvent("lumitec-motion-change", { detail: tweaks.cinematicBg }));
  }, [tweaks.cinematicBg]);

  return (
    <TweakCtx.Provider value={{ tweaks, setTweak }}>
      <a href="#top" className="skip-link">Skip to content</a>
      <NavBar />
      <main>
        <Hero />
        <ProofBand />
        <ValueProp />
        <HowWeWork />
        <CaseStudies />
        <Team />
        <MidCTA />
        <ROICalculator />
        <FAQ />
        <FinalCTA />
      </main>
      <Footer />
      <LumitecTweaks />
    </TweakCtx.Provider>
  );
}

function LumitecTweaks() {
  const { tweaks, setTweak } = React.useContext(TweakCtx);
  const TP = window.TweaksPanel;
  const TS = window.TweakSection;
  const TR = window.TweakRadio;
  if (!TP || !TS || !TR) return null;
  return (
    <TP title="Tweaks">
      <TS title="Cinematic background">
        <TR
          label="State"
          value={tweaks.cinematicBg}
          onChange={(v) => setTweak("cinematicBg", v)}
          options={[
            { value: "on", label: "On" },
            { value: "auto", label: "Auto" },
            { value: "off", label: "Off" },
          ]}
        />
        <p style={{ fontSize: 12, color: "rgba(0,0,0,0.55)", lineHeight: 1.5, margin: "4px 0 0" }}>
          Auto respects your OS reduced-motion setting.
        </p>
      </TS>
    </TP>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<LumitecHomepage />);
