// ff-hero.jsx — Hero section with rotating audience text + two variants

const { useState: __h_useState, useEffect: __h_useEffect } = React;

// Fade-in-up rotator — whole phrase swaps with a stable width
function Rotator({ items, interval = 2800 }) {
  const [i, setI] = __h_useState(0);
  const [key, setKey] = __h_useState(0);
  __h_useEffect(() => {
    setI(0);
    setKey((k) => k + 1);
  }, [items]);
  __h_useEffect(() => {
    const id = setInterval(() => {
      setI((v) => (v + 1) % items.length);
      setKey((k) => k + 1);
    }, interval);
    return () => clearInterval(id);
  }, [items, interval]);
  return (
    <span className="rotator">
      <span key={key} className="rotator-item">{items[i]}</span>
    </span>
  );
}

function HeroMetric({ value, label, sub }) {
  return (
    <div className="hero-metric">
      <div className="hero-metric-v">{value}</div>
      <div className="hero-metric-l">{label}</div>
      {sub && <div className="hero-metric-s">{sub}</div>}
    </div>
  );
}

function Hero({ variant = "flow", accent, s }) {
  return (
    <section
      id="top"
      data-screen-label="01 Hero"
      className={`hero hero-${variant}`}
    >
      <div className="hero-bg">
        {variant === "split" && <FlowMesh accent={accent} density={16} opacity={0.35} />}
        <div className="hero-grid" />
        <div className="hero-vignette" />
      </div>

      {variant === "flow" ? <HeroFlow s={s} /> : <HeroSplit s={s} />}
    </section>
  );
}

function HeroFlow({ s }) {
  return (
    <div className="wrap hero-inner hero-inner-flow">
      <Reveal className="hero-brand" as="div">
        <BrandIcon height={32} className="hero-brand-mark" />
      </Reveal>

      <Reveal delay={1} as="h1" className="hero-title">
        <span>{s.hero.titlePre}</span>
        <br />
        <Rotator items={s.hero.audiences} />
      </Reveal>

      <Reveal delay={2} className="hero-sub" as="p">
        {s.hero.sub}
      </Reveal>

      <Reveal delay={3} className="hero-cta">
        <a href="#contact" className="btn btn-primary btn-lg">
          {s.hero.ctaPrimary} <span className="arrow">→</span>
        </a>
        <a href="#cases" className="btn btn-ghost btn-lg">
          {s.hero.ctaSecondary}
        </a>
      </Reveal>

      <Reveal delay={4} className="hero-live" as="div">
        <LiveDemo />
      </Reveal>

      <Reveal delay={5} className="hero-metrics" as="div">
        <HeroMetric value={<><Counter to={50} suffix="+" /></>} label={s.hero.metric1.label} sub={s.hero.metric1.sub} />
        <HeroMetric value={<><Counter to={12} /></>}            label={s.hero.metric2.label} sub={s.hero.metric2.sub} />
        <HeroMetric value={<><Counter to={4} suffix="×" /></>}  label={s.hero.metric3.label} sub={s.hero.metric3.sub} />
        <HeroMetric value={<>~<Counter to={6} suffix={s.hero.timeUnit} /></>} label={s.hero.metric4.label} sub={s.hero.metric4.sub} />
      </Reveal>
    </div>
  );
}

// ─────────── Hero W — Three.js 3D glass model ───────────
const W_SVG_STRING = '<svg viewBox="0 0 353 265" xmlns="http://www.w3.org/2000/svg">' +
  '<path d="M87.4379 203.509L87.7743 203.846L176.262 115.358L176.5 115.596L176.976 116.071L264.414 203.509L264.75 203.846L265.086 203.509L353 115.596V176.966L352.901 176.868L264.75 265.019L176.597 176.87L176.26 176.533L175.926 176.868L87.7743 265.019L0 177.245L0 116.071L87.4379 203.509Z" fill="#ffffff"/>' +
  '<path d="M264.75 159.405L176.976 71.6304L176.598 71.2532L176.5 71.3517L176.262 71.5895L175.926 71.2532L88.25 158.929V87.7647L175.786 0.228357L176.5 0.941969L176.976 1.41771L263.933 88.3751L263.798 88.5102L264.144 88.8561L353 0V71.3517L352.901 71.2532L264.75 159.405Z" fill="#3b82f6"/>' +
  '</svg>';

function HeroW3D() {
  const containerRef = React.useRef(null);

  __h_useEffect(() => {
    const THREE = window.THREE;
    const container = containerRef.current;
    if (!THREE || !container || !THREE.SVGLoader) return;

    const accentVar = getComputedStyle(document.documentElement).getPropertyValue("--accent").trim() || "#3b82f6";

    const width = container.clientWidth || 200;
    const height = container.clientHeight || 200;

    // Renderer
    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setSize(width, height);
    renderer.outputEncoding = THREE.sRGBEncoding;
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    renderer.toneMappingExposure = 0.7;
    container.appendChild(renderer.domElement);

    // Scene
    const scene = new THREE.Scene();

    // Environment for glass reflections
    let envMap = null;
    if (THREE.RoomEnvironment && THREE.PMREMGenerator) {
      const pmrem = new THREE.PMREMGenerator(renderer);
      envMap = pmrem.fromScene(new THREE.RoomEnvironment(), 0.04).texture;
      scene.environment = envMap;
      pmrem.dispose();
    }

    // Camera
    const camera = new THREE.PerspectiveCamera(35, width / height, 1, 2000);
    camera.position.set(0, 0, 520);

    // Lights — soft accent rim
    const ambient = new THREE.AmbientLight(0xffffff, 0.15);
    scene.add(ambient);
    const key = new THREE.DirectionalLight(0xffffff, 0.35);
    key.position.set(120, 180, 200);
    scene.add(key);
    const rim = new THREE.DirectionalLight(new THREE.Color(accentVar), 0.7);
    rim.position.set(-160, -80, -140);
    scene.add(rim);

    // Parse SVG, build extruded meshes
    const loader = new THREE.SVGLoader();
    const svgData = loader.parse(W_SVG_STRING);

    const pivot = new THREE.Group();
    const meshGroup = new THREE.Group();
    const meshes = [];

    svgData.paths.forEach((path, idx) => {
      const isTopW = idx === 1;
      const shapes = THREE.SVGLoader.createShapes(path);
      shapes.forEach((shape) => {
        const geo = new THREE.ExtrudeGeometry(shape, {
          depth: 32,
          bevelEnabled: true,
          bevelThickness: 4,
          bevelSize: 3,
          bevelSegments: 6,
          curveSegments: 16,
        });
        const mat = new THREE.MeshPhysicalMaterial({
          color: isTopW ? new THREE.Color(accentVar) : 0xdfe2ea,
          transmission: 0,
          transparent: false,
          opacity: 1,
          roughness: 0.4,
          metalness: 0,
          clearcoat: 0.6,
          clearcoatRoughness: 0.25,
          envMapIntensity: 0.4,
          side: THREE.FrontSide,
        });
        const mesh = new THREE.Mesh(geo, mat);
        meshes.push(mesh);
        meshGroup.add(mesh);
      });
    });

    // SVG y-axis is inverted vs Three.js — flip and center
    meshGroup.scale.set(1, -1, 1);
    const bbox = new THREE.Box3().setFromObject(meshGroup);
    const center = bbox.getCenter(new THREE.Vector3());
    meshGroup.position.set(-center.x, -center.y, -center.z);

    pivot.add(meshGroup);
    scene.add(pivot);

    // Animation
    const reducedMotion = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    let frame;
    const start = performance.now();
    const animate = () => {
      const t = (performance.now() - start) / 1000;
      if (!reducedMotion) {
        pivot.rotation.y = t * 0.4;
        pivot.rotation.x = Math.sin(t * 0.3) * 0.12;
      } else {
        pivot.rotation.y = 0.5;
        pivot.rotation.x = 0.1;
      }
      renderer.render(scene, camera);
      frame = requestAnimationFrame(animate);
    };
    animate();

    // Resize
    const onResize = () => {
      const w = container.clientWidth || 200;
      const h = container.clientHeight || 200;
      camera.aspect = w / h;
      camera.updateProjectionMatrix();
      renderer.setSize(w, h);
    };
    window.addEventListener("resize", onResize);

    return () => {
      cancelAnimationFrame(frame);
      window.removeEventListener("resize", onResize);
      meshes.forEach((m) => { m.geometry.dispose(); m.material.dispose(); });
      if (envMap) envMap.dispose();
      renderer.dispose();
      if (renderer.domElement && renderer.domElement.parentNode === container) {
        container.removeChild(renderer.domElement);
      }
    };
  }, []);

  return <div ref={containerRef} className="hero-w-3d" aria-hidden="true" />;
}

