/* global React, ReactDOM, FramerMotion */
/* Página /websites — experiência Captixy Websites */
const { motion: mw, AnimatePresence: APw } = FramerMotion;
const { useEffect: uEw, useMemo: uMw, useRef: uRw, useState: uSw } = React;
const {
  IconArrowRight,
  IconSparkle,
  Navbar: NBw,
  Footer: FTw,
  EASE_EXPO: EEw,
} = window;

const ASSISTANT_CONTACT_URL =
  "https://app.captixy.com/c/assistant?source=embed";

const CHAPTERS = [
  {
    id: "velocidade",
    index: "01",
    label: "Velocidade",
    title: (
      <>
        Online em <em>dias</em>, não meses.
      </>
    ),
    body: "Enquanto uma agência tradicional promete uma época inteira e uma factura pesada, entregamos um website profissional, rápido e pronto a captar clientes em poucos dias.",
    tone: "dark",
    visual: "status",
    cta: "Ver pacotes",
  },
  {
    id: "cuidado",
    index: "02",
    label: "Cuidado",
    title: (
      <>
        Design com ar de quem <em>leva o negócio a sério.</em>
      </>
    ),
    body: "Cada site Captixy é afinado à mão: hierarquia clara, tipografia cuidada, imagens leves e uma primeira impressão que transmite confiança.",
    tone: "light",
    visual: "studio",
  },
  {
    id: "clareza",
    index: "03",
    label: "Clareza",
    title: (
      <>
        Sem código. Sem jargão. <em>Sem complicações.</em>
      </>
    ),
    body: "Dizes-nos o que fazes, o que vendes e como queres ser contactado. Tratamos da estrutura, domínio, configuração técnica e publicação.",
    tone: "dark",
    visual: "blocks",
  },
  {
    id: "crescimento",
    index: "04",
    label: "Crescimento",
    title: (
      <>
        Feito para ser <em>encontrado</em> e converter.
      </>
    ),
    body: "Páginas rápidas, base SEO limpa e chamadas à acção bem posicionadas para transformar visitas em contactos reais todos os dias.",
    tone: "light",
    visual: "analytics",
  },
  {
    id: "suporte",
    index: "05",
    label: "Suporte",
    title: (
      <>
        Não desaparecemos <em>depois do lançamento.</em>
      </>
    ),
    body: "Tens apoio real para pequenas alterações, dúvidas e melhorias. O teu site não fica abandonado no dia em que vai para o ar.",
    tone: "dark",
    visual: "chat",
  },
];

const PACKAGES = [
  { value: "Inicial", label: "Ponto de partida" },
  { value: "5–7 dias", label: "Entrega típica" },
  { value: "∞", label: "Acompanhamento" },
];

function accent(alpha) {
  return `rgba(var(--accent-rgb), ${alpha})`;
}

function scrollToId(id) {
  const target = document.getElementById(id);
  if (target) target.scrollIntoView({ behavior: "smooth", block: "start" });
}

function ChapterEyebrow({ index, label, centered = false, light = false }) {
  return (
    <div
      className={`flex items-center gap-3 ${centered ? "justify-center lg:justify-start" : ""}`}
    >
      <span className="h-px w-7" style={{ background: "var(--orange)" }} />
      <span
        className="text-[11px] font-semibold uppercase tracking-[0.28em]"
        style={{ color: "var(--orange)" }}
      >
        {index} · {label}
      </span>
      {light && (
        <span className="h-px w-7" style={{ background: "var(--orange)" }} />
      )}
    </div>
  );
}

function ChapterVisual({ type, light = false }) {
  if (type === "status") return <StatusVisual />;
  if (type === "studio") return <StudioVisual />;
  if (type === "blocks") return <BlocksVisual />;
  if (type === "analytics") return <AnalyticsVisual />;
  if (type === "chat") return <ChatVisual />;
  return <BrowserPreview light={light} />;
}

function BrowserShell({ children, light = false, url = "o-teu-negocio.pt" }) {
  return (
    <div
      className="overflow-hidden rounded-[1.25rem]"
      style={{
        background: light ? "#FFFFFF" : "#0F172A",
        border: light
          ? "1px solid rgba(15,23,42,.08)"
          : "1px solid rgba(255,255,255,.10)",
        boxShadow: light
          ? "0 50px 120px -38px rgba(15,23,42,.45)"
          : "0 50px 120px -34px rgba(0,0,0,.8)",
      }}
    >
      <div
        className="flex items-center gap-2 border-b px-4 py-3"
        style={{
          background: light ? "#F8FAFC" : "rgba(255,255,255,.04)",
          borderColor: light ? "rgba(15,23,42,.06)" : "rgba(255,255,255,.06)",
        }}
      >
        <span
          className="h-2.5 w-2.5 rounded-full"
          style={{ background: "var(--orange)" }}
        />
        <span
          className="h-2.5 w-2.5 rounded-full"
          style={{ background: light ? "#CBD5E1" : "#334155" }}
        />
        <span
          className="h-2.5 w-2.5 rounded-full"
          style={{ background: light ? "#CBD5E1" : "#334155" }}
        />
        <span
          className="ml-2 flex-1 rounded-md px-3 py-1 text-[10px] tracking-wide"
          style={{
            background: light ? "rgba(15,23,42,.04)" : "rgba(255,255,255,.05)",
            color: light ? "#64748B" : "#64748B",
          }}
        >
          {url}
        </span>
      </div>
      {children}
    </div>
  );
}

function BrowserPreview() {
  return (
    <BrowserShell url="meusite.pt">
      <div className="space-y-3 p-5">
        <div
          className="rounded-xl p-4"
          style={{
            background:
              "linear-gradient(135deg, var(--orange-deep), var(--orange-soft))",
          }}
        >
          <div className="mb-2 h-3 w-3/4 rounded-full bg-white/75" />
          <div className="h-2 w-1/2 rounded-full bg-white/[0.45]" />
          <div className="mt-4 h-7 w-28 rounded-full bg-white/80" />
        </div>
        <div className="grid grid-cols-3 gap-2">
          {[0, 1, 2].map((i) => (
            <div
              key={i}
              className="h-14 rounded-lg"
              style={{ background: "rgba(255,255,255,.06)" }}
            />
          ))}
        </div>
      </div>
    </BrowserShell>
  );
}