// ─────────── Live demo — Direction B · Editor mockup ───────────
// A 3-pane editor mockup: workspace rail (left) · flow canvas (centre) ·
// node inspector + events tail (right). Reads as an honest screenshot of
// the Wayrelay studio, not a marketing diagram.
const LIVE_TOPO_SOURCES = [
  { y: 56,  icon: "letter",     label: "Email",    role: "IMAP", tag: "EML", brand: false },
  { y: 130, icon: "code",       label: "Webhook",  role: "POST", tag: "WEB", brand: false },
  { y: 204, icon: "si-telegram",label: "Telegram", role: "BOT",  tag: "TG",  brand: true  },
];
const LIVE_TOPO_SINKS = [
  { y: 56,  icon: "shop",       label: "CRM",   role: "LEADS",  tag: "CRM", brand: false },
  { y: 130, icon: "server",     label: "ERP",   role: "ORDERS", tag: "ERP", brand: false },
  { y: 204, icon: "si-slack",   label: "Slack", role: "OPS",    tag: "SLK", brand: true  },
];

// Workspace rail entries — what the user sees in their "open flows" tree.
// One row is selected (the flow currently shown in the canvas).
const LIVE_RAIL_FLOWS = [
  { id: "order_intake",     label: "order_intake.flow",     state: "active",   selected: true  },
  { id: "payment_recon",    label: "payment_recon.flow",    state: "draft",    selected: false },
  { id: "lead_qualifier",   label: "lead_qualifier.flow",   state: "active",   selected: false },
  { id: "invoice_dispatch", label: "invoice_dispatch.flow", state: "active",   selected: false },
  { id: "ops_alerts",       label: "ops_alerts.flow",       state: "active",   selected: false },
  { id: "churn_signals",    label: "churn_signals.flow",    state: "paused",   selected: false },
];
// Real brand logos via Simple Icons where a brand exists; Solar Bold Duotone
// for generic concepts (Email/Webhook/CRM/ERP/Junction). Brand icons set
// `brand: true` so the rail can render them flush in currentColor without
// the duotone "two-paths" treatment.
const LIVE_RAIL_INTEGRATIONS = [
  { icon: "letter",       label: "Email",    brand: false },
  { icon: "code",         label: "Webhook",  brand: false },
  { icon: "si-telegram",  label: "Telegram", brand: true  },
  { icon: "si-postgres",  label: "Postgres", brand: true  },
  { icon: "si-openai",    label: "OpenAI",   brand: true  },
  { icon: "si-slack",     label: "Slack",    brand: true  },
];

// Payload examples per source kind. Realistic-feeling shorthand that reads as
// "things are happening" rather than placeholder text.
const LIVE_PAYLOADS = {
  EML: [
    { sink: "CRM", text: "lead@acme.co · RFQ #4821" },
    { sink: "CRM", text: "info@vela.eu · pricing" },
    { sink: "ERP", text: "po@northbound.co · invoice" },
    { sink: "CRM", text: "hello@stride.io · demo" },
    { sink: "SLK", text: "support@kr.co · urgent" },
  ],
  WEB: [
    { sink: "ERP", text: "POST /order/4821 · 200" },
    { sink: "CRM", text: "POST /signup · 201" },
    { sink: "ERP", text: "POST /invoice/19 · 200" },
    { sink: "SLK", text: "POST /alert/disk · 200" },
    { sink: "CRM", text: "POST /lead · 201" },
  ],
  TG: [
    { sink: "SLK", text: "@kfomenko · invoice?" },
    { sink: "CRM", text: "@dani · status update" },
    { sink: "SLK", text: "@team · daily report" },
    { sink: "ERP", text: "@ops · ship status" },
    { sink: "SLK", text: "@anna · onboarding" },
  ],
};
const LIVE_SOURCE_KEYS = ["EML", "WEB", "TG"];