function HeroWebsiteModel() {
  const canvasRef = uRw(null);
  const wrapRef = uRw(null);
  const [ready, setReady] = uSw(Boolean(window.THREE));

  uEw(() => {
    setReady(Boolean(window.THREE));
  }, []);

  uEw(() => {
    const THREE = window.THREE;
    const canvas = canvasRef.current;
    const wrap = wrapRef.current;
    if (!THREE || !canvas || !wrap) return undefined;

    const theme = getComputedStyle(document.documentElement);
    const cssVar = (name, fallback) =>
      theme.getPropertyValue(name).trim() || fallback;
    const accent = cssVar("--orange", "#6366F1");
    const accentSoft = cssVar("--orange-soft", "#818CF8");
    const accentDeep = cssVar("--orange-deep", "#4F46E5");
    const bg = cssVar("--bg", "#0F172A");
    const bgSoft = cssVar("--bg-soft", "#1E293B");
    const primary = cssVar("--primary", "#F1F5F9");
    const accentRgb = cssVar("--accent-rgb", "99,102,241");
    const accentRgba = (alpha) => `rgba(${accentRgb},${alpha})`;
    const threeColor = (value) => new THREE.Color(value);

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(40, 1, 0.1, 100);
    camera.position.set(0, 0, 8.6);

    const renderer = new THREE.WebGLRenderer({
      canvas,
      alpha: true,
      antialias: true,
      powerPreference: "high-performance",
    });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2.6));
    renderer.outputColorSpace = THREE.SRGBColorSpace;
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    renderer.toneMappingExposure = 1.08;

    scene.add(new THREE.AmbientLight(0xffffff, 0.42));
    const keyLight = new THREE.DirectionalLight(0xffffff, 0.5);
    keyLight.position.set(4, 5, 7);
    scene.add(keyLight);
    const accentLight = new THREE.PointLight(threeColor(accent), 1.2, 60);
    accentLight.position.set(-5, 3, 4);
    scene.add(accentLight);
    const softLight = new THREE.PointLight(threeColor(accentSoft), 0.82, 60);
    softLight.position.set(5, -3, 3);
    scene.add(softLight);
    const glint = new THREE.PointLight(0xffffff, 1.1, 45);
    scene.add(glint);

    const world = new THREE.Group();
    const root = new THREE.Group();
    const logoGroup = new THREE.Group();
    scene.add(world);
    world.add(root);
    root.add(logoGroup);

    const textureDisposables = [];

    const environmentCanvas = document.createElement("canvas");
    environmentCanvas.width = 1024;
    environmentCanvas.height = 512;
    const environmentCtx = environmentCanvas.getContext("2d");
    const environmentBg = environmentCtx.createLinearGradient(0, 0, 0, 512);
    environmentBg.addColorStop(0, "#1b2c49");
    environmentBg.addColorStop(0.5, bg);
    environmentBg.addColorStop(1, "#05070e");
    environmentCtx.fillStyle = environmentBg;
    environmentCtx.fillRect(0, 0, 1024, 512);
    const environmentBlob = (x, y, radius, color) => {
      const gradient = environmentCtx.createRadialGradient(
        x,
        y,
        0,
        x,
        y,
        radius,
      );
      gradient.addColorStop(0, color);
      gradient.addColorStop(1, "rgba(0,0,0,0)");
      environmentCtx.fillStyle = gradient;
      environmentCtx.fillRect(0, 0, 1024, 512);
    };
    environmentBlob(300, 150, 280, "rgba(255,255,255,0.55)");
    environmentBlob(770, 165, 240, accentRgba(0.55));
    environmentBlob(540, 370, 280, accentRgba(0.42));
    environmentBlob(120, 370, 220, "rgba(120,150,255,0.22)");
    const environmentStripe = environmentCtx.createLinearGradient(
      0,
      118,
      0,
      205,
    );
    environmentStripe.addColorStop(0, "rgba(255,255,255,0)");
    environmentStripe.addColorStop(0.5, "rgba(255,255,255,0.9)");
    environmentStripe.addColorStop(1, "rgba(255,255,255,0)");
    environmentCtx.fillStyle = environmentStripe;
    environmentCtx.fillRect(0, 118, 1024, 87);
    const environmentTexture = new THREE.CanvasTexture(environmentCanvas);
    environmentTexture.mapping = THREE.EquirectangularReflectionMapping;
    environmentTexture.colorSpace = THREE.SRGBColorSpace;
    const pmrem = new THREE.PMREMGenerator(renderer);
    pmrem.compileEquirectangularShader();
    const environment = pmrem.fromEquirectangular(environmentTexture).texture;
    scene.environment = environment;
    textureDisposables.push(environmentTexture, environment);
    pmrem.dispose();

    function roundRect(ctx, x, y, width, height, radius) {
      ctx.beginPath();
      ctx.moveTo(x + radius, y);
      ctx.lineTo(x + width - radius, y);
      ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
      ctx.lineTo(x + width, y + height - radius);
      ctx.quadraticCurveTo(
        x + width,
        y + height,
        x + width - radius,
        y + height,
      );
      ctx.lineTo(x + radius, y + height);
      ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
      ctx.lineTo(x, y + radius);
      ctx.quadraticCurveTo(x, y, x + radius, y);
      ctx.closePath();
    }

    const makeTexture = (width, height, draw) => {
      const textureCanvas = document.createElement("canvas");
      textureCanvas.width = width;
      textureCanvas.height = height;
      const ctx = textureCanvas.getContext("2d");
      draw(ctx, width, height);
      const texture = new THREE.CanvasTexture(textureCanvas);
      texture.colorSpace = THREE.SRGBColorSpace;
      texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
      texture.minFilter = THREE.LinearMipmapLinearFilter;
      texture.needsUpdate = true;
      textureDisposables.push(texture);
      return texture;
    };

    let disposed = false;
    let logoBorn = null;
    const logoDepth = 0.36;
    const colorStops = [
      threeColor(accentSoft),
      threeColor(accent),
      threeColor(accentDeep),
    ];
    const logoMaterial = new THREE.MeshPhysicalMaterial({
      vertexColors: true,
      color: 0xffffff,
      metalness: 0,
      roughness: 0.06,
      transmission: 0.82,
      ior: 1.46,
      clearcoat: 1,
      clearcoatRoughness: 0.07,
      reflectivity: 0.6,
      emissive: threeColor(accentDeep),
      emissiveIntensity: 0.18,
      envMapIntensity: 1.4,
      transparent: true,
      opacity: 0,
    });
    const lerpLogoColor = (value) => {
      const segment = value < 0.5 ? 0 : 1;
      const localValue = value < 0.5 ? value / 0.5 : (value - 0.5) / 0.5;
      const from = colorStops[segment];
      const to = colorStops[segment + 1];
      return new THREE.Color(
        from.r + (to.r - from.r) * localValue,
        from.g + (to.g - from.g) * localValue,
        from.b + (to.b - from.b) * localValue,
      );
    };
    const simplifyLogoPoints = (points, epsilon) => {
      if (points.length < 5) return points;
      const squareDistance = (a, b) => {
        const dx = a[0] - b[0];
        const dy = a[1] - b[1];
        return dx * dx + dy * dy;
      };
      const segmentDistance = (point, start, end) => {
        let x = start[0];
        let y = start[1];
        const dx = end[0] - x;
        const dy = end[1] - y;
        if (dx || dy) {
          const t =
            ((point[0] - x) * dx + (point[1] - y) * dy) / (dx * dx + dy * dy);
          if (t > 1) {
            x = end[0];
            y = end[1];
          } else if (t > 0) {
            x += dx * t;
            y += dy * t;
          }
        }
        return squareDistance(point, [x, y]);
      };
      const keep = new Array(points.length).fill(false);
      keep[0] = true;
      keep[points.length - 1] = true;
      const stack = [[0, points.length - 1]];
      while (stack.length) {
        const [start, end] = stack.pop();
        let maxDistance = 0;
        let index = -1;
        for (let i = start + 1; i < end; i += 1) {
          const distance = segmentDistance(
            points[i],
            points[start],
            points[end],
          );
          if (distance > maxDistance) {
            maxDistance = distance;
            index = i;
          }
        }
        if (maxDistance > epsilon * epsilon && index > 0) {
          keep[index] = true;
          stack.push([start, index], [index, end]);
        }
      }
      return points.filter((_, index) => keep[index]);
    };
    const smoothLogoPoints = (points, iterations) => {
      let output = points;
      for (let iteration = 0; iteration < iterations; iteration += 1) {
        const smoothed = [];
        for (let i = 0; i < output.length; i += 1) {
          const current = output[i];
          const next = output[(i + 1) % output.length];
          smoothed.push([
            current[0] * 0.75 + next[0] * 0.25,
            current[1] * 0.75 + next[1] * 0.25,
          ]);
          smoothed.push([
            current[0] * 0.25 + next[0] * 0.75,
            current[1] * 0.25 + next[1] * 0.75,
          ]);
        }
        output = smoothed;
      }
      return output;
    };
    const traceLogoComponent = (componentMask, size) => {
      const get = (x, y) =>
        x >= 0 && y >= 0 && x < size && y < size
          ? componentMask[y * size + x]
          : 0;
      let startIndex = -1;
      for (let i = 0; i < size * size; i += 1) {
        if (componentMask[i]) {
          startIndex = i;
          break;
        }
      }
      if (startIndex < 0) return null;
      const directions = [
        [1, 0],
        [1, 1],
        [0, 1],
        [-1, 1],
        [-1, 0],
        [-1, -1],
        [0, -1],
        [1, -1],
      ];
      const startX = startIndex % size;
      const startY = (startIndex / size) | 0;
      let currentX = startX;
      let currentY = startY;
      let backtrackX = startX - 1;
      let backtrackY = startY;
      const contour = [[startX, startY]];
      let guard = 0;
      while (guard < size * size * 8) {
        guard += 1;
        let backtrackDirection = 0;
        for (let i = 0; i < 8; i += 1) {
          if (
            currentX + directions[i][0] === backtrackX &&
            currentY + directions[i][1] === backtrackY
          ) {
            backtrackDirection = i;
            break;
          }
        }
        let moved = false;
        for (let i = 1; i <= 8; i += 1) {
          const directionIndex = (backtrackDirection + i) % 8;
          const nextX = currentX + directions[directionIndex][0];
          const nextY = currentY + directions[directionIndex][1];
          if (get(nextX, nextY)) {
            backtrackX = currentX + directions[(directionIndex + 7) % 8][0];
            backtrackY = currentY + directions[(directionIndex + 7) % 8][1];
            currentX = nextX;
            currentY = nextY;
            contour.push([currentX, currentY]);
            moved = true;
            break;
          }
        }
        if (!moved || (currentX === startX && currentY === startY)) break;
      }
      return contour;
    };
    const buildLogoMonogram = (image) => {
      const size = 480;
      const logoCanvas = document.createElement("canvas");
      logoCanvas.width = size;
      logoCanvas.height = size;
      const logoCtx = logoCanvas.getContext("2d");
      logoCtx.drawImage(image, 0, 0, size, size);
      const data = logoCtx.getImageData(0, 0, size, size).data;
      const mask = new Uint8Array(size * size);
      for (let i = 0; i < size * size; i += 1) {
        const r = data[i * 4];
        const g = data[i * 4 + 1];
        const b = data[i * 4 + 2];
        const a = data[i * 4 + 3];
        mask[i] = a > 60 && Math.min(r, g, b) > 165 ? 1 : 0;
      }
      const labels = new Int32Array(size * size);
      const componentSizes = [0];
      const stack = [];
      let component = 0;
      for (let i = 0; i < size * size; i += 1) {
        if (mask[i] && !labels[i]) {
          component += 1;
          let count = 0;
          stack.length = 0;
          stack.push(i);
          labels[i] = component;
          while (stack.length) {
            const point = stack.pop();
            count += 1;
            const x = point % size;
            const y = (point / size) | 0;
            for (let dy = -1; dy <= 1; dy += 1) {
              for (let dx = -1; dx <= 1; dx += 1) {
                if (!dx && !dy) continue;
                const nextX = x + dx;
                const nextY = y + dy;
                if (nextX < 0 || nextY < 0 || nextX >= size || nextY >= size) {
                  continue;
                }
                const nextPoint = nextY * size + nextX;
                if (mask[nextPoint] && !labels[nextPoint]) {
                  labels[nextPoint] = component;
                  stack.push(nextPoint);
                }
              }
            }
          }
          componentSizes[component] = count;
        }
      }
      const geometries = [];
      const worldSize = 2.4;
      for (let current = 1; current <= component; current += 1) {
        if (componentSizes[current] < size * size * 0.004) continue;
        const componentMask = new Uint8Array(size * size);
        for (let i = 0; i < size * size; i += 1) {
          componentMask[i] = labels[i] === current ? 1 : 0;
        }
        let contour = traceLogoComponent(componentMask, size);
        if (!contour || contour.length < 12) continue;
        if (
          contour.length > 2 &&
          contour[0][0] === contour[contour.length - 1][0] &&
          contour[0][1] === contour[contour.length - 1][1]
        ) {
          contour.pop();
        }
        contour = smoothLogoPoints(simplifyLogoPoints(contour, 1.6), 3);
        if (contour.length < 8) continue;
        const shape = new THREE.Shape();
        contour.forEach((point, index) => {
          const x = (point[0] / size - 0.5) * worldSize;
          const y = (0.5 - point[1] / size) * worldSize;
          if (index === 0) shape.moveTo(x, y);
          else shape.lineTo(x, y);
        });
        const geometry = new THREE.ExtrudeGeometry(shape, {
          depth: logoDepth,
          bevelEnabled: true,
          bevelThickness: 0.045,
          bevelSize: 0.04,
          bevelSegments: 6,
          curveSegments: 1,
        });
        geometry.translate(0, 0, -logoDepth / 2);
        geometry.computeBoundingBox();
        geometries.push(geometry);
      }
      if (!geometries.length) return;
      let minX = Infinity;
      let maxX = -Infinity;
      geometries.forEach((geometry) => {
        minX = Math.min(minX, geometry.boundingBox.min.x);
        maxX = Math.max(maxX, geometry.boundingBox.max.x);
      });
      const box = new THREE.Box3();
      geometries.forEach((geometry) => {
        const positions = geometry.attributes.position;
        const colors = new Float32Array(positions.count * 3);
        for (let i = 0; i < positions.count; i += 1) {
          const value = Math.max(
            0,
            Math.min(1, (positions.getX(i) - minX) / (maxX - minX)),
          );
          const color = lerpLogoColor(value);
          colors[i * 3] = color.r;
          colors[i * 3 + 1] = color.g;
          colors[i * 3 + 2] = color.b;
        }
        geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
        logoGroup.add(new THREE.Mesh(geometry, logoMaterial));
        box.union(geometry.boundingBox);
      });
      const center = box.getCenter(new THREE.Vector3());
      logoGroup.children.forEach((mesh) => mesh.position.sub(center));
      const logoSize = box.getSize(new THREE.Vector3());
      logoGroup.scale.setScalar(2.15 / Math.max(logoSize.x, logoSize.y));
      logoBorn = frame;
    };
    const logoImage = new Image();
    logoImage.crossOrigin = "anonymous";
    logoImage.onload = () => {
      if (disposed) return;
      try {
        buildLogoMonogram(logoImage);
      } catch (error) {
        console.warn("hero logo build failed", error);
      }
    };
    logoImage.src = "assets/captixy-logo.png";

    const glowTexture = makeTexture(256, 256, (ctx, width, height) => {
      const glow = ctx.createRadialGradient(
        width / 2,
        height / 2,
        0,
        width / 2,
        height / 2,
        width / 2,
      );
      glow.addColorStop(0, accentRgba(0.88));
      glow.addColorStop(0.42, accentRgba(0.32));
      glow.addColorStop(1, accentRgba(0));
      ctx.fillStyle = glow;
      ctx.fillRect(0, 0, width, height);
    });
    const glow = new THREE.Mesh(
      new THREE.PlaneGeometry(7.4, 7.4),
      new THREE.MeshBasicMaterial({
        map: glowTexture,
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
      }),
    );
    glow.position.z = -0.9;
    root.add(glow);

    const dotTexture = makeTexture(64, 64, (ctx, width, height) => {
      const dot = ctx.createRadialGradient(
        width / 2,
        height / 2,
        0,
        width / 2,
        height / 2,
        width / 2,
      );
      dot.addColorStop(0, "rgba(255,255,255,1)");
      dot.addColorStop(1, "rgba(255,255,255,0)");
      ctx.fillStyle = dot;
      ctx.fillRect(0, 0, width, height);
    });

    const isMobile = window.innerWidth < 760;
    const makeStars = (count, minRadius, radiusSpan) => {
      const positions = new Float32Array(count * 3);
      for (let i = 0; i < count; i += 1) {
        const radius = minRadius + Math.random() * radiusSpan;
        const theta = Math.random() * Math.PI * 2;
        const phi = Math.acos(2 * Math.random() - 1);
        positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
        positions[i * 3 + 1] = radius * Math.cos(phi);
        positions[i * 3 + 2] = radius * Math.sin(phi) * Math.sin(theta);
      }
      const geometry = new THREE.BufferGeometry();
      geometry.setAttribute(
        "position",
        new THREE.BufferAttribute(positions, 3),
      );
      return geometry;
    };
    const stars = new THREE.Points(
      makeStars(isMobile ? 440 : 920, 8, 13),
      new THREE.PointsMaterial({
        size: 0.07,
        map: dotTexture,
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
        color: threeColor(primary),
        opacity: 0.5,
      }),
    );
    const starsAccent = new THREE.Points(
      makeStars(isMobile ? 70 : 140, 6, 8),
      new THREE.PointsMaterial({
        size: 0.15,
        map: dotTexture,
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
        color: threeColor(accentSoft),
        opacity: 0.68,
      }),
    );
    scene.add(stars);
    scene.add(starsAccent);

    const nebulaTexture = makeTexture(256, 256, (ctx, width, height) => {
      const nebula = ctx.createRadialGradient(
        width / 2,
        height / 2,
        0,
        width / 2,
        height / 2,
        width / 2,
      );
      nebula.addColorStop(0, "rgba(255,255,255,0.55)");
      nebula.addColorStop(0.5, "rgba(255,255,255,0.12)");
      nebula.addColorStop(1, "rgba(255,255,255,0)");
      ctx.fillStyle = nebula;
      ctx.fillRect(0, 0, width, height);
    });
    const nebulas = [
      { x: -6.5, y: 3, z: -8, size: 11, color: accent, opacity: 0.2 },
      { x: 7, y: -3.4, z: -9, size: 13, color: accentDeep, opacity: 0.18 },
      { x: 2, y: 5, z: -10.5, size: 12, color: accentSoft, opacity: 0.12 },
    ].map((item, index) => {
      const nebula = new THREE.Mesh(
        new THREE.PlaneGeometry(item.size, item.size),
        new THREE.MeshBasicMaterial({
          map: nebulaTexture,
          transparent: true,
          blending: THREE.AdditiveBlending,
          depthWrite: false,
          color: threeColor(item.color),
          opacity: 0,
        }),
      );
      nebula.position.set(item.x, item.y, item.z);
      nebula.userData = { baseOpacity: item.opacity, phase: index * 2.1 };
      scene.add(nebula);
      return nebula;
    });

    const ringPivot = new THREE.Group();
    ringPivot.rotation.x = 1.16;
    ringPivot.rotation.z = 0.18;
    root.add(ringPivot);
    const ringCount = isMobile ? 560 : 1050;
    const ringPositions = new Float32Array(ringCount * 3);
    for (let i = 0; i < ringCount; i += 1) {
      const angle = Math.random() * Math.PI * 2;
      const radius = 1.95 + Math.random() * 0.95;
      ringPositions[i * 3] = Math.cos(angle) * radius;
      ringPositions[i * 3 + 1] = (Math.random() * 2 - 1) * 0.06;
      ringPositions[i * 3 + 2] = Math.sin(angle) * radius;
    }
    const ringGeometry = new THREE.BufferGeometry();
    ringGeometry.setAttribute(
      "position",
      new THREE.BufferAttribute(ringPositions, 3),
    );
    const ringPoints = new THREE.Points(
      ringGeometry,
      new THREE.PointsMaterial({
        size: 0.05,
        map: dotTexture,
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
        color: threeColor(accentSoft),
        opacity: 0.5,
      }),
    );
    ringPivot.add(ringPoints);

    const chip = (ctx, width, height) => {
      ctx.clearRect(0, 0, width, height);
      ctx.save();
      ctx.shadowColor = "rgba(0,0,0,.5)";
      ctx.shadowBlur = 18;
      ctx.shadowOffsetY = 8;
      roundRect(ctx, 16, 16, width - 32, height - 32, 26);
      ctx.fillStyle = bgSoft;
      ctx.fill();
      ctx.restore();
      roundRect(ctx, 16, 16, width - 32, height - 32, 26);
      ctx.lineWidth = 3;
      ctx.strokeStyle = accentRgba(0.5);
      ctx.stroke();
    };
    const iconCode = (ctx, width, height) => {
      chip(ctx, width, height);
      ctx.fillStyle = accentSoft;
      ctx.font = '700 50px "Almarai", sans-serif';
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      ctx.fillText("</>", width / 2, height / 2 + 2);
    };
    const iconBraces = (ctx, width, height) => {
      chip(ctx, width, height);
      ctx.fillStyle = primary;
      ctx.font = '700 56px "Almarai", sans-serif';
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      ctx.fillText("{ }", width / 2, height / 2 + 2);
    };
    const iconCursor = (ctx, width, height) => {
      chip(ctx, width, height);
      ctx.save();
      ctx.translate(width / 2 - 14, height / 2 - 22);
      ctx.beginPath();
      ctx.moveTo(0, 0);
      ctx.lineTo(0, 46);
      ctx.lineTo(12, 35);
      ctx.lineTo(21, 53);
      ctx.lineTo(29, 49);
      ctx.lineTo(20, 32);
      ctx.lineTo(36, 32);
      ctx.closePath();
      ctx.fillStyle = primary;
      ctx.strokeStyle = bg;
      ctx.lineWidth = 4;
      ctx.lineJoin = "round";
      ctx.stroke();
      ctx.fill();
      ctx.restore();
    };
    const iconSwatch = (ctx, width, height) => {
      chip(ctx, width, height);
      [accentSoft, accent, accentDeep].forEach((color, index) => {
        ctx.beginPath();
        ctx.arc(width / 2 - 30 + index * 30, height / 2, 13, 0, Math.PI * 2);
        ctx.fillStyle = color;
        ctx.fill();
      });
    };
    const iconLayout = (ctx, width, height) => {
      chip(ctx, width, height);
      ctx.strokeStyle = "rgba(255,255,255,.85)";
      ctx.lineWidth = 4;
      ctx.strokeRect(34, 34, width - 68, height - 68);
      ctx.fillStyle = accent;
      ctx.fillRect(34, 34, width - 68, 14);
      ctx.fillStyle = "rgba(255,255,255,.5)";
      ctx.fillRect(40, 62, 26, height - 88);
      ctx.fillRect(74, 62, width - 114, height - 88);
    };
    const iconPen = (ctx, width, height) => {
      chip(ctx, width, height);
      const centerX = width / 2;
      const centerY = height / 2;
      ctx.strokeStyle = "rgba(255,255,255,.5)";
      ctx.lineWidth = 3;
      ctx.beginPath();
      ctx.moveTo(centerX - 32, centerY + 16);
      ctx.lineTo(centerX + 32, centerY - 16);
      ctx.stroke();
      ctx.fillStyle = accent;
      ctx.beginPath();
      ctx.moveTo(centerX, centerY - 15);
      ctx.lineTo(centerX + 13, centerY);
      ctx.lineTo(centerX, centerY + 15);
      ctx.lineTo(centerX - 13, centerY);
      ctx.closePath();
      ctx.fill();
    };

    const satellites = [];
    const addSatellite = (draw, options) => {
      const texture = makeTexture(128, 128, draw);
      const satellite = new THREE.Mesh(
        new THREE.PlaneGeometry(options.size, options.size),
        new THREE.MeshBasicMaterial({
          map: texture,
          transparent: true,
          depthWrite: false,
          opacity: 0,
        }),
      );
      options.euler = new THREE.Euler(options.tiltX, 0, options.tiltZ);
      satellite.userData = options;
      root.add(satellite);
      satellites.push(satellite);

      const orbitPoints = [];
      for (let i = 0; i <= 120; i += 1) {
        const angle = (i / 120) * Math.PI * 2;
        orbitPoints.push(
          new THREE.Vector3(
            Math.cos(angle) * options.radius,
            0,
            Math.sin(angle) * options.radius,
          ).applyEuler(options.euler),
        );
      }
      const line = new THREE.Line(
        new THREE.BufferGeometry().setFromPoints(orbitPoints),
        new THREE.LineBasicMaterial({
          color: threeColor(accent),
          transparent: true,
          opacity: 0,
        }),
      );
      line.userData = { baseOpacity: options.lineOpacity };
      root.add(line);
      options.line = line;
    };
    [
      [iconCode, 3.0, 1.2, 0.15, 0, 0.34, 0.5, 0.06, 0, 0.18],
      [iconSwatch, 3.55, 0.5, -0.5, 2.0, 0.27, 0.46, 0.05, 1, 0.13],
      [iconCursor, 2.55, 1.45, 0.5, 4.1, 0.42, 0.42, 0.07, 2, 0.16],
      [iconLayout, 4.0, 0.9, 0.3, 3.0, 0.22, 0.52, 0.05, 3, 0.11],
      [iconBraces, 3.25, 1.0, -0.25, 5.2, 0.3, 0.46, 0.06, 4, 0.14],
      [iconPen, 4.45, 0.35, 0.45, 1.0, 0.19, 0.48, 0.05, 5, 0.1],
    ].forEach(
      ([
        draw,
        radius,
        tiltX,
        tiltZ,
        angle,
        speed,
        size,
        bob,
        index,
        lineOpacity,
      ]) =>
        addSatellite(draw, {
          radius,
          tiltX,
          tiltZ,
          angle,
          speed,
          size,
          bob,
          phase: index,
          index,
          maxOpacity: index === 5 ? 0.82 : 0.92,
          lineOpacity,
        }),
    );

    function resize() {
      const rect = wrap.getBoundingClientRect();
      const width = Math.max(1, Math.floor(rect.width));
      const height = Math.max(1, Math.floor(rect.height));
      renderer.setSize(width, height, false);
      camera.aspect = width / height;
      camera.updateProjectionMatrix();
      root.position.x = width < 760 ? 0 : width / height > 1.25 ? 0.28 : 0.18;
    }

    let frame = 0;
    let raf = 0;
    const pointer = { x: 0, y: 0 };
    const easedPointer = { x: 0, y: 0 };
    const cameraWorld = new THREE.Vector3();
    const satellitePosition = new THREE.Vector3();
    const clamp = (value) => Math.max(0, Math.min(1, value));
    const easeOut = (value) => 1 - Math.pow(1 - value, 3);
    const easeBack = (value) => {
      const c1 = 1.70158;
      const c3 = c1 + 1;
      return 1 + c3 * Math.pow(value - 1, 3) + c1 * Math.pow(value - 1, 2);
    };
    const onPointerMove = (event) => {
      pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
      pointer.y = -((event.clientY / window.innerHeight) * 2 - 1);
    };
    window.addEventListener("pointermove", onPointerMove, { passive: true });

    function animate(time) {
      frame = time * 0.001;
      const intro = clamp(frame / 1.4);
      world.scale.setScalar(
        (0.2 + 0.8 * easeBack(intro)) * (isMobile ? 0.66 : 0.92),
      );
      root.rotation.y = Math.sin(frame * 0.28) * 0.1 - 0.06;
      root.rotation.x = Math.sin(frame * 0.22) * 0.05;
      logoGroup.rotation.y = frame * 0.34 + (1 - easeOut(intro)) * Math.PI;
      logoGroup.rotation.x = Math.sin(frame * 0.4) * 0.08;
      logoGroup.position.y = Math.sin(frame * 0.55) * 0.07;
      logoMaterial.opacity =
        logoBorn == null ? 0 : clamp((frame - logoBorn) / 0.8);
      glow.scale.setScalar(1 + Math.sin(frame * 1.3) * 0.06);
      glow.material.opacity = 0.42 + Math.sin(frame * 1.3) * 0.1;
      glow.lookAt(camera.position);
      ringPoints.rotation.y = frame * 0.06;
      stars.rotation.y = frame * 0.012;
      starsAccent.rotation.y = -frame * 0.008;

      camera.getWorldPosition(cameraWorld);
      satellites.forEach((satellite) => {
        const options = satellite.userData;
        const reveal = clamp((frame - 0.45 - options.index * 0.1) / 0.7);
        const angle = options.angle + frame * options.speed;
        satellitePosition
          .set(
            Math.cos(angle) * options.radius,
            0,
            Math.sin(angle) * options.radius,
          )
          .applyEuler(options.euler);
        satellitePosition.y +=
          Math.sin(frame * 0.8 + options.phase) * options.bob;
        satellite.position.copy(satellitePosition);
        satellite.lookAt(cameraWorld);
        satellite.scale.setScalar(0.0001 + easeOut(reveal));
        satellite.material.opacity = reveal * options.maxOpacity;
        if (options.line) {
          options.line.material.opacity =
            easeOut(reveal) * options.line.userData.baseOpacity;
        }
      });

      nebulas.forEach((nebula) => {
        nebula.material.opacity =
          nebula.userData.baseOpacity *
          (0.7 + 0.3 * Math.sin(frame * 0.4 + nebula.userData.phase));
      });
      easedPointer.x += (pointer.x - easedPointer.x) * 0.05;
      easedPointer.y += (pointer.y - easedPointer.y) * 0.05;
      camera.position.x = easedPointer.x * 0.7;
      camera.position.y = easedPointer.y * 0.48;
      camera.position.z = 8.6 + (1 - easeOut(intro)) * 1.4;
      camera.lookAt(camera.position.x, 0, 0);
      glint.position.set(
        Math.cos(frame * 0.45) * 4.8,
        Math.sin(frame * 0.45) * 3.2 + 0.6,
        5.6,
      );
      renderer.render(scene, camera);
      raf = window.requestAnimationFrame(animate);
    }

    resize();
    window.addEventListener("resize", resize);
    raf = window.requestAnimationFrame(animate);

    return () => {
      disposed = true;
      window.removeEventListener("resize", resize);
      window.removeEventListener("pointermove", onPointerMove);
      window.cancelAnimationFrame(raf);
      scene.traverse((object) => {
        if (object.geometry) object.geometry.dispose();
        if (object.material) {
          if (Array.isArray(object.material)) {
            object.material.forEach((material) => material.dispose());
          } else {
            object.material.dispose();
          }
        }
      });
      textureDisposables.forEach((texture) => texture.dispose());
      renderer.dispose();
    };
  }, [ready]);

  if (!ready) {
    return (
      <div
        className="pointer-events-none absolute right-[9%] top-1/2 z-[1] aspect-square w-[clamp(180px,22vw,300px)] -translate-y-1/2 rounded-3xl bg-cover bg-center"
        style={{
          backgroundImage: "url('assets/captixy-logo.png')",
          boxShadow: `0 40px 130px -20px ${accent(0.65)}`,
          animation: "captixyFloat 8s ease-in-out infinite",
        }}
        aria-hidden="true"
      />
    );
  }

  return (
    <div
      ref={wrapRef}
      className="pointer-events-none absolute inset-0 z-[1] overflow-hidden"
      aria-hidden="true"
    >
      <canvas ref={canvasRef} className="absolute inset-0 h-full w-full" />
    </div>
  );
}