// Solar Bold Duotone + Simple Icons (Slack/Telegram).
// Returns bare <path> elements (no wrapping <svg>) so they can be embedded
// inside a parent SVG via <g transform="…">. 24×24 source viewBox.
function LiveNodeIcon({ kind }) {
  switch (kind) {
    case "letter":
      return (
        <React.Fragment>
          <path fill="currentColor" opacity=".5" d="M14.2 3H9.8C5.652 3 3.577 3 2.289 4.318S1 7.758 1 12s0 6.364 1.289 7.682S5.652 21 9.8 21h4.4c4.148 0 6.223 0 7.511-1.318S23 16.242 23 12s0-6.364-1.289-7.682S18.348 3 14.2 3" />
          <path fill="currentColor" d="M19.128 8.033a.825.825 0 0 0-1.056-1.268l-2.375 1.98c-1.026.855-1.738 1.447-2.34 1.833c-.582.375-.977.5-1.357.5s-.774-.125-1.357-.5c-.601-.386-1.314-.978-2.34-1.834L5.928 6.765a.825.825 0 0 0-1.056 1.268l2.416 2.014c.975.812 1.765 1.47 2.463 1.92c.726.466 1.434.762 2.25.762c.814 0 1.522-.296 2.249-.763c.697-.448 1.487-1.107 2.462-1.92z" />
        </React.Fragment>
      );
    case "code":
      return (
        <React.Fragment>
          <path fill="currentColor" opacity=".5" d="M2 12c0-4.714 0-7.071 1.464-8.536C4.93 2 7.286 2 12 2s7.071 0 8.535 1.464C22 4.93 22 7.286 22 12s0 7.071-1.465 8.535C19.072 22 16.714 22 12 22s-7.071 0-8.536-1.465C2 19.072 2 16.714 2 12" />
          <path fill="currentColor" d="M13.488 6.446a.75.75 0 0 1 .53.918l-2.588 9.66a.75.75 0 0 1-1.449-.389l2.589-9.659a.75.75 0 0 1 .918-.53M14.97 8.47a.75.75 0 0 1 1.06 0l.209.208c.635.635 1.165 1.165 1.529 1.642c.384.504.654 1.036.654 1.68s-.27 1.176-.654 1.68c-.364.477-.894 1.007-1.53 1.642l-.208.208a.75.75 0 1 1-1.06-1.06l.171-.172c.682-.682 1.139-1.14 1.434-1.528c.283-.37.347-.586.347-.77s-.064-.4-.347-.77c-.295-.387-.752-.846-1.434-1.528l-.171-.172a.75.75 0 0 1 0-1.06m-7 0a.75.75 0 0 1 1.06 1.06l-.171.172c-.682.682-1.138 1.14-1.434 1.528c-.283.37-.346.586-.346.77s.063.4.346.77c.296.387.752.846 1.434 1.528l.172.172a.75.75 0 1 1-1.061 1.06l-.208-.208c-.636-.635-1.166-1.165-1.53-1.642c-.384-.504-.653-1.036-.653-1.68s.27-1.176.653-1.68c.364-.477.894-1.007 1.53-1.642z" />
        </React.Fragment>
      );
    case "telegram":
    case "si-telegram":
      return (
        <path fill="currentColor" d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12a12 12 0 0 0 12-12A12 12 0 0 0 12 0zm4.962 7.224c.1-.002.321.023.465.14a.5.5 0 0 1 .171.325c.016.093.036.306.02.472c-.18 1.898-.962 6.502-1.36 8.627c-.168.9-.499 1.201-.82 1.23c-.696.065-1.225-.46-1.9-.902c-1.056-.693-1.653-1.124-2.678-1.8c-1.185-.78-.417-1.21.258-1.91c.177-.184 3.247-2.977 3.307-3.23c.007-.032.014-.15-.056-.212s-.174-.041-.249-.024q-.159.037-5.061 3.345q-.72.495-1.302.48c-.428-.008-1.252-.241-1.865-.44c-.752-.245-1.349-.374-1.297-.789q.04-.324.893-.663q5.247-2.286 6.998-3.014c3.332-1.386 4.025-1.627 4.476-1.635" />
      );
    // ── Simple Icons brand marks (single-path, currentColor) ──
    case "si-postgres":
      return (
        <path fill="currentColor" d="M23.56 14.723a.5.5 0 0 0-.057-.12q-.21-.395-1.007-.231c-1.654.34-2.294.13-2.526-.02c1.342-2.048 2.445-4.522 3.041-6.83c.272-1.05.798-3.523.122-4.73a1.6 1.6 0 0 0-.15-.236C21.693.91 19.8.025 17.51.001c-1.495-.016-2.77.346-3.116.479a10 10 0 0 0-.516-.082a8 8 0 0 0-1.312-.127c-1.182-.019-2.203.264-3.05.84C8.66.79 4.729-.534 2.296 1.19C.935 2.153.309 3.873.43 6.304c.041.818.507 3.334 1.243 5.744q.69 2.26 1.433 3.582q.83 1.493 1.714 1.79c.448.148 1.133.143 1.858-.729a56 56 0 0 1 1.945-2.206c.435.235.906.362 1.39.377v.004a11 11 0 0 0-.247.305c-.339.43-.41.52-1.5.745c-.31.064-1.134.233-1.146.811a.6.6 0 0 0 .091.327c.227.423.922.61 1.015.633c1.335.333 2.505.092 3.372-.679c-.017 2.231.077 4.418.345 5.088c.221.553.762 1.904 2.47 1.904q.375.001.829-.094c1.782-.382 2.556-1.17 2.855-2.906c.15-.87.402-2.875.539-4.101c.017-.07.036-.12.057-.136c0 0 .07-.048.427.03l.044.007l.254.022l.015.001c.847.039 1.911-.142 2.531-.43c.644-.3 1.806-1.033 1.595-1.67M2.37 11.876c-.744-2.435-1.178-4.885-1.212-5.571c-.109-2.172.417-3.683 1.562-4.493c1.837-1.299 4.84-.54 6.108-.13l-.01.01C6.795 3.734 6.843 7.226 6.85 7.44c0 .082.006.199.016.36c.034.586.1 1.68-.074 2.918c-.16 1.15.194 2.276.973 3.089q.12.126.252.237c-.347.371-1.1 1.193-1.903 2.158c-.568.682-.96.551-1.088.508c-.392-.13-.813-.587-1.239-1.322c-.48-.839-.963-2.032-1.415-3.512m6.007 5.088a1.6 1.6 0 0 1-.432-.178c.089-.039.237-.09.483-.14c1.284-.265 1.482-.451 1.915-1a8 8 0 0 1 .367-.443a.4.4 0 0 0 .074-.13c.17-.151.272-.11.436-.042c.156.065.308.26.37.475c.03.102.062.295-.045.445c-.904 1.266-2.222 1.25-3.168 1.013m2.094-3.988l-.052.14c-.133.357-.257.689-.334 1.004c-.667-.002-1.317-.288-1.81-.803c-.628-.655-.913-1.566-.783-2.5c.183-1.308.116-2.447.08-3.059l-.013-.22c.296-.262 1.666-.996 2.643-.772c.446.102.718.406.83.928c.585 2.704.078 3.83-.33 4.736a9 9 0 0 0-.23.546m7.364 4.572q-.024.266-.062.596l-.146.438a.4.4 0 0 0-.018.108c-.006.475-.054.649-.115.87a4.8 4.8 0 0 0-.18 1.057c-.11 1.414-.878 2.227-2.417 2.556c-1.515.325-1.784-.496-2.02-1.221a7 7 0 0 0-.078-.227c-.215-.586-.19-1.412-.157-2.555c.016-.561-.025-1.901-.33-2.646q.006-.44.019-.892a.4.4 0 0 0-.016-.113a2 2 0 0 0-.044-.208c-.122-.428-.42-.786-.78-.935c-.142-.059-.403-.167-.717-.087c.067-.276.183-.587.309-.925l.053-.142c.06-.16.134-.325.213-.5c.426-.948 1.01-2.246.376-5.178c-.237-1.098-1.03-1.634-2.232-1.51c-.72.075-1.38.366-1.709.532a6 6 0 0 0-.196.104c.092-1.106.439-3.174 1.736-4.482a4 4 0 0 1 .303-.276a.35.35 0 0 0 .145-.064c.752-.57 1.695-.85 2.802-.833q.616.01 1.174.081c1.94.355 3.244 1.447 4.036 2.383c.814.962 1.255 1.931 1.431 2.454c-1.323-.134-2.223.127-2.68.78c-.992 1.418.544 4.172 1.282 5.496c.135.242.252.452.289.54c.24.583.551.972.778 1.256c.07.087.138.171.189.245c-.4.116-1.12.383-1.055 1.717a35 35 0 0 1-.084.815c-.046.208-.07.46-.1.766m.89-1.621c-.04-.832.27-.919.597-1.01l.135-.041a1 1 0 0 0 .134.103c.57.376 1.583.421 3.007.134c-.202.177-.519.4-.953.601c-.41.19-1.096.333-1.747.364c-.72.034-1.086-.08-1.173-.151m.57-9.271a7 7 0 0 1-.105 1.001c-.055.358-.112.728-.127 1.177c-.014.436.04.89.093 1.33c.107.887.216 1.8-.207 2.701a4 4 0 0 1-.188-.385a8 8 0 0 0-.325-.617c-.616-1.104-2.057-3.69-1.32-4.744c.38-.543 1.342-.566 2.179-.463m.228 7.013l-.085-.107l-.035-.044c.726-1.2.584-2.387.457-3.439c-.052-.432-.1-.84-.088-1.222c.013-.407.066-.755.118-1.092c.064-.415.13-.844.111-1.35a.6.6 0 0 0 .012-.19c-.046-.486-.6-1.938-1.73-3.253a7.8 7.8 0 0 0-2.688-2.04A9.3 9.3 0 0 1 17.62.746c2.052.046 3.675.814 4.824 2.283a1 1 0 0 1 .067.1c.723 1.356-.276 6.275-2.987 10.54m-8.816-6.116c-.025.18-.31.423-.621.423l-.081-.006a.8.8 0 0 1-.506-.315c-.046-.06-.12-.178-.106-.285a.22.22 0 0 1 .093-.149c.118-.089.352-.122.61-.086c.316.044.642.193.61.418m7.93-.411c.011.08-.049.2-.153.31a.72.72 0 0 1-.408.223l-.075.005c-.293 0-.541-.234-.56-.371c-.024-.177.264-.31.56-.352c.298-.042.612.009.636.185" />
      );
    case "si-openai":
      return (
        <path fill="currentColor" d="M22.282 9.821a6 6 0 0 0-.516-4.91a6.05 6.05 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a6 6 0 0 0-3.998 2.9a6.05 6.05 0 0 0 .743 7.097a5.98 5.98 0 0 0 .51 4.911a6.05 6.05 0 0 0 6.515 2.9A6 6 0 0 0 13.26 24a6.06 6.06 0 0 0 5.772-4.206a6 6 0 0 0 3.997-2.9a6.06 6.06 0 0 0-.747-7.073M13.26 22.43a4.48 4.48 0 0 1-2.876-1.04l.141-.081l4.779-2.758a.8.8 0 0 0 .392-.681v-6.737l2.02 1.168a.07.07 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494M3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085l4.783 2.759a.77.77 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646M2.34 7.896a4.5 4.5 0 0 1 2.366-1.973V11.6a.77.77 0 0 0 .388.677l5.815 3.354l-2.02 1.168a.08.08 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855l-5.833-3.387L15.119 7.2a.08.08 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667m2.01-3.023l-.141-.085l-4.774-2.782a.78.78 0 0 0-.785 0L9.409 9.23V6.897a.07.07 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135l-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08L8.704 5.46a.8.8 0 0 0-.393.681zm1.097-2.365l2.602-1.5l2.607 1.5v2.999l-2.597 1.5l-2.607-1.5Z" />
      );
    case "junction":
      return (
        <React.Fragment>
          <path fill="currentColor" opacity=".5" d="M3.464 20.536C4.93 22 7.286 22 12 22s7.071 0 8.535-1.465C22 19.072 22 16.714 22 12s0-7.071-1.465-8.536C19.072 2 16.714 2 12 2S4.929 2 3.464 3.464C2 4.93 2 7.286 2 12s0 7.071 1.464 8.535" />
          <path fill="currentColor" d="M12.75 6.5a.75.75 0 0 0-1.5 0v6A3.25 3.25 0 0 1 8 15.75h-.19l.22-.22a.75.75 0 1 0-1.06-1.06l-1.5 1.5a.75.75 0 0 0 0 1.06l1.5 1.5a.75.75 0 0 0 1.06-1.06l-.22-.22H8c1.68 0 3.155-.872 4-2.187a4.75 4.75 0 0 0 4 2.187h.19l-.22.22a.75.75 0 1 0 1.06 1.06l1.5-1.5a.75.75 0 0 0 0-1.06l-1.5-1.5a.75.75 0 1 0-1.06 1.06l.22.22H16a3.25 3.25 0 0 1-3.25-3.25z" />
        </React.Fragment>
      );
    case "shop":
      return (
        <React.Fragment>
          <path fill="currentColor" d="M14.5 21.991V18.5c0-.935 0-1.402-.201-1.75a1.5 1.5 0 0 0-.549-.549C13.402 16 12.935 16 12 16s-1.402 0-1.75.201a1.5 1.5 0 0 0-.549.549c-.201.348-.201.815-.201 1.75v3.491z" />
          <path fill="currentColor" fillRule="evenodd" clipRule="evenodd" opacity=".5" d="M5.732 12c-.89 0-1.679-.376-2.232-.967V14c0 3.771 0 5.657 1.172 6.828c.943.944 2.348 1.127 4.828 1.163h5c2.48-.036 3.885-.22 4.828-1.163C20.5 19.657 20.5 17.771 20.5 14v-2.966a3.06 3.06 0 0 1-5.275-1.789l-.073-.728a3.167 3.167 0 1 1-6.307.038l-.069.69A3.06 3.06 0 0 1 5.732 12m8.768 6.5v3.491h-5V18.5c0-.935 0-1.402.201-1.75a1.5 1.5 0 0 1 .549-.549C10.598 16 11.065 16 12 16s1.402 0 1.75.201a1.5 1.5 0 0 1 .549.549c.201.348.201.815.201 1.75" />
          <path fill="currentColor" d="M9.5 2h5l.652 6.517a3.167 3.167 0 1 1-6.304 0z" />
          <path fill="currentColor" opacity=".7" d="M3.33 5.351c.178-.89.267-1.335.448-1.696a3 3 0 0 1 1.889-1.548C6.057 2 6.51 2 7.418 2h2.083l-.725 7.245a3.06 3.06 0 1 1-6.044-.904zm17.34 0c-.178-.89-.267-1.335-.448-1.696a3 3 0 0 0-1.888-1.548C17.944 2 17.49 2 16.582 2H14.5l.725 7.245a3.06 3.06 0 1 0 6.043-.904z" />
        </React.Fragment>
      );
    case "server":
      return (
        <React.Fragment>
          <path fill="currentColor" opacity=".5" d="M6 13h12c1.886 0 2.828 0 3.414.586S22 15.114 22 17s0 2.828-.586 3.414S19.886 21 18 21H6c-1.886 0-2.828 0-3.414-.586S2 18.886 2 17s0-2.828.586-3.414S4.114 13 6 13M6 3h12c1.886 0 2.828 0 3.414.586S22 5.114 22 7s0 2.828-.586 3.414S19.886 11 18 11H6c-1.886 0-2.828 0-3.414-.586S2 8.886 2 7s0-2.828.586-3.414S4.114 3 6 3" />
          <path fill="currentColor" d="M12.75 7a.75.75 0 0 1 .75-.75H18a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75M6 8.75A.75.75 0 0 1 5.25 8V6a.75.75 0 0 1 1.5 0v2a.75.75 0 0 1-.75.75m3 0A.75.75 0 0 1 8.25 8V6a.75.75 0 0 1 1.5 0v2a.75.75 0 0 1-.75.75M12.75 17a.75.75 0 0 1 .75-.75H18a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75M6 18.75a.75.75 0 0 1-.75-.75v-2a.75.75 0 0 1 1.5 0v2a.75.75 0 0 1-.75.75m3 0a.75.75 0 0 1-.75-.75v-2a.75.75 0 0 1 1.5 0v2a.75.75 0 0 1-.75.75" />
        </React.Fragment>
      );
    case "slack":
    case "si-slack":
      return (
        <path fill="currentColor" d="M5.042 15.165a2.53 2.53 0 0 1-2.52 2.523A2.53 2.53 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52zm1.271 0a2.527 2.527 0 0 1 2.521-2.52a2.527 2.527 0 0 1 2.521 2.52v6.313A2.53 2.53 0 0 1 8.834 24a2.53 2.53 0 0 1-2.521-2.522zM8.834 5.042a2.53 2.53 0 0 1-2.521-2.52A2.53 2.53 0 0 1 8.834 0a2.53 2.53 0 0 1 2.521 2.522v2.52zm0 1.271a2.53 2.53 0 0 1 2.521 2.521a2.53 2.53 0 0 1-2.521 2.521H2.522A2.53 2.53 0 0 1 0 8.834a2.53 2.53 0 0 1 2.522-2.521zm10.122 2.521a2.53 2.53 0 0 1 2.522-2.521A2.53 2.53 0 0 1 24 8.834a2.53 2.53 0 0 1-2.522 2.521h-2.522zm-1.268 0a2.53 2.53 0 0 1-2.523 2.521a2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.53 2.53 0 0 1 2.523 2.522zm-2.523 10.122a2.53 2.53 0 0 1 2.523 2.522A2.53 2.53 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522zm0-1.268a2.527 2.527 0 0 1-2.52-2.523a2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.53 2.53 0 0 1-2.522 2.523z" />
      );
    case "folder":
      return (
        <React.Fragment>
          <path fill="currentColor" opacity=".5" d="M22 14v-2.202c0-2.632 0-3.949-.77-4.804a3 3 0 0 0-.224-.225C20.151 6 18.834 6 16.202 6h-.374c-1.153 0-1.73 0-2.268-.153a4 4 0 0 1-.848-.352C12.224 5.224 11.816 4.815 11 4l-.55-.55c-.274-.274-.41-.41-.554-.53a4 4 0 0 0-2.18-.903C7.53 2 7.336 2 6.95 2c-.883 0-1.324 0-1.692.07A4 4 0 0 0 2.07 5.257C2 5.626 2 6.068 2 6.95V14c0 3.771 0 5.657 1.172 6.828S6.229 22 10 22h4c3.771 0 5.657 0 6.828-1.172S22 17.771 22 14" />
          <path fill="currentColor" d="M12.25 10a.75.75 0 0 1 .75-.75h6a.75.75 0 0 1 0 1.5h-6a.75.75 0 0 1-.75-.75" />
        </React.Fragment>
      );
    case "play":
      return (
        <React.Fragment>
          <path fill="currentColor" fillRule="evenodd" d="M23 12c0-1.035-.53-2.07-1.591-2.647L8.597 2.385C6.534 1.264 4 2.724 4 5.033V12z" clipRule="evenodd" />
          <path fill="currentColor" opacity=".5" d="m8.597 21.615l12.812-6.968A2.99 2.99 0 0 0 23 12H4v6.967c0 2.31 2.534 3.769 4.597 2.648" />
        </React.Fragment>
      );
    case "search":
      return (
        <g fill="none" stroke="currentColor" strokeWidth="1.6">
          <circle cx="11.5" cy="11.5" r="7.5" />
          <path strokeLinecap="round" d="M17 17L21 21" />
        </g>
      );
    // Solar alt-arrow-right-bold-duotone — tiny "drills into" chevron used
    // in the sidebar integration rows.
    case "chev-right":
      return (
        <React.Fragment>
          <path fill="currentColor" d="m12.404 8.303l3.431 3.327c.22.213.22.527 0 .74l-6.63 6.43C8.79 19.201 8 18.958 8 18.43v-5.723z" />
          <path fill="currentColor" opacity=".5" d="M8 11.293V5.57c0-.528.79-.771 1.205-.37l2.481 2.406z" />
        </React.Fragment>
      );
    // Solar widget-bold-duotone — 2×2 grid mark used in the canvas header
    // in place of the old leading accent dot. Reads as "flow / project file".
    case "widget":
      return (
        <React.Fragment>
          <path fill="currentColor" opacity=".5" d="M2 6.5c0-2.121 0-3.182.659-3.841S4.379 2 6.5 2s3.182 0 3.841.659S11 4.379 11 6.5s0 3.182-.659 3.841S8.621 11 6.5 11s-3.182 0-3.841-.659S2 8.621 2 6.5m11 11c0-2.121 0-3.182.659-3.841S15.379 13 17.5 13s3.182 0 3.841.659S22 15.379 22 17.5s0 3.182-.659 3.841S19.621 22 17.5 22s-3.182 0-3.841-.659S13 19.621 13 17.5" />
          <path fill="currentColor" d="M2 17.5c0-2.121 0-3.182.659-3.841S4.379 13 6.5 13s3.182 0 3.841.659S11 15.379 11 17.5s0 3.182-.659 3.841S8.621 22 6.5 22s-3.182 0-3.841-.659S2 19.621 2 17.5m11-11c0-2.121 0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11s-3.182 0-3.841-.659S13 8.621 13 6.5" />
        </React.Fragment>
      );
    // ── Toolbar set — Solar Bold Duotone, sourced from iconify.
    case "tool-cursor":
      return (
        <React.Fragment>
          <path fill="currentColor" fillRule="evenodd" d="m11.433 16.464l1.203-1.202l2.626-2.626l1.202-1.203c1.232-1.23 1.847-1.846 1.702-2.508s-.963-.963-2.596-1.565l-5.45-2.007C6.861 4.152 5.232 3.55 4.392 4.39s-.24 2.47.962 5.73l2.006 5.45c.602 1.633.903 2.45 1.565 2.596s1.277-.47 2.508-1.702" clipRule="evenodd" />
          <path fill="currentColor" opacity=".5" d="m12.636 15.262l3.938 3.938c.408.408.612.612.84.706c.302.126.643.126.946 0c.228-.094.432-.298.84-.706c.407-.408.611-.612.706-.84a1.24 1.24 0 0 0 0-.946c-.095-.228-.299-.432-.706-.84l-3.939-3.938z" />
        </React.Fragment>
      );
    case "tool-frame":
      return (
        <React.Fragment>
          <path fill="currentColor" fillRule="evenodd" d="M2 6.634a4.634 4.634 0 1 1 9.268 0a4.634 4.634 0 0 1-9.268 0" clipRule="evenodd" />
          <path fill="currentColor" fillRule="evenodd" opacity=".5" d="M12.732 17.366a4.634 4.634 0 1 1 9.269 0a4.634 4.634 0 0 1-9.269 0" clipRule="evenodd" />
          <path fill="currentColor" d="M2 17.5c0-2.121 0-3.182.659-3.841S4.379 13 6.5 13s3.182 0 3.841.659S11 15.379 11 17.5s0 3.182-.659 3.841S8.621 22 6.5 22s-3.182 0-3.841-.659S2 19.621 2 17.5" />
          <path fill="currentColor" opacity=".5" d="M13 6.5c0-2.121 0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11s-3.182 0-3.841-.659S13 8.621 13 6.5" />
        </React.Fragment>
      );
    case "tool-pen":
      return (
        <React.Fragment>
          <path fill="currentColor" opacity=".5" d="M1 12c0-5.185 0-7.778 1.61-9.39C4.223 1 6.816 1 12 1s7.778 0 9.39 1.61C23 4.223 23 6.816 23 12s0 7.778-1.61 9.39C19.777 23 17.184 23 12 23s-7.778 0-9.39-1.61C1 19.777 1 17.184 1 12" />
          <path fill="currentColor" d="M13.926 14.302c.245-.191.467-.413.912-.858l5.54-5.54c.134-.134.073-.365-.106-.427a6.1 6.1 0 0 1-2.3-1.449a6.1 6.1 0 0 1-1.45-2.3c-.061-.18-.292-.24-.426-.106l-5.54 5.54c-.445.444-.667.667-.858.912a5 5 0 0 0-.577.932c-.133.28-.233.579-.431 1.175l-.257.77l-.409 1.226l-.382 1.148a.817.817 0 0 0 1.032 1.033l1.15-.383l1.224-.408l.77-.257c.597-.199.895-.298 1.175-.432q.498-.237.933-.576m8.187-8.132a3.028 3.028 0 0 0-4.282-4.283l-.179.178a.73.73 0 0 0-.206.651c.027.15.077.37.168.633a4.9 4.9 0 0 0 1.174 1.863a4.9 4.9 0 0 0 1.862 1.174c.263.09.483.141.633.168c.24.043.48-.035.652-.207z" />
        </React.Fragment>
      );
    case "tool-text":
      return (
        <React.Fragment>
          <path fill="currentColor" opacity=".5" d="M2 12c0-4.714 0-7.071 1.464-8.536C4.93 2 7.286 2 12 2s7.071 0 8.535 1.464C22 4.93 22 7.286 22 12s0 7.071-1.465 8.535C19.072 22 16.714 22 12 22s-7.071 0-8.536-1.465C2 19.072 2 16.714 2 12" />
          <path fill="currentColor" d="M9.952 6.25c-.43 0-.832 0-1.16.049c-.371.055-.752.186-1.057.525c-.294.327-.398.717-.443 1.089c-.042.348-.042.78-.042 1.267v.57a.75.75 0 0 0 1.5 0v-.528c0-.543.001-.882.031-1.129a1 1 0 0 1 .046-.22a.1.1 0 0 1 .023-.046h.001q0-.002.011-.008a.6.6 0 0 1 .152-.037c.204-.03.491-.032.986-.032h1.25v8.5H9.5a.75.75 0 0 0 0 1.5H15a.75.75 0 0 0 0-1.5h-2.25v-8.5H14c.495 0 .782.002.986.032c.092.014.135.03.152.037l.011.007v.001a.1.1 0 0 1 .024.045c.014.038.032.105.046.221c.03.247.031.586.031 1.13v.527a.75.75 0 0 0 1.5 0v-.57c0-.488 0-.919-.042-1.267c-.045-.372-.149-.762-.443-1.09c-.305-.338-.686-.469-1.057-.524c-.328-.05-.73-.05-1.16-.049z" />
        </React.Fragment>
      );
    case "tool-comment":
      return (
        <React.Fragment>
          <path fill="currentColor" opacity=".5" d="M12 23c6.075 0 11-4.925 11-11S18.075 1 12 1S1 5.925 1 12c0 1.76.413 3.423 1.148 4.898c.195.392.26.84.147 1.263l-.655 2.448a1.43 1.43 0 0 0 1.75 1.751l2.45-.655a1.8 1.8 0 0 1 1.262.147A10.96 10.96 0 0 0 12 23" />
          <path fill="currentColor" d="M10.9 12a1.1 1.1 0 1 0 2.2 0a1.1 1.1 0 0 0-2.2 0m-4.4 0a1.1 1.1 0 1 0 2.2 0a1.1 1.1 0 0 0-2.2 0m8.8 0a1.1 1.1 0 1 0 2.2 0a1.1 1.1 0 0 0-2.2 0" />
        </React.Fragment>
      );
    case "tool-clip":
      return (
        <React.Fragment>
          <path fill="currentColor" fillRule="evenodd" d="M11.244 1.955c1.7-.94 3.79-.94 5.49 0c.63.348 1.218.91 2.172 1.825l.094.09a.75.75 0 1 1-1.037 1.083c-1.079-1.032-1.518-1.444-1.954-1.685a4.2 4.2 0 0 0-4.04 0c-.436.24-.875.653-1.953 1.685l-5.99 5.735A.75.75 0 0 1 2.99 9.605L8.98 3.87l.093-.09c.955-.914 1.543-1.477 2.172-1.825m3.701 4.805a.75.75 0 0 1 1.06-.023l.081.078c.367.35.683.651.86 1.003a2.21 2.21 0 0 1 0 1.994c-.177.352-.493.653-.86 1.004l-.08.077l-7.38 7.066a.75.75 0 0 1-1.038-1.083l7.38-7.067c.495-.473.594-.583.638-.671a.71.71 0 0 0 0-.646c-.044-.088-.143-.198-.638-.671a.75.75 0 0 1-.023-1.06" clipRule="evenodd" />
          <path fill="currentColor" opacity=".5" d="M17.963 4.954c1.08 1.034 1.507 1.452 1.756 1.865a3.65 3.65 0 0 1 0 3.788c-.249.413-.676.831-1.756 1.866L10.53 19.59c-.56.535-.945.903-1.269 1.164c-.316.255-.523.365-.707.418a2 2 0 0 1-1.108 0c-.184-.053-.391-.163-.707-.418c-.324-.261-.71-.63-1.269-1.164c-.558-.535-.943-.904-1.215-1.214c-.267-.303-.376-.495-.428-.659a1.7 1.7 0 0 1 0-1.009c.052-.163.16-.355.428-.658c.272-.31.657-.679 1.215-1.214l7.327-7.015c.492-.471.61-.57.71-.616a.9.9 0 0 1 .75 0c.101.046.22.145.711.616a.75.75 0 0 1 1.02-1.1l-.06-.058c-.37-.355-.682-.654-1.042-.82a2.4 2.4 0 0 0-2.007 0c-.36.166-.672.465-1.041.82l-7.429 7.113c-.529.506-.96.92-1.28 1.283c-.33.376-.592.752-.733 1.2a3.2 3.2 0 0 0 0 1.907c.14.449.402.825.733 1.2c.32.365.751.778 1.28 1.284l.048.046c.529.507.96.92 1.34 1.226c.393.317.78.561 1.234.692a3.5 3.5 0 0 0 1.937 0c.455-.13.842-.375 1.235-.692c.38-.306.81-.72 1.34-1.226l7.555-7.234c.95-.91 1.54-1.474 1.906-2.08a5.14 5.14 0 0 0 0-5.337c-.366-.607-.955-1.171-1.906-2.081l-.08-.077a.75.75 0 0 1-1.055 1.067" />
        </React.Fragment>
      );
    default:
      return null;
  }
}