function StatusVisual() {
  const rows = ["Briefing", "Design", "Construção", "Publicado"];
  return (
    <BrowserShell url="estado-do-projecto">
      <div className="p-6">
        <div className="space-y-4">
          {rows.map((row, index) => {
            const done = index < 2;
            const active = index === 2;
            return (
              <div key={row} className="flex items-center gap-4">
                <div
                  className="flex h-7 w-7 items-center justify-center rounded-full text-sm font-bold"
                  style={{
                    background: done
                      ? "var(--orange)"
                      : active
                        ? accent(0.18)
                        : "transparent",
                    border: active
                      ? `2px solid var(--orange)`
                      : done
                        ? "none"
                        : "2px solid #334155",
                    color: done ? "#0F172A" : "var(--orange)",
                  }}
                >
                  {done ? "✓" : ""}
                </div>
                <div className="flex-1">
                  <div
                    className="h-2 rounded-full"
                    style={{
                      width: `${50 + index * 10}%`,
                      background:
                        done || active
                          ? "rgba(241,245,249,.18)"
                          : "rgba(241,245,249,.08)",
                    }}
                  />
                </div>
                <span
                  className="text-xs"
                  style={{ color: active ? "var(--orange)" : "#64748B" }}
                >
                  {row}
                </span>
              </div>
            );
          })}
        </div>
        <div
          className="mt-7 flex items-center justify-between rounded-xl p-4"
          style={{
            background: accent(0.08),
            border: `1px solid ${accent(0.2)}`,
          }}
        >
          <span className="text-sm text-primary/85">Entrega estimada</span>
          <span className="serif text-2xl" style={{ color: "var(--orange)" }}>
            Sexta-feira
          </span>
        </div>
      </div>
    </BrowserShell>
  );
}

function StudioLogoMark() {
  return (
    <svg viewBox="0 0 64 64" className="h-10 w-10" aria-hidden="true">
      <defs>
        <linearGradient id="studioLogoGradient" x1="8" y1="8" x2="56" y2="58">
          <stop offset="0%" stopColor="#111827" />
          <stop offset="52%" stopColor="#9A6B4F" />
          <stop offset="100%" stopColor="#FF6B73" />
        </linearGradient>
      </defs>
      <rect width="64" height="64" rx="22" fill="url(#studioLogoGradient)" />
      <path
        d="M18 39.5c9.5-1.2 17-8.4 18.8-18.6 5.4 5.8 7.1 14.4 4.2 21.7-6.9 3-15.8 1.8-23-3.1Z"
        fill="#FFF7EF"
        opacity=".95"
      />
      <path
        d="M23 22.5c3.6 1.7 6.2 4.6 7.5 8.4-4.2.5-8.2-.7-11.5-3.4 1.1-2.1 2.4-3.7 4-5Z"
        fill="#FFD8C7"
      />
      <circle cx="43" cy="20" r="3.8" fill="#FFF7EF" opacity=".9" />
    </svg>
  );
}

function StudioServiceIcon({ type }) {
  const common = {
    fill: "none",
    stroke: "#9A6B4F",
    strokeWidth: "2.8",
    strokeLinecap: "round",
    strokeLinejoin: "round",
  };

  return (
    <svg viewBox="0 0 48 48" className="h-9 w-9" aria-hidden="true">
      <rect width="48" height="48" rx="16" fill="#F4EEE8" />
      {type === "facial" && (
        <>
          <path
            {...common}
            d="M16 23c0-6 3.8-10 8-10s8 4 8 10c0 7-3.5 12-8 12s-8-5-8-12Z"
          />
          <path {...common} d="M20 24h.1M28 24h.1M21 30c2 1.5 4 1.5 6 0" />
          <path {...common} d="M34 15l3-3M36 20h4" />
        </>
      )}
      {type === "body" && (
        <>
          <path {...common} d="M17 32c5-8 9-14 14-18" />
          <path {...common} d="M14 24c7 0 14 3 20 9" />
          <path {...common} d="M16 15c5 2 9 5 12 10" />
          <circle cx="32" cy="15" r="2.5" fill="#FF6B73" />
        </>
      )}
      {type === "bridal" && (
        <>
          <path {...common} d="M15 34h18l-4-17H19l-4 17Z" />
          <path {...common} d="M19 17c1.5-3 3.2-4.5 5-4.5s3.5 1.5 5 4.5" />
          <path {...common} d="M20 25h8M19 30h10" />
          <circle cx="34" cy="13" r="2.8" fill="#FF6B73" />
        </>
      )}
    </svg>
  );
}