// Icon glyph as an inline SVG <g>; expects to be placed inside a parent SVG.
// Scales 24×24 source to size px, translated so its top-left lands at (x,y).
function LiveIconG({ kind, x, y, size = 24, className = "hero-live-icon-g" }) {
  const k = size / 24;
  return (
    <g transform={`translate(${x} ${y}) scale(${k})`} className={className}>
      <LiveNodeIcon kind={kind} />
    </g>
  );
}

// Inline I/O endpoint — 28px disc + 8px tick stub + canvas-typeset label.
// The disc IS the port; the tick visually bridges disc edge to bezier rail.
// Label sits OUTSIDE the disc on the side away from the rail, typeset on
// the canvas (not inside a card) so the topology reads as wired terminals,
// not flow-tool node cards.
function LiveTopoNode({ x, y, icon, label, role, side, idx = 0 }) {
  const R = 14;
  const TICK = 8;
  const isLeft = side === "left";
  const tickStart = isLeft ? x + R : x - R;
  const tickEnd   = isLeft ? x + R + TICK : x - R - TICK;
  const labelW = 64;
  const labelBoxX = isLeft ? x - R - 8 - labelW : x + R + 8;
  return (
    <g
      className={`hero-live-node-g ${isLeft ? "is-src" : "is-sink"}`}
      style={{ "--i": idx }}
    >
      <line
        x1={tickStart} y1={y} x2={tickEnd} y2={y}
        className="hero-live-node-tick"
      />
      <circle cx={x} cy={y} r={R} className="hero-live-node-disc" />
      <LiveIconG kind={icon} x={x - 8} y={y - 8} size={16} />
      <foreignObject x={labelBoxX} y={y - 14} width={labelW} height={28}>
        <div
          xmlns="http://www.w3.org/1999/xhtml"
          className={`hero-live-node-label ${isLeft ? "align-r" : "align-l"}`}
        >
          <div className="hero-live-node-name">{label}</div>
          <div className="hero-live-node-role">{role}</div>
        </div>
      </foreignObject>
    </g>
  );
}

// Compact icon used inside rail rows + inspector chips. 14px default.
function LiveTinyIcon({ kind, size = 14, className = "hero-live-tiny" }) {
  return (
    <svg
      viewBox="0 0 24 24"
      width={size}
      height={size}
      className={className}
      aria-hidden="true"
    >
      <LiveNodeIcon kind={kind} />
    </svg>
  );
}

// ── Left rail — workspace tree with one selected flow + integrations list.
function LiveSidebar({ flows, integrations }) {
  // Three tabs across the top — like Framer's Pages · Layers · Assets.
  // Active is "Layers" so the tree below reads as the layers panel.
  const tabs = [
    { id: "pages",  label: "Pages"  },
    { id: "layers", label: "Layers" },
    { id: "assets", label: "Assets" },
  ];
  const activeTabIdx = 1;
  return (
    <aside className="hero-live-rail" aria-hidden="true">
      <div className="hero-live-rail-tabs" role="tablist">
        {tabs.map((t, i) => (
          <span
            key={t.id}
            className={`hero-live-rail-tab${i === activeTabIdx ? " is-active" : ""}`}
          >
            {t.label}
          </span>
        ))}
      </div>

      <div className="hero-live-rail-head">
        <span className="hero-live-rail-crumb">
          <LiveTinyIcon kind="folder" size={12} />
          <span>wayrelay</span>
          <span className="hero-live-rail-crumb-sep">/</span>
          <span className="hero-live-rail-crumb-cur">intake</span>
        </span>
      </div>

      <div className="hero-live-rail-search">
        <LiveTinyIcon kind="search" size={12} className="hero-live-rail-search-icon" />
        <span className="hero-live-rail-search-text">Search flows…</span>
        <span className="hero-live-rail-search-kbd">⌘K</span>
      </div>

      <div className="hero-live-rail-section">
        <div className="hero-live-rail-section-h">
          <span className="hero-live-rail-section-chev">▾</span>
          <span>FLOWS</span>
          <span className="hero-live-rail-section-count">{flows.length}</span>
        </div>
        <ul className="hero-live-rail-list">
          {flows.map((f, i) => (
            <li
              key={f.id}
              className={`hero-live-rail-row${f.selected ? " is-selected" : ""}`}
            >
              <span className="hero-live-rail-row-tw" aria-hidden="true">
                {f.selected ? "▾" : "▸"}
              </span>
              <span className="hero-live-rail-row-label">{f.label}</span>
              <span className={`hero-live-rail-row-state is-${f.state}`}>{f.state}</span>
            </li>
          ))}
        </ul>
      </div>

      <div className="hero-live-rail-divider" />

      <div className="hero-live-rail-section">
        <div className="hero-live-rail-section-h">
          <span className="hero-live-rail-section-chev">▾</span>
          <span>INTEGRATIONS</span>
          <span className="hero-live-rail-section-count">{integrations.length}</span>
        </div>
        <ul className="hero-live-rail-list hero-live-rail-list-ints">
          {integrations.map((it) => (
            <li key={it.label} className="hero-live-rail-int">
              <span className="hero-live-rail-int-icon">
                <LiveTinyIcon kind={it.icon} size={14} />
              </span>
              <span className="hero-live-rail-int-label">{it.label}</span>
              <span className="hero-live-rail-int-chev" aria-hidden="true">
                <LiveTinyIcon kind="chev-right" size={10} />
              </span>
            </li>
          ))}
        </ul>
      </div>
    </aside>
  );
}