function StudioVisual() {
  return (
    <div className="relative mx-auto w-full max-w-xl py-8">
      <div
        className="absolute left-2 right-10 top-2 h-[82%] rotate-[-5deg] rounded-[1.5rem] bg-white"
        style={{
          border: "1px solid rgba(15,23,42,.08)",
          boxShadow: "0 34px 90px -50px rgba(15,23,42,.55)",
        }}
      >
        <div className="h-20 rounded-t-[1.5rem] bg-[#101827]" />
        <div className="grid gap-3 p-5">
          <div className="h-3 w-2/5 rounded-full bg-slate-200" />
          <div className="h-3 w-4/5 rounded-full bg-slate-100" />
          <div className="grid grid-cols-3 gap-2 pt-3">
            <span className="h-16 rounded-xl bg-[#EDE7DF]" />
            <span className="h-16 rounded-xl bg-[#F7D8CC]" />
            <span className="h-16 rounded-xl bg-slate-100" />
          </div>
        </div>
      </div>
      <div
        className="absolute bottom-2 left-10 right-0 h-[76%] rotate-[4deg] rounded-[1.5rem] bg-white"
        style={{
          border: "1px solid rgba(15,23,42,.08)",
          boxShadow: "0 34px 90px -52px rgba(15,23,42,.5)",
        }}
      >
        <div className="grid h-full grid-cols-[.82fr_1fr] overflow-hidden rounded-[1.5rem]">
          <div className="bg-[#F4EEE8] p-5">
            <div className="mb-4 h-8 w-8 rounded-full bg-[#9A6B4F]" />
            <div className="h-3 w-4/5 rounded-full bg-white/70" />
            <div className="mt-2 h-3 w-3/5 rounded-full bg-white/60" />
          </div>
          <div className="bg-[#1E293B] p-5">
            <div className="ml-auto h-8 w-24 rounded-full bg-white/10" />
            <div className="mt-20 h-3 w-3/4 rounded-full bg-white/20" />
            <div className="mt-2 h-3 w-1/2 rounded-full bg-white/10" />
          </div>
        </div>
      </div>

      <div className="relative z-10">
        <BrowserShell light url="studio-lumina.pt">
          <div className="bg-[#FBFAF7] p-4 text-[#171717]">
            <div className="overflow-hidden rounded-[1.1rem] bg-white shadow-sm">
              <div className="flex items-center justify-between border-b border-black/[0.06] px-5 py-4">
                <div className="flex items-center gap-2">
                  <StudioLogoMark />
                  <div>
                    <div className="serif text-lg leading-none">
                      Studio Lumina
                    </div>
                    <div className="text-[9px] uppercase tracking-[0.22em] text-slate-400">
                      Beauty & wellness
                    </div>
                  </div>
                </div>
                <div className="hidden items-center gap-4 text-[10px] font-semibold text-slate-500 sm:flex">
                  <span>Serviços</span>
                  <span>Galeria</span>
                  <span>Contactos</span>
                </div>
              </div>

              <div className="grid min-h-[260px] grid-cols-1 lg:grid-cols-[1.05fr_.95fr]">
                <div className="flex flex-col justify-between p-6">
                  <div>
                    <div className="mb-4 inline-flex rounded-full bg-[#F4EEE8] px-3 py-1 text-[10px] font-bold uppercase tracking-[0.18em] text-[#9A6B4F]">
                      Agenda aberta
                    </div>
                    <h3 className="serif max-w-[10ch] text-5xl leading-[0.92] tracking-[-0.02em] text-[#151515]">
                      Beleza com tempo, detalhe e cuidado.
                    </h3>
                    <p className="mt-4 max-w-sm text-sm leading-6 text-slate-500">
                      Tratamentos personalizados num espaço calmo, elegante e
                      pensado para te receber sem pressas.
                    </p>
                  </div>
                  <div className="mt-6 flex items-center gap-3">
                    <span
                      className="rounded-full px-4 py-2 text-xs font-bold text-white"
                      style={{ background: "var(--orange)" }}
                    >
                      Marcar visita
                    </span>
                    <span className="text-xs font-semibold text-slate-500">
                      Ver tratamentos
                    </span>
                  </div>
                </div>

                <div className="relative min-h-[260px] overflow-hidden">
                  <div
                    className="absolute inset-0"
                    style={{
                      backgroundImage:
                        "url('https://images.unsplash.com/photo-1522337360788-8b13dee7a37e?auto=format&fit=crop&w=900&q=80')",
                      backgroundPosition: "center",
                      backgroundSize: "cover",
                    }}
                  />
                  <div className="absolute bottom-4 left-4 right-4 overflow-hidden rounded-2xl bg-white/[0.92] shadow-xl backdrop-blur">
                    <div
                      className="h-20 w-full"
                      style={{
                        backgroundImage:
                          "linear-gradient(to top, rgba(15,23,42,.12), rgba(15,23,42,0)), url('https://images.unsplash.com/photo-1515377905703-c4788e51af15?auto=format&fit=crop&w=520&q=80')",
                        backgroundPosition: "center",
                        backgroundSize: "cover",
                      }}
                    />
                    <div className="flex items-center justify-between gap-3 p-4">
                      <div className="min-w-0 flex-1">
                        <div className="text-xs font-bold text-[#151515]">
                          Tratamento assinatura
                        </div>
                        <div className="mt-1 text-[11px] text-slate-500">
                          Consulta + plano personalizado
                        </div>
                      </div>
                      <div className="serif shrink-0 text-2xl text-[#151515]">
                        4.9
                      </div>
                    </div>
                  </div>
                </div>
              </div>

              <div className="grid gap-3 bg-[#FBFAF7] p-4 sm:grid-cols-3">
                {[
                  ["Facial", "Limpeza e hidratação", "facial"],
                  ["Corpo", "Massagens e cuidado", "body"],
                  ["Noivas", "Preparação completa", "bridal"],
                ].map(([title, body, icon]) => (
                  <div
                    key={title}
                    className="rounded-2xl border border-black/[0.06] bg-white p-4"
                  >
                    <div className="mb-4">
                      <StudioServiceIcon type={icon} />
                    </div>
                    <div className="text-sm font-bold text-[#151515]">
                      {title}
                    </div>
                    <div className="mt-1 text-[11px] leading-5 text-slate-500">
                      {body}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          </div>
        </BrowserShell>
      </div>
    </div>
  );
}

function BlocksVisual() {
  const essentials = [
    ["Negócio", "Studio Lumina", true],
    ["Contactos", "WhatsApp + email", true],
    ["Serviços", "Facial, corpo, noivas", true],
    ["Domínio", "studio-lumina.pt", false],
  ];

  return (
    <div className="flex items-start justify-center gap-5 text-left">
      <div
        className="w-full max-w-[390px] overflow-hidden rounded-[1.25rem] bg-[#0F172A]"
        style={{
          border: "1px solid rgba(255,255,255,.10)",
          boxShadow: "0 50px 120px -34px rgba(0,0,0,.8)",
        }}
      >
        <div className="flex items-center justify-between border-b border-white/[0.08] px-5 py-4">
          <div>
            <div className="text-[10px] font-bold uppercase tracking-[0.22em] text-slate-500">
              Pedido guiado
            </div>
            <div className="mt-1 text-sm font-bold text-primary">
              Dados essenciais
            </div>
          </div>
          <span
            className="rounded-full px-3 py-1 text-xs font-bold text-[#0F172A]"
            style={{ background: "var(--orange)" }}
          >
            4 min
          </span>
        </div>
        <div className="space-y-3 p-5">
          {essentials.map(([label, value, done]) => (
            <div
              key={label}
              className="flex items-center gap-3 rounded-xl p-3"
              style={{
                background: done ? accent(0.1) : "rgba(255,255,255,.05)",
                border: done
                  ? `1px solid ${accent(0.24)}`
                  : "1px dashed rgba(255,255,255,.16)",
              }}
            >
              <span
                className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg text-sm font-bold"
                style={{
                  background: done ? accent(0.22) : "rgba(255,255,255,.08)",
                  color: done ? "var(--orange)" : "#94A3B8",
                }}
              >
                {done ? "✓" : "+"}
              </span>
              <div className="min-w-0 flex-1">
                <div className="text-[10px] font-bold uppercase tracking-[0.16em] text-slate-500">
                  {label}
                </div>
                <div className="truncate text-sm font-semibold text-primary">
                  {value}
                </div>
              </div>
            </div>
          ))}
          <div className="rounded-xl bg-white/[0.05] p-4">
            <div className="mb-3 flex items-center justify-between">
              <span className="text-[10px] font-bold uppercase tracking-[0.18em] text-slate-500">
                Próximo passo
              </span>
              <IconSparkle
                className="h-4 w-4"
                style={{ color: "var(--orange)" }}
              />
            </div>
            <p className="text-sm leading-6 text-primary/70">
              A equipa Captixy confirma os detalhes contigo e transforma este
              briefing num website final.
            </p>
          </div>
        </div>
      </div>
      <div
        className="mt-10 hidden w-32 rounded-[1.35rem] bg-[#0F172A] p-3 sm:block"
        style={{
          border: "1px solid rgba(255,255,255,.12)",
          boxShadow: "0 40px 90px -26px rgba(0,0,0,.85)",
        }}
      >
        <div className="mx-auto mb-3 h-1.5 w-8 rounded-full bg-white/[0.15]" />
        <div
          className="mb-3 h-14 rounded-xl bg-cover bg-center"
          style={{
            backgroundImage:
              "url('https://images.unsplash.com/photo-1522337660859-02fbefca4702?auto=format&fit=crop&w=260&q=80')",
          }}
        />
        <div className="mb-2 h-1.5 w-3/4 rounded-full bg-white/[0.15]" />
        <div className="h-1.5 w-1/2 rounded-full bg-white/10" />
      </div>
    </div>
  );
}

function AnalyticsVisual() {
  return (
    <div
      className="rounded-[1.25rem] bg-white p-7"
      style={{
        border: "1px solid rgba(15,23,42,.08)",
        boxShadow: "0 50px 120px -38px rgba(15,23,42,.45)",
      }}
    >
      <div className="mb-7 flex items-start justify-between gap-4">
        <div>
          <div className="text-xs uppercase tracking-widest text-slate-400">
            Visitantes este mês
          </div>
          <div className="serif text-5xl font-light tracking-tight text-slate-900">
            4.820
          </div>
        </div>
        <div className="rounded-full bg-emerald-100 px-3 py-1 text-xs font-bold text-emerald-700">
          ↑ 38%
        </div>
      </div>
      <div className="flex h-36 items-end gap-2.5">
        {[38, 52, 45, 66, 58, 78, 100].map((h, i) => (
          <div
            key={i}
            className="flex-1 rounded-t-md"
            style={{
              height: `${h}%`,
              background:
                i > 4
                  ? "linear-gradient(180deg, var(--orange-soft), var(--orange))"
                  : "rgba(15,23,42,.12)",
            }}
          />
        ))}
      </div>
      <div className="mt-5 grid grid-cols-3 gap-3 border-t border-slate-100 pt-5 text-sm">
        <div>
          <div className="text-xs text-slate-400">Conversão</div>
          <div className="font-bold text-slate-900">6,4%</div>
        </div>
        <div>
          <div className="text-xs text-slate-400">Carga média</div>
          <div className="font-bold text-slate-900">0,8s</div>
        </div>
        <div>
          <div className="text-xs text-slate-400">Google</div>
          <div className="font-bold text-emerald-600">98/100</div>
        </div>
      </div>
    </div>
  );
}

function ChatVisual() {
  return (
    <div
      className="rounded-[1.25rem] bg-[#0F172A] p-6"
      style={{
        border: "1px solid rgba(255,255,255,.10)",
        boxShadow: "0 50px 120px -34px rgba(0,0,0,.8)",
      }}
    >
      <div className="mb-5 flex items-center gap-3 border-b border-white/10 pb-5">
        <div
          className="h-10 w-10 rounded-full"
          style={{
            background:
              "linear-gradient(135deg, var(--orange), var(--orange-soft))",
          }}
        />
        <div>
          <div className="text-sm font-bold text-primary">Suporte Captixy</div>
          <div className="flex items-center gap-2 text-xs text-emerald-400">
            <span className="h-1.5 w-1.5 rounded-full bg-emerald-400" />{" "}
            Resposta rápida
          </div>
        </div>
      </div>
      <div className="space-y-3 text-sm leading-relaxed">
        <div className="max-w-[82%] rounded-2xl rounded-bl-md bg-white/[0.07] px-4 py-3 text-slate-300">
          Podes trocar a fotografia da homepage e actualizar o horário?
        </div>
        <div
          className="ml-auto max-w-[82%] rounded-2xl rounded-br-md px-4 py-3 font-semibold text-[#0F172A]"
          style={{ background: "var(--orange)" }}
        >
          Feito. Já está tudo online.
        </div>
        <div className="inline-flex gap-1.5 rounded-2xl bg-white/[0.07] px-4 py-3">
          {[0, 1, 2].map((i) => (
            <span
              key={i}
              className="captixy-blink h-2 w-2 rounded-full bg-slate-500"
              style={{ animationDelay: `${i * 0.2}s` }}
            />
          ))}
        </div>
      </div>
    </div>
  );
}

function WebsitesHero() {
  return (
    <>
      <NBw items={window.NAV_ITEMS_HOME} />

      <section
        id="topo"
        data-chapter
        className="relative flex min-h-screen flex-col justify-center overflow-hidden px-[clamp(24px,4vw,56px)] pb-[clamp(48px,7vh,90px)] pt-[clamp(90px,12vh,140px)] text-[#F8FAFC]"
        style={{
          background:
            "radial-gradient(120% 90% at 80% 10%, #142036 0%, #0B1120 55%)",
          fontFamily: "'Space Grotesk', -apple-system, sans-serif",
        }}
      >
        <div
          className="pointer-events-none absolute inset-0"
          style={{
            backgroundImage:
              "linear-gradient(rgba(255,255,255,.022) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.022) 1px,transparent 1px)",
            backgroundSize: "60px 60px",
            animation: "captixyGridDrift 16s linear infinite",
          }}
        />
        <div
          className="pointer-events-none absolute right-[-5%] top-[-10%] h-[46vw] max-h-[680px] w-[46vw] max-w-[680px] rounded-full"
          style={{
            background: `radial-gradient(circle, ${accent(0.16)} 0%, transparent 68%)`,
          }}
        />
        <HeroWebsiteModel />
        <div
          className="pointer-events-none absolute inset-0 z-[2]"
          style={{
            background:
              "linear-gradient(90deg,#0B1120 0%, rgba(11,17,32,.92) 24%, rgba(11,17,32,.55) 46%, rgba(11,17,32,0) 66%)",
          }}
        />

        <div className="relative z-[3] ml-0 max-w-[1080px] md:ml-[clamp(24px,5vw,76px)]">
          <mw.div
            initial={{ opacity: 0, y: 28 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.75, ease: EEw }}
            className="mb-[30px] flex items-center gap-4"
          >
            <span className="inline-block h-px w-8 bg-[var(--orange)]" />
            <RotatingWords />
          </mw.div>
          <mw.h1
            initial={{ opacity: 0, y: 30 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.85, delay: 0.08, ease: EEw }}
            className="m-0 max-w-[14ch] font-['Newsreader'] text-[clamp(48px,8.2vw,128px)] font-light leading-[0.96] tracking-[-0.025em]"
          >
            Criamos os sites que{" "}
            <em className="font-normal italic text-[var(--orange)]">
              fazem crescer o teu negócio
            </em>
          </mw.h1>
          <mw.p
            initial={{ opacity: 0, y: 26 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.85, delay: 0.18, ease: EEw }}
            className="mt-[34px] max-w-[560px] text-[clamp(16px,1.4vw,21px)] leading-[1.6] text-[#94A3B8]"
          >
            Sites profissionais para pequenas empresas e profissionais
            independentes. Desenhados, criados e online em dias — não meses.
          </mw.p>
          <mw.div
            initial={{ opacity: 0, y: 24 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.85, delay: 0.28, ease: EEw }}
            className="mt-10 flex flex-wrap gap-4"
          >
            <a
              href="https://app.captixy.com/login?product=websites"
              className="inline-flex cursor-pointer items-center gap-3 rounded-full border-0 bg-[var(--orange)] px-7 py-[17px] text-sm font-semibold tracking-[0.03em] text-[#0B1120] transition-all hover:-translate-y-1"
              style={{ boxShadow: `0 20px 44px -14px ${accent(0.65)}` }}
            >
              Obter o meu website
              <IconArrowRight className="h-4 w-4" />
            </a>
            <button
              type="button"
              onClick={() => scrollToId("velocidade")}
              className="inline-flex cursor-pointer items-center gap-3 rounded-full border border-white/20 bg-white/[0.07] px-7 py-[17px] text-sm font-medium tracking-[0.03em] text-[#F8FAFC] transition-all hover:-translate-y-1 hover:bg-white/[0.12]"
            >
              Ver o que fazemos
              <span className="inline-block text-base">↓</span>
            </button>
            <button
              type="button"
              onClick={() => scrollToId("precos")}
              className="inline-flex cursor-pointer items-center gap-3 rounded-full border border-white/20 bg-transparent px-7 py-[17px] text-sm font-medium tracking-[0.03em] text-[#F8FAFC] transition-all hover:-translate-y-1 hover:bg-white/[0.07]"
            >
              Ver pacotes →
            </button>
          </mw.div>
        </div>

        <mw.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ duration: 0.85, delay: 0.55, ease: EEw }}
          className="pointer-events-none absolute bottom-[clamp(28px,5vh,52px)] left-1/2 z-[3] hidden -translate-x-1/2 flex-col items-center gap-2.5 md:flex"
        >
          <span className="text-[10px] uppercase tracking-[0.3em] text-[#475569]">
            Deslizar
          </span>
          <div className="relative h-[42px] w-px overflow-hidden bg-white/10">
            <div className="captixy-scroll-dot absolute left-0 top-0 h-3.5 w-px bg-[var(--orange)]" />
          </div>
        </mw.div>
      </section>
    </>
  );
}

function RotatingWords() {
  const words = [
    "Velocidade",
    "Design",
    "Clareza",
    "Crescimento",
    "Suporte",
    "Lançamento",
  ];
  const [index, setIndex] = uSw(0);
  uEw(() => {
    const timer = setInterval(
      () => setIndex((value) => (value + 1) % words.length),
      2200,
    );
    return () => clearInterval(timer);
  }, []);
  return (
    <div className="relative h-5 min-w-[160px] overflow-hidden">
      <APw mode="wait">
        <mw.span
          key={words[index]}
          initial={{ y: "105%", opacity: 0 }}
          animate={{ y: 0, opacity: 1 }}
          exit={{ y: "-105%", opacity: 0 }}
          transition={{ duration: 0.45, ease: EEw }}
          className="absolute left-0 top-0 text-xs font-bold uppercase tracking-[0.34em]"
          style={{ color: "var(--orange)" }}
        >
          {words[index]}
        </mw.span>
      </APw>
    </div>
  );
}

function ChapterSection({ chapter }) {
  const light = chapter.tone === "light";
  return (
    <section
      id={chapter.id}
      data-chapter
      className={`relative min-h-screen overflow-hidden px-5 py-24 md:px-8 lg:px-14 ${light ? "bg-[#F4F6FF] text-[#0F172A]" : "bg-[#0F172A] text-primary"}`}
    >
      {!light && (
        <div
          className="absolute -left-[12%] top-1/4 h-[38vw] max-h-[560px] w-[38vw] max-w-[560px] rounded-full"
          style={{
            background: `radial-gradient(circle, ${accent(0.1)}, transparent 68%)`,
          }}
        />
      )}
      <div className="relative mx-auto grid w-full max-w-7xl grid-cols-1 items-center justify-center gap-12 text-center lg:min-h-[calc(100svh-12rem)] lg:grid-cols-2 lg:gap-20 lg:text-left">
        <mw.div
          initial={{ opacity: 0, y: 32 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true, amount: 0.25 }}
          transition={{ duration: 0.8, ease: EEw }}
          className={`${light ? "lg:order-2" : ""} relative mx-auto w-full max-w-xl lg:mx-0`}
        >
          <ChapterEyebrow
            index={chapter.index}
            label={chapter.label}
            centered
          />
          <h2
            className={`serif mx-auto mt-7 max-w-[12ch] text-[clamp(42px,6.5vw,104px)] font-light leading-[0.99] tracking-[-0.025em] lg:mx-0 ${light ? "text-[#0F172A]" : "text-primary"}`}
          >
            {chapter.title}
          </h2>
          <p
            className={`mx-auto mt-8 max-w-md text-base leading-8 md:text-lg lg:mx-0 ${light ? "text-slate-600" : "text-primary/60"}`}
          >
            {chapter.body}
          </p>
          {chapter.cta && (
            <button
              onClick={() => scrollToId("precos")}
              className={`mt-8 inline-flex items-center gap-2 rounded-full border px-6 py-3.5 text-sm font-semibold transition-all hover:-translate-y-1 ${light ? "border-slate-300 text-slate-900 hover:bg-white" : "border-white/20 text-primary hover:bg-white/[0.07]"}`}
            >
              {chapter.cta} <IconArrowRight className="h-4 w-4" />
            </button>
          )}
        </mw.div>
        <mw.div
          initial={{ opacity: 0, y: 44 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true, amount: 0.25 }}
          transition={{ duration: 0.85, delay: 0.1, ease: EEw }}
          className={`${light ? "lg:order-1" : ""} relative mx-auto w-full max-w-xl`}
        >
          <ChapterVisual type={chapter.visual} light={light} />
        </mw.div>
      </div>
    </section>
  );
}

const WEBSITE_PRICE_PLANS = [
  {
    name: "Landing Page Profissional",
    price: "350€",
    note: "pagamento único",
    tagline: "Uma página profissional para apresentares o teu negócio.",
    features: [
      "1 página, até 5 secções",
      "Design mobile-first",
      "Texto base incluído",
      "Formulário ou botão de contacto",
      "Domínio configurado",
      "1 ronda de revisão",
      "Integração opcional com o Captixy Assistant",
    ],
    cta: "Começar",
    highlighted: false,
  },
  {
    name: "Website Profissional",
    price: "850€",
    note: "pagamento único",
    tagline: "Um site completo para o teu negócio crescer online.",
    features: [
      "Tudo da Landing",
      "Até 5 páginas",
      "Backoffice para editares os conteúdos",
      "Email profissional configurado",
      "Blog opcional",
      "2 rondas de revisão",
      "Pequena formação de utilização",
    ],
    cta: "Começar",
    highlighted: true,
  },
  {
    name: "Website à Medida",
    price: "desde 1.200€",
    note: "sob proposta",
    tagline: "Para necessidades específicas e funcionalidades avançadas.",
    features: [
      "Funcionalidades à medida",
      "Área reservada, ecommerce ou integrações",
      "Multi-idioma",
      "Migração de site existente",
      "Design totalmente personalizado",
    ],
    cta: "Pedir proposta",
    highlighted: false,
  },
];

const MAINTENANCE_PLANS = [
  {
    name: "Manutenção Essencial",
    price: "15€/mês",
    body: "Alojamento, domínio e SSL · backups mensais · até 2 alterações de conteúdo por mês · suporte por email",
  },
  {
    name: "Manutenção Plus",
    price: "29€/mês",
    body: "Tudo do Essencial · backups semanais · até 6 alterações por mês · suporte prioritário",
  },
];

const WEBSITE_BUNDLES = [
  {
    name: "Site + Assistant — Essencial",
    perk: "Assistant Growth com 50% nos primeiros 3 meses",
    body: "Landing Page (350€) + Captixy Assistant integrado",
    quote: "O teu site já nasce a captar pedidos.",
    highlighted: false,
  },
  {
    name: "Site + Assistant — Completo",
    perk: "Assistant Growth grátis nos primeiros 3 meses + 1 mês de manutenção incluído",
    body: "Website Profissional (850€) + Captixy Assistant integrado",
    quote: "Presença profissional e pedidos organizados, num só passo.",
    highlighted: true,
  },
  {
    name: "Site + Assistant — Pro",
    perk: "Assistant Pro ao preço de Growth durante 6 meses + 3 meses de manutenção Plus",
    body: "Website Profissional ou À Medida + Captixy Assistant",
    quote: "Para negócios com mais volume e mais marca.",
    highlighted: false,
  },
];

function WebsitesPricingSection({ onOpenContact }) {
  return (
    <section
      id="precos"
      data-chapter
      className="relative overflow-hidden bg-[#F4F6FF] px-5 py-24 text-[#0F172A] md:px-8 lg:px-14"
    >
      <div className="mx-auto w-full max-w-7xl">
        <div className="mx-auto max-w-3xl text-center">
          <ChapterEyebrow index="06" label="Preços" centered />
          <h2 className="serif mx-auto mt-7 max-w-[12ch] text-[clamp(42px,6.5vw,104px)] font-light leading-[0.99] tracking-[-0.025em] text-[#0F172A]">
            Escolhe o ponto de partida.
          </h2>
          <p className="mx-auto mt-8 max-w-2xl text-base leading-8 text-slate-600 md:text-lg">
            Os websites são projectos de pagamento único. A manutenção é mensal
            e opcional, para manter domínio, alojamento e alterações simples
            tratados pela Captixy.
          </p>
        </div>

        <div className="mt-14 grid gap-5 lg:grid-cols-3">
          {WEBSITE_PRICE_PLANS.map((plan) => (
            <div
              key={plan.name}
              className={`relative flex h-full flex-col rounded-[1.35rem] border bg-white p-6 shadow-sm ${plan.highlighted ? "border-[var(--orange)] shadow-[0_24px_70px_-40px_rgba(15,23,42,.6)]" : "border-slate-200"}`}
            >
              {plan.highlighted && (
                <div className="absolute -top-3 left-6 rounded-full bg-[var(--orange)] px-3 py-1 text-[10px] font-bold uppercase tracking-[0.18em] text-white">
                  Mais escolhido
                </div>
              )}
              <div className="text-xs font-bold uppercase tracking-[0.2em] text-slate-400">
                {plan.note}
              </div>
              <h3 className="mt-4 text-xl font-bold text-[#0F172A]">
                {plan.name}
              </h3>
              <div className="mt-5 flex items-end gap-2">
                <span className="serif text-5xl leading-none text-[#0F172A]">
                  {plan.price}
                </span>
              </div>
              <p className="mt-5 text-sm leading-6 text-slate-600">
                {plan.tagline}
              </p>
              <ul className="mt-6 flex-1 space-y-3">
                {plan.features.map((feature) => (
                  <li
                    key={feature}
                    className="flex items-start gap-2.5 text-sm leading-6 text-slate-700"
                  >
                    <span className="mt-1 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-emerald-100 text-[10px] font-bold text-emerald-700">
                      ✓
                    </span>
                    {feature}
                  </li>
                ))}
              </ul>
              <a
                href="https://app.captixy.com/login?product=websites"
                className={`mt-8 inline-flex items-center justify-center gap-2 rounded-full px-5 py-3 text-sm font-bold transition-transform hover:-translate-y-1 ${plan.highlighted ? "bg-[#0F172A] text-white" : "border border-slate-200 text-[#0F172A] hover:border-[var(--orange)]"}`}
              >
                {plan.cta} <IconArrowRight className="h-4 w-4" />
              </a>
            </div>
          ))}
        </div>

        <div className="mt-8 grid gap-4 md:grid-cols-2">
          {MAINTENANCE_PLANS.map((plan) => (
            <div
              key={plan.name}
              className="rounded-[1.2rem] border border-slate-200 bg-white p-5"
            >
              <div className="flex flex-wrap items-baseline justify-between gap-3">
                <h3 className="text-base font-bold text-[#0F172A]">
                  {plan.name}
                </h3>
                <span className="serif text-3xl text-[#0F172A]">
                  {plan.price}
                </span>
              </div>
              <p className="mt-3 text-sm leading-6 text-slate-600">
                {plan.body}
              </p>
            </div>
          ))}
        </div>

        <div className="mt-14 rounded-[1.5rem] bg-[#0F172A] p-6 text-white md:p-8">
          <div className="flex flex-wrap items-end justify-between gap-4">
            <div>
              <div
                className="text-xs font-bold uppercase tracking-[0.22em]"
                style={{ color: "var(--orange)" }}
              >
                Bundles
              </div>
              <h3 className="serif mt-3 text-4xl leading-none">
                Site + Assistant
              </h3>
            </div>
            <button
              type="button"
              onClick={onOpenContact}
              className="inline-flex items-center gap-2 rounded-full bg-white px-5 py-3 text-sm font-bold text-[#0F172A]"
            >
              Falar connosco <IconArrowRight className="h-4 w-4" />
            </button>
          </div>
          <div className="mt-7 grid gap-4 lg:grid-cols-3">
            {WEBSITE_BUNDLES.map((bundle) => (
              <div
                key={bundle.name}
                className={`rounded-[1.15rem] border p-5 ${bundle.highlighted ? "border-white/25 bg-white/[0.08]" : "border-white/10 bg-white/[0.04]"}`}
              >
                {bundle.highlighted && (
                  <div
                    className="mb-3 inline-flex rounded-full px-3 py-1 text-[10px] font-bold uppercase tracking-[0.18em]"
                    style={{ background: accent(0.2), color: "var(--orange)" }}
                  >
                    Bundle recomendado
                  </div>
                )}
                <h4 className="text-lg font-bold text-white">{bundle.name}</h4>
                <p className="mt-3 text-sm leading-6 text-white/65">
                  {bundle.body}
                </p>
                <p
                  className="mt-4 text-sm font-bold"
                  style={{ color: "var(--orange)" }}
                >
                  {bundle.perk}
                </p>
                <p className="mt-5 border-t border-white/10 pt-4 text-sm italic text-white/70">
                  “{bundle.quote}”
                </p>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}

function LaunchSection({ onOpenContact }) {
  return (
    <section
      id="lancamento"
      data-chapter
      className="relative flex min-h-screen flex-col items-center justify-center overflow-hidden bg-[#0F172A] px-5 py-28 text-center md:px-8"
    >
      <div className="captixy-grid absolute inset-0 opacity-50" />
      <div
        className="absolute left-1/2 top-1/3 h-[60vw] max-h-[780px] w-[60vw] max-w-[780px] -translate-x-1/2 -translate-y-1/2 rounded-full"
        style={{
          background: `radial-gradient(circle, ${accent(0.15)}, transparent 64%)`,
        }}
      />
      <mw.div
        initial={{ opacity: 0, y: 28 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true, amount: 0.25 }}
        transition={{ duration: 0.8, ease: EEw }}
        className="relative inline-flex items-center gap-3 rounded-full px-5 py-2"
        style={{ background: accent(0.08), border: `1px solid ${accent(0.4)}` }}
      >
        <span
          className="h-2 w-2 rounded-full captixy-pulse"
          style={{ background: "var(--orange)" }}
        />
        <span
          className="text-xs font-bold uppercase tracking-[0.22em]"
          style={{ color: "var(--orange)" }}
        >
          07 · Lançamento
        </span>
      </mw.div>
      <mw.h2
        initial={{ opacity: 0, y: 34 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true, amount: 0.25 }}
        transition={{ duration: 0.9, delay: 0.1, ease: EEw }}
        className="serif relative mt-9 max-w-[15ch] text-[clamp(46px,8.5vw,140px)] font-light leading-[0.97] tracking-[-0.03em] text-primary"
      >
        O teu negócio merece ser{" "}
        <em className="font-normal" style={{ color: "var(--orange)" }}>
          visto
        </em>
      </mw.h2>
      <mw.p
        initial={{ opacity: 0, y: 30 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true, amount: 0.25 }}
        transition={{ duration: 0.85, delay: 0.2, ease: EEw }}
        className="relative mt-8 max-w-xl text-base leading-8 text-primary/60 md:text-xl"
      >
        Colocamos-te online de forma profissional, acessível e pronta para
        clientes. Escolhe o pacote certo na zona de planos.
      </mw.p>
      <mw.div
        initial={{ opacity: 0, y: 28 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true, amount: 0.25 }}
        transition={{ duration: 0.85, delay: 0.3, ease: EEw }}
        className="relative mt-10 flex flex-wrap justify-center gap-4"
      >
        <a
          href="https://app.captixy.com/login?product=websites"
          className="inline-flex items-center gap-3 rounded-full px-8 py-4 text-sm font-bold transition-transform hover:-translate-y-1"
          style={{
            background: "var(--orange)",
            color: "#0F172A",
            boxShadow: `0 20px 48px -16px ${accent(0.75)}`,
          }}
        >
          Começar projecto <IconArrowRight className="h-4 w-4" />
        </a>
        <button
          type="button"
          onClick={onOpenContact}
          className="inline-flex items-center gap-3 rounded-full border border-white/20 px-8 py-4 text-sm font-semibold text-primary transition-all hover:-translate-y-1 hover:bg-white/[0.07]"
        >
          Entrar em contacto
        </button>
      </mw.div>
      <mw.div
        initial={{ opacity: 0, y: 28 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true, amount: 0.25 }}
        transition={{ duration: 0.85, delay: 0.4, ease: EEw }}
        className="relative mt-14 flex flex-wrap justify-center gap-8 md:gap-16"
      >
        {PACKAGES.map((item, index) => (
          <div key={item.label} className="flex items-center gap-8 md:gap-16">
            {index > 0 && (
              <span className="hidden h-14 w-px bg-white/10 sm:block" />
            )}
            <div>
              <div className="serif text-4xl font-light text-primary md:text-5xl">
                {item.value}
              </div>
              <div className="mt-2 text-xs uppercase tracking-widest text-slate-500">
                {item.label}
              </div>
            </div>
          </div>
        ))}
      </mw.div>
    </section>
  );
}

function ProgressChrome({ onOpenContact }) {
  const [active, setActive] = uSw(0);
  const [progress, setProgress] = uSw(0);
  const sections = uMw(
    () => ["topo", ...CHAPTERS.map((c) => c.id), "precos", "lancamento"],
    [],
  );

  uEw(() => {
    let ticking = false;
    function update() {
      const scrollTop = window.scrollY || window.pageYOffset || 0;
      const docHeight =
        document.documentElement.scrollHeight - window.innerHeight;
      setProgress(docHeight > 0 ? Math.min(1, scrollTop / docHeight) : 0);
      const mark = scrollTop + window.innerHeight * 0.42;
      let nextActive = 0;
      sections.forEach((id, i) => {
        const el = document.getElementById(id);
        if (el && el.offsetTop <= mark) nextActive = i;
      });
      setActive(nextActive);
      ticking = false;
    }
    function onScroll() {
      if (!ticking) {
        window.requestAnimationFrame(update);
        ticking = true;
      }
    }
    update();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, [sections]);

  function go(dir) {
    const next = Math.max(0, Math.min(sections.length - 1, active + dir));
    scrollToId(sections[next]);
  }

  return (
    <>
      <div className="fixed left-4 top-1/2 z-50 hidden -translate-y-1/2 flex-col items-center gap-4 lg:flex">
        <span className="serif text-base text-primary">
          {String(active + 1).padStart(2, "0")}
        </span>
        <div className="relative h-32 w-px overflow-hidden bg-white/[0.12]">
          <div
            className="absolute left-0 top-0 h-full w-px origin-top"
            style={{
              background: "var(--orange)",
              transform: `scaleY(${progress})`,
            }}
          />
        </div>
        <span className="serif text-base text-slate-600">
          {String(sections.length).padStart(2, "0")}
        </span>
      </div>
      <div className="fixed bottom-5 right-5 z-50 hidden items-center gap-3 md:flex">
        <button
          onClick={() => go(-1)}
          className="flex h-12 w-12 items-center justify-center rounded-full border border-white/[0.15] bg-white/[0.07] text-primary transition-colors hover:bg-white/[0.14]"
        >
          ↑
        </button>
        <button
          onClick={() => go(1)}
          className="flex h-12 w-12 items-center justify-center rounded-full font-bold transition-transform hover:translate-y-0.5"
          style={{ background: "var(--orange)", color: "#0F172A" }}
        >
          ↓
        </button>
      </div>
      <button
        type="button"
        onClick={onOpenContact}
        className="fixed bottom-5 left-5 z-50 hidden items-center gap-3 rounded-full bg-white py-2.5 pl-3 pr-5 text-sm font-bold text-[#0F172A] shadow-2xl transition-transform hover:-translate-y-1 md:flex"
      >
        <span className="flex h-7 w-7 items-center justify-center rounded-full bg-[#25D366] text-white">
          ☎
        </span>
        Falar connosco
      </button>
    </>
  );
}

function AssistantContactPopup({ open, onClose }) {
  uEw(() => {
    if (!open) return undefined;
    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";

    function handleKeyDown(event) {
      if (event.key === "Escape") onClose();
    }

    window.addEventListener("keydown", handleKeyDown);
    return () => {
      document.body.style.overflow = previousOverflow;
      window.removeEventListener("keydown", handleKeyDown);
    };
  }, [open, onClose]);

  return (
    <APw>
      {open && (
        <mw.div
          className="fixed inset-0 z-[80] flex items-center justify-center bg-[#020617]/78 px-4 py-6 backdrop-blur-xl"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          onClick={onClose}
        >
          <mw.div
            role="dialog"
            aria-modal="true"
            aria-label="Assistente Captixy"
            className="relative flex h-[min(760px,92vh)] w-full max-w-[440px] flex-col overflow-hidden rounded-[1.7rem] border border-white/12 bg-[#070B1F] shadow-[0_30px_120px_rgba(2,6,23,.62)]"
            initial={{ opacity: 0, y: 24, scale: 0.96 }}
            animate={{ opacity: 1, y: 0, scale: 1 }}
            exit={{ opacity: 0, y: 18, scale: 0.97 }}
            transition={{ duration: 0.28, ease: EEw }}
            onClick={(event) => event.stopPropagation()}
          >
            <div className="flex items-center justify-between gap-4 border-b border-white/10 px-5 py-4">
              <div>
                <div className="text-[10px] font-bold uppercase tracking-[0.2em] text-white/38">
                  Captixy Assistant
                </div>
                <h2 className="mt-1 text-lg font-bold text-white">
                  Fala connosco
                </h2>
              </div>
              <button
                type="button"
                onClick={onClose}
                className="flex h-10 w-10 items-center justify-center rounded-full bg-white/8 text-xl leading-none text-white/70 transition-colors hover:bg-white/14 hover:text-white"
                aria-label="Fechar pop-up"
              >
                ×
              </button>
            </div>
            <iframe
              title="Captixy Assistant"
              src={ASSISTANT_CONTACT_URL}
              className="min-h-0 flex-1 border-0 bg-[#070B1F]"
            />
          </mw.div>
        </mw.div>
      )}
    </APw>
  );
}

function PageStyles() {
  return (
    <style>{`
      #root em { font-style: italic; color: var(--orange); }
      .captixy-grid {
        background-image: linear-gradient(rgba(255,255,255,.024) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.024) 1px, transparent 1px);
        background-size: 60px 60px;
        animation: captixyGridDrift 16s linear infinite;
        pointer-events: none;
      }
      @keyframes captixyGridDrift { to { background-position: 60px 60px; } }
      @keyframes captixyFloat { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-20px); } }
      @keyframes captixyFloatSoft { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(14px); } }
      @keyframes captixyScrollDot { 0% { transform: translateY(0); opacity: 0; } 30%, 80% { opacity: 1; } 100% { transform: translateY(44px); opacity: 0; } }
      @keyframes captixyPulse { 0%, 100% { opacity: .45; transform: scale(1); } 50% { opacity: .9; transform: scale(1.08); } }
      @keyframes captixyBlink { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
      .captixy-float { animation: captixyFloat 8s ease-in-out infinite; }
      .captixy-float-soft { animation: captixyFloatSoft 6s ease-in-out infinite; }
      .captixy-scroll-dot { animation: captixyScrollDot 2s ease-in-out infinite; }
      .captixy-pulse { animation: captixyPulse 1.6s ease-in-out infinite; }
      .captixy-blink { animation: captixyBlink 1.2s infinite; }
      @media (max-width: 900px) {
        #root [data-chapter] { min-height: auto; }
      }
    `}</style>
  );
}

function WebsitesApp() {
  const [contactOpen, setContactOpen] = uSw(false);

  return (
    <>
      <PageStyles />
      <WebsitesHero />
      {CHAPTERS.map((chapter) => (
        <ChapterSection key={chapter.id} chapter={chapter} />
      ))}
      <WebsitesPricingSection onOpenContact={() => setContactOpen(true)} />
      <LaunchSection onOpenContact={() => setContactOpen(true)} />
      <FTw />
      <ProgressChrome onOpenContact={() => setContactOpen(true)} />
      <AssistantContactPopup
        open={contactOpen}
        onClose={() => setContactOpen(false)}
      />
    </>
  );
}

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