// ── Centre canvas — flow header strip + topology + classifier panel.
// The centre is no longer a small junction tile. It's a live classifier
// output panel that streams the most recent routing decisions — the rails
// feed into its left edge and exit from its right edge.
function LiveCanvas({ decisions }) {
  const SRC_X  = 86;
  const SINK_X = 374;
  // Classifier panel — flat filled card centered on the canvas. Width/height
  // chosen so the panel reads as a real product surface, not a "node".
  const PANEL = { cx: 230, cy: 130, w: 196, h: 132 };
  const PANEL_L = PANEL.cx - PANEL.w / 2;
  const PANEL_R = PANEL.cx + PANEL.w / 2;
  const PANEL_T = PANEL.cy - PANEL.h / 2;

  // Each lane: source row Y → panel left edge → fan-out from panel right
  // edge → sink row Y. Rails arrive at three distinct Y positions on the
  // panel's left edge so the visual cadence reads (top/mid/bottom lane).
  const PANEL_Y_OFFSETS = [-PANEL.h * 0.32, 0, PANEL.h * 0.32];

  const lanes = LIVE_TOPO_SOURCES.map((s, i) => {
    const yS = s.y;
    const yD = LIVE_TOPO_SINKS[i].y;
    const yPL = PANEL.cy + PANEL_Y_OFFSETS[i];
    const yPR = PANEL.cy + PANEL_Y_OFFSETS[i];
    // Rails attach at the end of each node's 8px tick stub (disc edge + tick).
    // Disc r=14, tick=8 → attach at ±22 from node center.
    const xStart = SRC_X + 22;
    const xEnd   = SINK_X - 22;
    return {
      key: s.tag,
      inPath:  `M ${xStart} ${yS} C ${xStart + 60} ${yS}, ${PANEL_L - 40} ${yPL}, ${PANEL_L} ${yPL}`,
      outPath: `M ${PANEL_R} ${yPR} C ${PANEL_R + 40} ${yPR}, ${xEnd - 60} ${yD}, ${xEnd} ${yD}`,
    };
  });

  // Toolbar uses Solar Bold Duotone (same family as the node icons). Order +
  // semantic mapping: select → frame → pen → text → comment → attach.
  const tools = [
    { k: "tool-cursor",  label: "Select",  active: true  },
    { k: "tool-frame",   label: "Frame",   active: false },
    { k: "tool-pen",     label: "Pen",     active: false },
    { k: "tool-text",    label: "Text",    active: false },
    { k: "tool-comment", label: "Comment", active: false },
    { k: "tool-clip",    label: "Attach",  active: false },
  ];

  return (
    <div className="hero-live-canvas" aria-hidden="true">
      <div className="hero-live-canvas-head">
        <div className="hero-live-canvas-head-l">
          <span className="hero-live-canvas-live">
            <span className="hero-live-canvas-live-icon" aria-hidden="true">
              <LiveTinyIcon kind="widget" size={14} />
            </span>
            <span>order_intake.flow</span>
          </span>
          <span className="hero-live-canvas-sep" />
          <span className="hero-live-canvas-meta">v 0.7.3 · main</span>
        </div>
        <div className="hero-live-canvas-head-r">
          <span className="hero-live-canvas-btn">
            <LiveTinyIcon kind="play" size={11} />
            <span>Run</span>
          </span>
          <span className="hero-live-canvas-btn">
            <span>Save</span>
            <span className="hero-live-canvas-btn-kbd">⌘S</span>
          </span>
        </div>
      </div>

      <div className="hero-live-canvas-tools">
        <div className="hero-live-canvas-tools-group">
          {tools.map((t) => (
            <span
              key={t.k}
              className={`hero-live-canvas-tool${t.active ? " is-active" : ""}`}
              title={t.label}
            >
              <LiveTinyIcon kind={t.k} size={14} />
            </span>
          ))}
        </div>
        <span className="hero-live-canvas-tools-sep" />
        <span className="hero-live-canvas-breadcrumb">
          <span className="hero-live-canvas-breadcrumb-seg">Frame 1</span>
          <span className="hero-live-canvas-breadcrumb-sep">/</span>
          <span className="hero-live-canvas-breadcrumb-seg is-active">Junction</span>
        </span>
      </div>

      <div className="hero-live-canvas-stage">
        <svg
          viewBox="0 0 460 260"
          className="hero-live-topo"
          preserveAspectRatio="xMidYMid meet"
        >
          {lanes.map((l) => (
            <React.Fragment key={`bg-${l.key}`}>
              <path d={l.inPath}  className="hero-live-rail-path" />
              <path d={l.outPath} className="hero-live-rail-path" />
            </React.Fragment>
          ))}


          {/* Classifier panel — flat recessed card containing the most recent
              routing decisions as a mono log. No accent ring, no halo, no
              drop-shadow. Reads as a real product surface: "router.classify"
              header strip + 4 streaming decision rows. Rails arrive at the
              left edge and exit from the right edge so the panel reads as
              the actual destination of source traffic. */}
          <foreignObject
            x={PANEL_L}
            y={PANEL_T}
            width={PANEL.w}
            height={PANEL.h}
            className="hero-live-classifier-fo"
          >
            <div xmlns="http://www.w3.org/1999/xhtml" className="hero-live-classifier">
              <div className="hero-live-classifier-h">
                <span className="hero-live-classifier-h-l">
                  <span className="hero-live-classifier-h-k">router.classify</span>
                  <span className="hero-live-classifier-h-sep">·</span>
                  <span className="hero-live-classifier-h-s">streaming</span>
                </span>
                <span className="hero-live-classifier-h-pulse" aria-hidden="true" />
              </div>
              <ol className="hero-live-classifier-list">
                {decisions.slice(-4).reverse().map((d, i) => (
                  <li
                    key={d.id}
                    className={`hero-live-classifier-row${i === 0 ? " is-new" : ""}`}
                  >
                    <span className="hero-live-classifier-ts">{d.ts}</span>
                    <span className="hero-live-classifier-src">{d.src}</span>
                    <span className="hero-live-classifier-arrow">→</span>
                    <span className="hero-live-classifier-sink">{d.sink}</span>
                    <span className="hero-live-classifier-conf">{d.conf}</span>
                    <span className="hero-live-classifier-ok">OK</span>
                  </li>
                ))}
              </ol>
            </div>
          </foreignObject>

          {LIVE_TOPO_SOURCES.map((n, i) => (
            <LiveTopoNode key={`s-${n.tag}`} x={SRC_X}  y={n.y} side="left"  idx={i} {...n} />
          ))}
          {LIVE_TOPO_SINKS.map((n, i) => (
            <LiveTopoNode key={`d-${n.tag}`} x={SINK_X} y={n.y} side="right" idx={i} {...n} />
          ))}
        </svg>

        {/* Floating inline preview cards — small dashboard tile (metric +
            sparkline) and a tiny code block. They sit absolutely over the
            canvas, hinting at "this is what we deliver" without competing
            with the flow. */}
        <div className="hero-live-preview hero-live-preview-metric">
          <div className="hero-live-preview-h">
            <span className="hero-live-preview-k">EVENTS / 24H</span>
            <span className="hero-live-preview-trend">+12.4%</span>
          </div>
          <div className="hero-live-preview-v">1,284</div>
          <svg
            viewBox="0 0 120 28"
            className="hero-live-preview-spark"
            preserveAspectRatio="none"
          >
            <path
              d="M0 22 L12 18 L24 20 L36 14 L48 16 L60 10 L72 12 L84 7 L96 9 L108 5 L120 8"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.4"
              strokeLinecap="round"
              strokeLinejoin="round"
            />
            <path
              d="M0 22 L12 18 L24 20 L36 14 L48 16 L60 10 L72 12 L84 7 L96 9 L108 5 L120 8 L120 28 L0 28 Z"
              fill="currentColor"
              opacity=".12"
            />
          </svg>
        </div>

        {/* Zoom indicator — segmented pill centered along the canvas bottom. */}
        <div className="hero-live-canvas-zoom">
          <span className="hero-live-canvas-zoom-btn">−</span>
          <span className="hero-live-canvas-zoom-v">100%</span>
          <span className="hero-live-canvas-zoom-btn">+</span>
          <span className="hero-live-canvas-zoom-sep" />
          <span className="hero-live-canvas-zoom-fit">Fit</span>
        </div>

        {/* Collaborator cursor — small label that drifts gently. */}
        <span className="hero-live-cursor" aria-hidden="true">
          <svg
            viewBox="0 0 16 16"
            width="11"
            height="11"
            className="hero-live-cursor-arrow"
          >
            <path
              d="M1.5 1.5 L1.5 12.5 L4.7 9.6 L6.6 14 L8.7 13.1 L6.8 8.6 L11 8.6 Z"
              fill="currentColor"
            />
          </svg>
          <span className="hero-live-cursor-name">Mira</span>
        </span>
      </div>
    </div>
  );
}

// ── Right inspector — properties for the selected node + events.tail.
// Small reusable "real input" — value box with a hairline border and a
// trailing unit label (s, %, px, L). Mirrors Framer's inspector inputs.
function LiveInspInput({ value, unit, mono = true, accent = false }) {
  return (
    <span className={`hero-live-insp-input${accent ? " is-accent" : ""}`}>
      <span className={`hero-live-insp-input-v${mono ? "" : " is-sans"}`}>
        {value}
      </span>
      {unit && <span className="hero-live-insp-input-u">{unit}</span>}
    </span>
  );
}

function LiveInspector({ events, count }) {
  const counter = String(count).padStart(4, "0");
  return (
    <aside className="hero-live-inspector" aria-hidden="true">
      <div className="hero-live-insp-head">
        <span className="hero-live-insp-title">Junction · LLM router</span>
        <span className="hero-live-insp-dots" aria-hidden="true">
          <span /><span /><span />
        </span>
      </div>

      <div className="hero-live-insp-tabs">
        <span className="hero-live-insp-tab is-active">Properties</span>
        <span className="hero-live-insp-tab">Events</span>
        <span className="hero-live-insp-tab">Logs</span>
      </div>

      <div className="hero-live-insp-section">
        <div className="hero-live-insp-section-h">
          <span className="hero-live-insp-section-chev">▾</span>
          <span>Trigger</span>
        </div>
        <div className="hero-live-insp-row">
          <span className="hero-live-insp-k">Type</span>
          <LiveInspInput value="IMAP poll" mono={false} />
        </div>
        <div className="hero-live-insp-row">
          <span className="hero-live-insp-k">Cadence</span>
          <LiveInspInput value="60" unit="s" />
        </div>
        <div className="hero-live-insp-row">
          <span className="hero-live-insp-k">Mailbox</span>
          <LiveInspInput value="support@" mono={false} />
        </div>
      </div>

      <div className="hero-live-insp-section">
        <div className="hero-live-insp-section-h">
          <span className="hero-live-insp-section-chev">▾</span>
          <span>Routing</span>
        </div>
        <div className="hero-live-insp-row">
          <span className="hero-live-insp-k">Strategy</span>
          <LiveInspInput value="LLM classifier" mono={false} />
        </div>
        <div className="hero-live-insp-row">
          <span className="hero-live-insp-k">Temp</span>
          <LiveInspInput value="0.20" />
        </div>
        <div className="hero-live-insp-row">
          <span className="hero-live-insp-k">Threshold</span>
          <LiveInspInput value="70" unit="%" />
        </div>
        <div className="hero-live-insp-row">
          <span className="hero-live-insp-k">Confidence</span>
          <LiveInspInput value="0.82" unit="L" accent />
        </div>
      </div>

      <div className="hero-live-insp-section">
        <div className="hero-live-insp-section-h">
          <span className="hero-live-insp-section-chev">▾</span>
          <span>Output</span>
        </div>
        <div className="hero-live-insp-chips">
          <span className="hero-live-insp-chip">→ CRM</span>
          <span className="hero-live-insp-chip">→ ERP</span>
          <span className="hero-live-insp-chip">→ Slack</span>
        </div>
      </div>

      <div className="hero-live-insp-divider" />

      <div className="hero-live-insp-tail">
        <div className="hero-live-insp-tail-h">
          <span>events.tail —f</span>
          <span className="hero-live-insp-tail-count">{counter}</span>
        </div>
        <ol className="hero-live-console-list">
          {events.map((e, i) => (
            <li
              key={e.id}
              className={`hero-live-row${i === events.length - 1 ? " is-new" : ""}`}
            >
              <span className="hero-live-row-ts">{e.ts}</span>
              <span className="hero-live-row-src">{e.src}</span>
              <span className="hero-live-row-arrow">→</span>
              <span className="hero-live-row-sink">{e.sink}</span>
              <span className="hero-live-row-text">{e.text}</span>
            </li>
          ))}
        </ol>
      </div>
    </aside>
  );
}

function LiveDemo() {
  // ~1800ms beat drives all motion + console feed + classifier panel.
  const [events, setEvents] = __h_useState([]);
  const [decisions, setDecisions] = __h_useState([]);
  const [count, setCount] = __h_useState(0);

  __h_useEffect(() => {
    let seq = 0;
    const weights = [
      { k: "WEB", w: 5 },
      { k: "EML", w: 3 },
      { k: "TG",  w: 2 },
    ];
    const wsum = weights.reduce((a, b) => a + b.w, 0);
    const pick = () => {
      let r = Math.random() * wsum;
      for (const x of weights) { if ((r -= x.w) < 0) return x.k; }
      return "WEB";
    };
    const tick = () => {
      const src = pick();
      const pool = LIVE_PAYLOADS[src];
      const item = pool[seq % pool.length];
      seq += 1;
      const id = Date.now() + "-" + seq;
      const t = new Date();
      const hh = String(t.getHours()).padStart(2, "0");
      const mm = String(t.getMinutes()).padStart(2, "0");
      const ss = String(t.getSeconds()).padStart(2, "0");
      const ms = String(t.getMilliseconds()).padStart(3, "0").slice(0, 2);
      // Classifier confidence — 0.78–0.97 range with two decimals.
      const conf = (0.78 + Math.random() * 0.19).toFixed(2);
      const ts = `${hh}:${mm}:${ss}.${ms}`;
      setEvents((es) => [...es.slice(-4), {
        id,
        src,
        sink: item.sink,
        text: item.text,
        ts,
      }]);
      setDecisions((ds) => [...ds.slice(-4), {
        id,
        src,
        sink: item.sink,
        conf,
        ts,
      }]);
      setCount((c) => c + 1);
    };
    tick();
    const id = setInterval(tick, 1800);
    return () => clearInterval(id);
  }, []);

  const counter = String(count).padStart(4, "0");

  return (
    <div className="hero-live-frame" aria-hidden="true">
      <div className="hero-live-kicker">
        <span className="hero-live-kicker-id">intake.flow</span>
        <span className="hero-live-kicker-dot">·</span>
        <span className="hero-live-kicker-state">live</span>
        <span className="hero-live-kicker-stats">
          <span className="hero-live-kicker-stat">{counter} <em>EVENTS</em></span>
          <span className="hero-live-kicker-dot">·</span>
          <span className="hero-live-kicker-stat">0000 <em>ERR</em></span>
        </span>
      </div>

      <div className="hero-live-slab">
        <LiveSidebar flows={LIVE_RAIL_FLOWS} integrations={LIVE_RAIL_INTEGRATIONS} />
        <LiveCanvas decisions={decisions} />
        <LiveInspector events={events} count={count} />
      </div>
    </div>
  );
}

function HeroSplit({ s }) {
  return (
    <div className="wrap hero-inner hero-inner-split">
      <div className="hero-split-left">
        <Reveal className="hero-brand hero-brand-split" as="div">
          <BrandIcon height={24} />
        </Reveal>
        <Reveal delay={1} as="h1" className="hero-title hero-title-split">
          <span>{s.hero.titlePre}</span>
          <br />
          <Rotator items={s.hero.audiences} />
        </Reveal>
        <Reveal delay={2} className="hero-sub" as="p">
          {s.hero.sub}
        </Reveal>
        <Reveal delay={3} className="hero-cta">
          <a href="#contact" className="btn btn-primary btn-lg">
            {s.hero.ctaPrimary} <span className="arrow">→</span>
          </a>
          <a href="#cases" className="btn btn-ghost btn-lg">
            {s.hero.ctaSecondary}
          </a>
        </Reveal>
      </div>

      <Reveal delay={2} className="hero-split-right" as="div">
        <HeroDiagram s={s} />
      </Reveal>
    </div>
  );
}

function HeroDiagram({ s }) {
  return (
    <div className="hero-diagram card">
      <div className="hero-diagram-head">
        <span className="dot dot-r" /><span className="dot dot-y" /><span className="dot dot-g" />
        <span className="hero-diagram-title">{s.hero.diagramHead}</span>
      </div>
      <svg viewBox="0 0 360 280" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
        <defs>
          <marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
            <path d="M0 0 L10 5 L0 10 Z" fill="var(--accent)" />
          </marker>
        </defs>
        {[
          { x: 30,  y: 40,  w: 96, h: 32, label: "Email" },
          { x: 30,  y: 124, w: 96, h: 32, label: "Webhook" },
          { x: 30,  y: 208, w: 96, h: 32, label: "Telegram" },
          { x: 160, y: 124, w: 92, h: 36, label: "AI Router", on: true },
          { x: 286, y: 40,  w: 64, h: 32, label: "CRM" },
          { x: 286, y: 124, w: 64, h: 32, label: "ERP" },
          { x: 286, y: 208, w: 64, h: 32, label: "Slack" },
        ].map((n, i) => (
          <g key={i}>
            <rect x={n.x} y={n.y} width={n.w} height={n.h} rx="6"
              fill="var(--bg-2)"
              stroke={n.on ? "var(--accent)" : "var(--line-strong)"}
              strokeWidth={n.on ? 1.4 : 1} />
            <text x={n.x + 10} y={n.y + n.h / 2 + 4} fontFamily="var(--font-mono)" fontSize="11" fill="var(--fg)">
              {n.label}
            </text>
            <circle cx={n.x + n.w - 9} cy={n.y + n.h / 2} r="3" fill={n.on ? "var(--accent)" : "var(--fg-4)"}>
              {n.on && <animate attributeName="opacity" values="0.3;1;0.3" dur="1.6s" repeatCount="indefinite" />}
            </circle>
          </g>
        ))}
        {[
          ["M126,56 C 145,56 145,142 158,142"],
          ["M126,140 L158,140"],
          ["M126,224 C 145,224 145,144 158,144"],
          ["M252,142 C 270,142 270,56 284,56"],
          ["M252,142 L284,140"],
          ["M252,144 C 270,144 270,224 284,224"],
        ].map((d, i) => (
          <path key={i} d={d[0]} fill="none" stroke="var(--accent)" strokeOpacity="0.55"
            strokeWidth="1.2" markerEnd="url(#ar)" />
        ))}
      </svg>
      <div className="hero-diagram-foot">
        <span className="tag">n8n</span>
        <span className="tag">openai</span>
        <span className="tag">postgres</span>
        <span className="tag">+8</span>
      </div>
    </div>
  );
}

window.Hero = Hero;
