/* .v1 — платформа разметки мультимодальных данных для VLA / гуманоидов — dark theme */

const { useState, useEffect, useRef } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#EA0A23",
  "cursor": true,
  "loader": true
} /*EDITMODE-END*/;

/* ============== HOOKS ============== */
function useReveal(threshold = 0.15) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {if (e.isIntersecting) {el.classList.add("in");io.disconnect();}});
    }, { threshold });
    io.observe(el);
    return () => io.disconnect();
  }, [threshold]);
  return ref;
}
function useScrollY() {
  const [y, setY] = useState(0);
  useEffect(() => {
    const on = () => setY(window.scrollY);
    on();window.addEventListener("scroll", on, { passive: true });
    return () => window.removeEventListener("scroll", on);
  }, []);
  return y;
}
function useClock() {
  const [t, setT] = useState("");
  useEffect(() => {
    const fmt = () => {
      try {setT(new Intl.DateTimeFormat("ru-RU", { hour: "2-digit", minute: "2-digit", timeZone: "Europe/Moscow", hour12: false }).format(new Date()));}
      catch {setT("");}
    };
    fmt();const id = setInterval(fmt, 30000);
    return () => clearInterval(id);
  }, []);
  return t;
}

/* ============== PRIMITIVES ============== */
function getText(node) {
  if (node == null || typeof node === "boolean") return "";
  if (typeof node === "string" || typeof node === "number") return String(node);
  if (Array.isArray(node)) return node.map(getText).join("");
  if (React.isValidElement(node)) return getText(node.props.children);
  return "";
}
function Words({ children, delay = 0 }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;if (!el) return;
    const words = el.querySelectorAll(".word");
    const fire = () => words.forEach((w, i) => setTimeout(() => w.classList.add("in"), delay + i * 70));
    const r = el.getBoundingClientRect();
    if (r.top < innerHeight && r.bottom > 0) {fire();} else
    {
      const io = new IntersectionObserver((entries) => {
        entries.forEach((e) => {if (e.isIntersecting) {fire();io.disconnect();}});
      }, { threshold: 0 });
      io.observe(el);
      // safety: force-show after 2.5s no matter what
      const safety = setTimeout(() => {words.forEach((w) => w.classList.add("in"));io.disconnect();}, 2500);
      return () => {io.disconnect();clearTimeout(safety);};
    }
    // safety for in-viewport too
    const safety = setTimeout(() => words.forEach((w) => w.classList.add("in")), 2500);
    return () => clearTimeout(safety);
  }, [delay]);
  const parts = getText(children).split(" ");
  return (
    <span ref={ref} style={{ display: "inline" }}>
      {parts.map((w, i) =>
      <React.Fragment key={i}>
          <span className="word"><span>{w}</span></span>
          {i < parts.length - 1 ? " " : ""}
        </React.Fragment>
      )}
    </span>);

}

function SectionNum({ label, num, total = "07" }) {
  return (
    <div className="sec-num">
      <span className="label">{label}</span>
      <span className="sep" />
      <span className="num">{num}</span>
      <span className="total"> | {total}</span>
    </div>);

}

function Bracket({ children, accent = false }) {
  return (
    <span className="brackets" style={accent ? { color: "var(--accent)" } : {}}>
      <span style={{ display: "inline-block" }}>[</span>
      {children}
      <span style={{ display: "inline-block" }}>]</span>
    </span>);

}

/* ============== TYPEWRITER LINES — types each bracket word in sequence, then loops ============== */
function TypeLines({ words }) {
  const [shown, setShown] = useState(() => words.map(() => ""));
  const [revealedMax, setRevealedMax] = useState(0);
  const [caretLine, setCaretLine] = useState(0);
  useEffect(() => {
    let timer,mounted = true;
    let line = 0,pos = 0;
    const typeChar = () => {
      if (!mounted) return;
      const full = words[line];
      pos++;
      setShown((s) => {const n = [...s];n[line] = full.slice(0, pos);return n;});
      if (pos >= full.length) {
        if (line < words.length - 1) {
          // brief pause, then drop to next line and keep typing
          timer = setTimeout(() => {
            if (!mounted) return;
            line++;pos = 0;
            setRevealedMax(line);setCaretLine(line);
            typeChar();
          }, 440);
        } else {
          setCaretLine(-1); // all three typed — hold, hide caret
          timer = setTimeout(restart, 2400);
        }
      } else {
        timer = setTimeout(typeChar, 78 + Math.random() * 72);
      }
    };
    const restart = () => {
      if (!mounted) return;
      line = 0;pos = 0;
      setShown(words.map(() => ""));
      setRevealedMax(0);setCaretLine(0);
      timer = setTimeout(typeChar, 460);
    };
    timer = setTimeout(typeChar, 700);
    return () => {mounted = false;clearTimeout(timer);};
  }, []);
  return words.map((w, i) =>
  <React.Fragment key={i}>
      <span className="brackets" style={{ color: "var(--accent)", opacity: i <= revealedMax ? 1 : 0, transition: "opacity .22s ease" }}>
        <span style={{ display: "inline-block" }}>[</span>
        <span>{shown[i]}</span>
        {caretLine === i ? <span className="tw-caret" aria-hidden="true" /> : null}
        <span style={{ display: "inline-block" }}>]</span>
      </span>
      {i < words.length - 1 ? <br /> : null}
    </React.Fragment>
  );
}

/* ============== DECORATIVE PRIMITIVES (generic graphic techniques) ============== */
function HalftoneRing({ size = 260, color = "rgba(255,255,255,0.95)" }) {
  const cx = size / 2,cy = size / 2;
  const rings = [
  { r: size * 0.20, n: 12, d: 1.6 },
  { r: size * 0.27, n: 22, d: 1.9 },
  { r: size * 0.34, n: 32, d: 2.2 },
  { r: size * 0.40, n: 42, d: 2.4 },
  { r: size * 0.46, n: 28, d: 1.8 } // sparser outer
  ];
  const dots = [];
  rings.forEach((ring, ri) => {
    for (let i = 0; i < ring.n; i++) {
      const a = i / ring.n * Math.PI * 2 + ri * 0.12;
      dots.push(<circle key={`${ri}-${i}`} cx={cx + Math.cos(a) * ring.r} cy={cy + Math.sin(a) * ring.r} r={ring.d} fill={color} />);
    }
  });
  return <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>{dots}</svg>;
}

function DotCluster({ size = 90, color = "var(--accent)" }) {
  return (
    <svg width={size} height={size} viewBox="0 0 90 90">
      <circle cx="22" cy="22" r="13" fill={color} />
      <circle cx="62" cy="22" r="10" fill={color} />
      <circle cx="22" cy="62" r="10" fill={color} />
      <circle cx="64" cy="64" r="7" fill={color} />
    </svg>);

}

/* ============== HERO BACKDROP — dotted concentric orbits (programmatic SVG) ============== */
function HeroBackdrop({ size = 720 }) {
  const cx = size / 2,cy = size / 2;
  const rings = [];
  const ringCount = 28;
  for (let i = 0; i < ringCount; i++) {
    const t = i / (ringCount - 1);
    const r = 90 + t * (size * 0.48 - 90);
    const density = 1 - Math.pow(t, 1.4) * 0.85;
    const n = Math.max(8, Math.round(r * 0.38 * density));
    const dotR = 1.6 - t * 1.0;
    const opacity = 0.18 + (1 - t) * 0.42;
    rings.push({ r, n, dotR, opacity });
  }
  const dots = [];
  rings.forEach((ring, ri) => {
    const phase = ri * 0.13;
    for (let i = 0; i < ring.n; i++) {
      const a = i / ring.n * Math.PI * 2 + phase;
      dots.push(
        <circle
          key={`${ri}-${i}`}
          cx={cx + Math.cos(a) * ring.r}
          cy={cy + Math.sin(a) * ring.r}
          r={ring.dotR}
          fill="#0A0A0A"
          opacity={ring.opacity} />

      );
    }
  });
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ overflow: "visible" }}>
      <defs>
        <radialGradient id="v1-hero-glow" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.32" />
          <stop offset="38%" stopColor="var(--accent)" stopOpacity="0.10" />
          <stop offset="68%" stopColor="var(--accent)" stopOpacity="0" />
        </radialGradient>
      </defs>
      <circle cx={cx} cy={cy} r={size * 0.46} fill="url(#v1-hero-glow)" />
      {dots}
    </svg>);

}

/* ============== HERO SPHERE — canvas particle sphere ============== */
function HeroSphere({ size = 380 }) {
  const ref = useRef(null);
  useEffect(() => {
    const canvas = ref.current;if (!canvas) return;
    const dpr = Math.min(2, window.devicePixelRatio || 1);
    canvas.width = size * dpr;canvas.height = size * dpr;
    canvas.style.width = size + "px";canvas.style.height = size + "px";
    const ctx = canvas.getContext("2d");
    ctx.scale(dpr, dpr);

    // Fibonacci-sphere uniform distribution
    const N = 720;
    const pts = [];
    const golden = Math.PI * (3 - Math.sqrt(5));
    for (let i = 0; i < N; i++) {
      const y = 1 - i / (N - 1) * 2;
      const r = Math.sqrt(1 - y * y);
      const theta = golden * i;
      pts.push({
        x: Math.cos(theta) * r,
        y,
        z: Math.sin(theta) * r,
        accent: Math.random() < 0.045,
        sz: 0.7 + Math.random() * 0.6
      });
    }

    let mx = 0,my = 0,tx = 0,ty = 0;
    const onMove = (e) => {
      const rect = canvas.getBoundingClientRect();
      const ccx = rect.left + rect.width / 2;
      const ccy = rect.top + rect.height / 2;
      tx = (e.clientX - ccx) / window.innerWidth * 0.55;
      ty = (e.clientY - ccy) / window.innerHeight * 0.4;
    };
    window.addEventListener("pointermove", onMove);

    let raf,t = 0,stopped = false;
    const cx = size / 2,cy = size / 2;
    const R = size * 0.42;
    const accentRgb = (() => {
      const v = getComputedStyle(document.documentElement).getPropertyValue("--accent").trim() || "#EA0A23";
      const hex = v.replace("#", "");
      return [parseInt(hex.slice(0, 2), 16), parseInt(hex.slice(2, 4), 16), parseInt(hex.slice(4, 6), 16)];
    })();

    // ===== annotation bounding box (data-labeling motif) =====
    const classes = ["obj", "kp", "grasp", "edge", "node", "pose"];
    let targetIdx = 0,holdUntil = 0;
    let boxCX = cx,boxCY = cy,boxHS = 64,lockT = 0;
    let conf = 0.95,label = "obj 0.95";

    // ===== letter flock — "Базис / Консенсус / Контур" =====
    const FWORDS = ["Базис", "Консенсус", "Контур"];
    const wordFontPx = Math.max(20, Math.round(size * 0.048));
    const flockFontPx = Math.max(12, Math.round(size * 0.030));
    ctx.font = `700 ${wordFontPx}px Onest, system-ui, sans-serif`;
    const layouts = FWORDS.map((w) => {
      const chars = Array.from(w);
      const widths = chars.map((c) => ctx.measureText(c).width);
      const trk = wordFontPx * 0.04;
      const total = widths.reduce((a, b) => a + b, 0) + trk * (chars.length - 1);
      let xx = cx - total / 2;
      const slots = chars.map((c, i) => {
        const s = { x: xx + widths[i] / 2, y: cy };
        xx += widths[i] + trk;
        return s;
      });
      return { chars, slots };
    });
    ctx.textAlign = "start";ctx.textBaseline = "alphabetic";
    const flockBaseX = size * 0.115,flockBaseY = size * 0.090;
    const flockSpread = size * 0.045;
    const flock = [];
    FWORDS.forEach((w, wi) => {
      Array.from(w).forEach((ch, si) => {
        flock.push({
          ch, wi, si,
          oang: Math.random() * Math.PI * 2,
          orad: (0.30 + Math.sqrt(Math.random()) * 0.70) * flockSpread,
          ospd: 1.30 + Math.random() * 1.00,
          wph: Math.random() * Math.PI * 2,
          wsp: 0.5 + Math.random() * 0.7,
          wamp: size * 0.006 + Math.random() * size * 0.012,
          ddx: Math.random() * 2 - 1,
          x: flockBaseX, y: flockBaseY, s: flockFontPx, a: 0,
          sparkCycle: -1
        });
      });
    });
    const F_IN = 1300,F_HOLD = 1500,F_OUT = 650,F_GAP = 350;
    const F_CYCLE = F_IN + F_HOLD + F_OUT + F_GAP;
    const F_STAG = 80,F_TRAVEL = 520;
    const flockStart = performance.now();
    let sparks = [];
    const shOff = document.createElement("canvas");
    const shCtx = shOff.getContext("2d");
    const shatterLetter = (p) => {
      const fs = Math.max(8, p.s);
      const pad = 4;
      shCtx.font = `700 ${fs}px Onest, system-ui, sans-serif`;
      const w = Math.ceil(shCtx.measureText(p.ch).width) + pad * 2;
      const h = Math.ceil(fs * 1.45) + pad * 2;
      shOff.width = w;shOff.height = h;
      shCtx.font = `700 ${fs}px Onest, system-ui, sans-serif`;
      shCtx.textAlign = "center";shCtx.textBaseline = "middle";
      shCtx.clearRect(0, 0, w, h);
      shCtx.fillStyle = "#0A0A0A";
      shCtx.fillText(p.ch, w / 2, h / 2);
      const data = shCtx.getImageData(0, 0, w, h).data;
      const step = 2;
      const ox = p.x - w / 2,oy = p.y - h / 2;
      for (let yy = 0; yy < h; yy += step) {
        for (let xx = 0; xx < w; xx += step) {
          if (data[(yy * w + xx) * 4 + 3] > 60) {
            const dx = (xx - w / 2) / w,dy = (yy - h / 2) / h;
            sparks.push({
              x: ox + xx + Math.random() * step,
              y: oy + yy + Math.random() * step,
              vx: dx * size * 0.012 + (Math.random() - 0.5) * size * 0.006,
              vy: dy * size * 0.006 - size * 0.004 - Math.random() * size * 0.005,
              life: 24 + Math.random() * 34, max: 58,
              sz: 0.8 + Math.random() * 1.0
            });
          }
        }
      }
    };
    const clampF = (v, a, b) => v < a ? a : v > b ? b : v;
    const eoCubic = (p) => 1 - Math.pow(1 - p, 3);

    const render = () => {
      if (stopped) return;
      t += 0.0035;
      mx += (tx - mx) * 0.06;
      my += (ty - my) * 0.06;

      ctx.clearRect(0, 0, size, size);

      const yaw = t + mx * 0.9;
      const pitch = 0.14 + my * 0.7;
      const cy_ = Math.cos(yaw),sy_ = Math.sin(yaw);
      const cp_ = Math.cos(pitch),sp_ = Math.sin(pitch);
      const breath = 1 + Math.sin(t * 1.2) * 0.018;

      const screen = pts.map((p) => {
        const x1 = p.x * cy_ + p.z * sy_;
        const z1 = -p.x * sy_ + p.z * cy_;
        const y1 = p.y * cp_ - z1 * sp_;
        const z2 = p.y * sp_ + z1 * cp_;
        return { x: x1, y: y1, z: z2, accent: p.accent, sz: p.sz };
      });
      screen.sort((a, b) => a.z - b.z);

      // back-glow behind sphere
      const grd = ctx.createRadialGradient(cx, cy, 0, cx, cy, R * 0.95);
      grd.addColorStop(0, `rgba(${accentRgb[0]},${accentRgb[1]},${accentRgb[2]},0.18)`);
      grd.addColorStop(1, `rgba(${accentRgb[0]},${accentRgb[1]},${accentRgb[2]},0)`);
      ctx.fillStyle = grd;
      ctx.beginPath();ctx.arc(cx, cy, R * 0.95, 0, Math.PI * 2);ctx.fill();

      for (const p of screen) {
        const depth = (p.z + 1) / 2; // 0 back, 1 front
        const px = cx + p.x * R * breath;
        const py = cy + p.y * R * breath;
        const r = (p.sz + depth * 1.4) * (p.accent ? 1.6 : 1);
        const op = p.accent ? 0.6 + depth * 0.4 : 0.10 + depth * 0.78;
        ctx.fillStyle = p.accent ?
        `rgba(${accentRgb[0]},${accentRgb[1]},${accentRgb[2]},${op})` :
        `rgba(10,10,10,${op})`;
        ctx.beginPath();
        ctx.arc(px, py, r, 0, Math.PI * 2);
        ctx.fill();
      }

      // ===== annotation bounding box — acquire / track / re-target =====
      const project = (p) => {
        const x1 = p.x * cy_ + p.z * sy_;
        const z1 = -p.x * sy_ + p.z * cy_;
        const y1 = p.y * cp_ - z1 * sp_;
        const z2 = p.y * sp_ + z1 * cp_;
        return { x: x1, y: y1, z: z2 };
      };
      const nowMs = performance.now();
      if (nowMs > holdUntil) {
        // gather front-facing points sitting away from the silhouette edge
        const cands = [];
        for (let i = 0; i < pts.length; i++) {
          const pr = project(pts[i]);
          const rad = Math.hypot(pr.x, pr.y);
          if (pr.z > 0.5 && rad < 0.78 && (pts[i].accent || Math.random() < 0.05)) cands.push(i);
        }
        if (cands.length) {
          let pick = cands[Math.random() * cands.length | 0];
          if (pick === targetIdx && cands.length > 1) pick = cands[Math.random() * cands.length | 0];
          targetIdx = pick;
        }
        holdUntil = nowMs + 1300 + Math.random() * 800;
        boxHS = 72; // snap open, then contract = "acquire target"
        lockT = 0;
        conf = 0.9 + Math.random() * 0.098;
        label = classes[Math.random() * classes.length | 0] + " " + conf.toFixed(2);
      }

      // track the locked point as the sphere keeps rotating
      const tp = project(pts[targetIdx]);
      const tdepth = (tp.z + 1) / 2;
      const tpx = cx + tp.x * R * breath;
      const tpy = cy + tp.y * R * breath;
      const tightHS = 19 + tdepth * 12;
      boxCX += (tpx - boxCX) * 0.2;
      boxCY += (tpy - boxCY) * 0.2;
      boxHS += (tightHS - boxHS) * 0.16;
      lockT += (1 - lockT) * 0.12;

      // fade out as the tracked point rotates toward the back
      const boxVis = Math.max(0, Math.min(1, (tp.z - 0.12) / 0.32));
      if (boxVis > 0.01) {
        const bx = boxCX,by = boxCY,hs = boxHS;
        const aC = `${accentRgb[0]},${accentRgb[1]},${accentRgb[2]}`;
        ctx.save();
        ctx.lineJoin = "round";
        // faint full frame
        ctx.strokeStyle = `rgba(${aC},${(0.20 + 0.38 * lockT) * boxVis})`;
        ctx.lineWidth = 1;
        ctx.strokeRect(bx - hs, by - hs, hs * 2, hs * 2);
        // L-shaped corner brackets
        const cl = 7 + 5 * lockT;
        ctx.strokeStyle = `rgba(${aC},${0.95 * boxVis})`;
        ctx.lineWidth = 2;
        ctx.lineCap = "round";
        const corner = (sx, sy, dx, dy) => {
          ctx.beginPath();
          ctx.moveTo(sx + dx * cl, sy);
          ctx.lineTo(sx, sy);
          ctx.lineTo(sx, sy + dy * cl);
          ctx.stroke();
        };
        corner(bx - hs, by - hs, 1, 1);
        corner(bx + hs, by - hs, -1, 1);
        corner(bx - hs, by + hs, 1, -1);
        corner(bx + hs, by + hs, -1, -1);
        // center crosshair
        ctx.strokeStyle = `rgba(${aC},${0.7 * boxVis})`;
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(bx - 4, by);ctx.lineTo(bx + 4, by);
        ctx.moveTo(bx, by - 4);ctx.lineTo(bx, by + 4);
        ctx.stroke();
        // label tag
        ctx.font = "600 9px 'JetBrains Mono', ui-monospace, monospace";
        const tw = ctx.measureText(label).width;
        const tagH = 13,tagW = tw + 10;
        let tagX = bx - hs,tagY = by - hs - tagH - 2;
        if (tagY < 2) tagY = by + hs + 2;
        ctx.globalAlpha = boxVis * (0.45 + 0.55 * lockT);
        ctx.fillStyle = `rgb(${aC})`;
        ctx.fillRect(tagX, tagY, tagW, tagH);
        ctx.fillStyle = "#fff";
        ctx.textBaseline = "middle";
        ctx.fillText(label, tagX + 5, tagY + tagH / 2 + 0.5);
        ctx.restore();
      }

      // ===== letter flock: drift → staggered fly-in → hold → dissolve → loop =====
      const fnow = performance.now() - flockStart;
      const cycleIdx = Math.floor(fnow / F_CYCLE);
      const fslot = cycleIdx % 3;
      const localT = fnow % F_CYCLE;
      const fcx = flockBaseX + Math.sin(t * 0.5) * size * 0.016;
      const fcy = flockBaseY + Math.cos(t * 0.42) * size * 0.014;

      for (const p of flock) {
        const active = p.wi === fslot;
        const oa = p.oang + t * p.ospd;
        const rr = p.orad + Math.sin(t * p.wsp + p.wph) * p.wamp;
        const hx = fcx + Math.cos(oa) * rr + Math.cos(t * p.wsp * 1.3 + p.wph) * p.wamp * 0.4;
        const hy = fcy + Math.sin(oa) * rr * 0.58 + Math.sin(t * p.wsp * 1.1 + p.wph) * p.wamp * 0.4;
        let gx = hx,gy = hy,ga = 0.42,gs = flockFontPx,posLerp = 0.045,aLerp = 0.16;

        if (active) {
          const sl = layouts[p.wi].slots[p.si];
          const tHoldEnd = F_IN + F_HOLD;
          const tOutEnd = tHoldEnd + F_OUT;
          if (localT < F_IN) {
            // fly out of the flock one by one, fast, toward the word slot
            const pr = clampF((localT - p.si * F_STAG) / F_TRAVEL, 0, 1);
            const e = eoCubic(pr);
            gx = hx + (sl.x - hx) * e;
            gy = hy + (sl.y - hy) * e;
            gs = flockFontPx + (wordFontPx - flockFontPx) * e;
            ga = 0.42 + 0.58 * clampF(pr * 1.5, 0, 1);
            posLerp = 0.5;aLerp = 0.3;
          } else if (localT < tHoldEnd) {
            // word formed, held in the sphere centre
            gx = sl.x;gy = sl.y;gs = wordFontPx;ga = 1;posLerp = 0.45;aLerp = 0.4;
          } else if (localT < tOutEnd) {
            // dissolve (Telegram-style): glyph shatters into tiny pixels that scatter & fade
            if (p.sparkCycle !== cycleIdx) {
              p.sparkCycle = cycleIdx;
              shatterLetter(p);
              p.a = 0;
            }
            gx = sl.x;gy = sl.y;gs = p.s;ga = 0;posLerp = 0;aLerp = 1;
          } else {
            // gap — slip back toward the flock, invisible
            gx = hx;gy = hy;gs = flockFontPx;ga = 0;posLerp = 0.1;aLerp = 0.25;
          }
        }

        p.x += (gx - p.x) * posLerp;
        p.y += (gy - p.y) * posLerp;
        p.s += (gs - p.s) * posLerp;
        p.a += (ga - p.a) * aLerp;

        if (p.a > 0.015) {
          const f = clampF((p.s - flockFontPx) / (wordFontPx - flockFontPx), 0, 1);
          const cr = Math.round(107 + (10 - 107) * f);
          const cg = Math.round(107 + (10 - 107) * f);
          const cb = Math.round(107 + (10 - 107) * f);
          ctx.save();
          ctx.globalAlpha = p.a;
          ctx.textAlign = "center";ctx.textBaseline = "middle";
          ctx.font = `700 ${p.s.toFixed(1)}px Onest, system-ui, sans-serif`;
          if (f > 0.25) {ctx.shadowColor = "rgba(255,255,255,0.92)";ctx.shadowBlur = 7 * f;}
          ctx.fillStyle = `rgb(${cr},${cg},${cb})`;
          ctx.fillText(p.ch, p.x, p.y);
          ctx.restore();
        }
      }

      // dissolve shards (Telegram-style break-up into tiny pieces)
      if (sparks.length) {
        for (let i = sparks.length - 1; i >= 0; i--) {
          const s = sparks[i];
          s.x += s.vx;s.y += s.vy;
          s.vx *= 0.94;s.vy = s.vy * 0.94 + size * 0.0004;
          s.life -= 1;
          if (s.life <= 0) {sparks.splice(i, 1);continue;}
          const sa = Math.pow(s.life / s.max, 0.85);
          const d = s.sz * (0.5 + sa * 0.6);
          ctx.fillStyle = `rgba(10,10,10,${(sa * 0.92).toFixed(3)})`;
          ctx.fillRect(s.x, s.y, d, d);
        }
      }

      raf = requestAnimationFrame(render);
    };
    raf = requestAnimationFrame(render);
    return () => {stopped = true;cancelAnimationFrame(raf);window.removeEventListener("pointermove", onMove);};
  }, [size]);
  return <canvas ref={ref} style={{ display: "block" }} />;
}

/* ============== HERO ORB — composition wrapper (backdrop + sphere) ============== */
function HeroOrb({ size = 520 }) {
  // wrapper matches the actual visual extent so the sphere never overflows the layout
  const visual = size * 1.2;
  return (
    <div style={{ position: "relative", width: visual, height: visual }}>
      <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", pointerEvents: "none" }}>
        <HeroBackdrop size={visual} />
      </div>
      <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center" }}>
        <HeroSphere size={size * 0.95} />
      </div>
    </div>);

}

/* ============== CURSOR ============== */
function Cursor({ enabled }) {
  const dot = useRef(null),ring = useRef(null);
  const [mode, setMode] = useState("idle");
  const [label, setLabel] = useState("");
  useEffect(() => {
    if (!enabled) {document.body.classList.remove("has-cursor");return;}
    document.body.classList.add("has-cursor");
    let mx = innerWidth / 2,my = innerHeight / 2,rx = mx,ry = my,dx = mx,dy = my,raf;
    const tick = () => {
      rx += (mx - rx) * 0.18;ry += (my - ry) * 0.18;
      dx += (mx - dx) * 0.45;dy += (my - dy) * 0.45;
      if (dot.current) dot.current.style.transform = `translate(${dx}px, ${dy}px) translate(-50%, -50%)`;
      if (ring.current) ring.current.style.transform = `translate(${rx}px, ${ry}px) translate(-50%, -50%)`;
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    const onMove = (e) => {mx = e.clientX;my = e.clientY;};
    const onOver = (e) => {
      const t = e.target.closest?.("[data-cursor]");
      if (!t) {setMode("idle");setLabel("");return;}
      const m = t.dataset.cursor;const l = t.dataset.cursorLabel || "";
      setMode(l ? "text" : m || "big");setLabel(l);
    };
    const onLeave = () => {setMode("idle");setLabel("");};
    addEventListener("pointermove", onMove);
    document.addEventListener("pointerover", onOver);
    document.addEventListener("pointerout", onLeave);
    return () => {
      cancelAnimationFrame(raf);
      removeEventListener("pointermove", onMove);
      document.removeEventListener("pointerover", onOver);
      document.removeEventListener("pointerout", onLeave);
      document.body.classList.remove("has-cursor");
    };
  }, [enabled]);
  if (!enabled) return null;
  return (
    <>
      <div ref={ring} className={`cursor-ring ${mode === "idle" ? "" : "hidden"}`} />
      <div ref={dot} className={`cursor ${mode === "big" ? "big" : ""} ${mode === "text" ? "text" : ""}`}>
        {label ? <span className="label-in">{label}</span> : null}
      </div>
    </>);

}

/* ============== LOADER ============== */
function Loader({ active, onDone }) {
  const [pct, setPct] = useState(0);
  const [lifting, setLifting] = useState(false);
  useEffect(() => {
    if (!active) return;
    let n = 0;
    const id = setInterval(() => {
      n = Math.min(100, n + Math.random() * 16 + 6);
      setPct(Math.floor(n));
      if (n >= 100) {
        clearInterval(id);
        setTimeout(() => setLifting(true), 320);
        setTimeout(() => onDone?.(), 1400);
      }
    }, 95);
    return () => clearInterval(id);
  }, [active, onDone]);
  if (!active) return null;
  return (
    <div className="loader" style={{
      transform: lifting ? "translateY(-100%)" : "translateY(0)",
      transition: "transform 1.0s cubic-bezier(.7,.0,.2,1)"
    }}>
      <div className="loader-mark"><span style={{ color: "var(--accent)" }}>.</span>v1</div>
      <div className="loader-meta">{pct.toString().padStart(3, "0")} / 100</div>
    </div>);

}

/* ============== NAV + MENU OVERLAY ============== */
function Nav({ onMenu }) {
  const y = useScrollY();
  return (
    <nav className={`nav ${y > 24 ? "scrolled" : ""}`}>
      <a href="#top" className="logo-serif" data-cursor="big">
        <span className="dot">.</span>v1
      </a>
      <div className="nav-right">
        <button onClick={onMenu} className="menu-pill" data-cursor="big">МЕНЮ</button>
        <span className="live-pill" data-cursor="big" title="online"><span className="core" /></span>
      </div>
    </nav>);

}

function MenuOverlay({ open, onClose }) {
  const items = [
  { label: "Применения", href: "#Применения" },
  { label: "Домены", href: "#Домены" },
  { label: "Кейсы", href: "pages/cases.html" },
  { label: "Услуги", href: "pages/services.html" },
  { label: "Команда", href: "pages/team.html" },
  { label: "Медиа", href: "pages/media.html" },
  { label: "О компании", href: "pages/about.html" },
  { label: "Контакты", href: "#Контакты" }];

  return (
    <div className={`menu-overlay ${open ? "" : "hidden"}`}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
        <span className="logo-serif" style={{ color: "#fff" }}><span style={{ color: "var(--accent)" }}>.</span>v1</span>
        <button onClick={onClose} className="menu-pill" style={{ background: "var(--accent)", color: "#fff" }} data-cursor="big">ЗАКРЫТЬ</button>
      </div>
      <div style={{ flex: 1, display: "flex", flexDirection: "column", justifyContent: "center", gap: 8 }}>
        {items.map((it, i) =>
        <a key={it.label} href={it.href} onClick={onClose} className="lift" data-cursor="big" style={{ display: "flex", alignItems: "baseline", gap: 20, color: "#fff", fontFamily: "var(--sans)", fontWeight: 500, fontSize: "clamp(40px,7vw,80px)", letterSpacing: "-0.025em", lineHeight: 1.1 }}>
            <span style={{ fontFamily: "var(--mono)", fontSize: 13, color: "rgba(255,255,255,.4)" }}>[{String(i + 1).padStart(2, "0")}]</span>
            <span>{it.label}</span>
          </a>
        )}
      </div>
      <div className="menu-contacts" style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 24, color: "rgba(255,255,255,.7)" }}>
        <div><div className="upper-mono" style={{ color: "rgba(255,255,255,.4)" }}>Email</div><div style={{ marginTop: 8 }}>office@v1data.ru</div></div>
        <div><div className="upper-mono" style={{ color: "rgba(255,255,255,.4)" }}>Телефон</div><div className="menu-phone-val" style={{ marginTop: 8 }}>+7 (499) 117-00-87</div></div>
        <div><div className="upper-mono" style={{ color: "rgba(255,255,255,.4)" }}>Партнёрство</div><div style={{ marginTop: 8 }}>partners@v1data.ru</div></div>
        <div><div className="upper-mono" style={{ color: "rgba(255,255,255,.4)" }}>Офис</div><div style={{ marginTop: 8 }}>Москва · 2026</div></div>
      </div>
    </div>);

}

/* ============== HERO ============== */
function Hero() {
  const time = useClock();
  return (
    <section id="top" style={{ position: "relative", paddingTop: 60, paddingBottom: 24, marginBottom: -134 }}>
      {/* sphere — sits just under the nav, fully visible */}
      <div style={{ display: "flex", justifyContent: "center", marginTop: 0, marginBottom: -40, pointerEvents: "none", position: "relative", zIndex: 0 }}>
        <HeroOrb size={597} />
      </div>

      {/* satellite cards — stacked vertically on the right, overlaying the sphere */}
      <div className="container" style={{ position: "absolute", right: 0, left: 0, top: 120, height: 0, pointerEvents: "none", zIndex: 3 }}>
        <div style={{ position: "relative", maxWidth: 1320, margin: "0 auto" }}>
        <article style={{ position: "absolute", right: 24, top: 0, width: 260, padding: 18, borderRadius: 22, background: "rgba(248,247,243,0.62)", backdropFilter: "blur(28px) saturate(180%)", WebkitBackdropFilter: "blur(28px) saturate(180%)", border: "1px solid rgba(255,255,255,0.75)", boxShadow: "inset 0 1px 0 rgba(255,255,255,0.85), inset 0 -1px 0 rgba(0,0,0,0.04), 0 6px 24px rgba(0,0,0,0.05)", pointerEvents: "auto" }}>
          <div style={{ display: "flex", justifyContent: "flex-end", fontFamily: "var(--mono)", fontSize: 12, color: "var(--dim)" }}>[3]</div>
          <div style={{ marginTop: 12, fontFamily: "var(--mono)", fontWeight: 500, fontSize: 26, letterSpacing: "-0.01em", color: "var(--accent)", lineHeight: 1.1 }}>Восстановление</div>
          <div style={{ marginTop: 8, fontFamily: "var(--sans)", fontWeight: 400, fontSize: 12, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--muted)", lineHeight: 1.4 }}>
            slip / collision / mis-pick
          </div>
        </article>
        <article style={{ position: "absolute", right: 24, top: 150, width: 260, padding: 18, borderRadius: 22, background: "rgba(248,247,243,0.62)", backdropFilter: "blur(28px) saturate(180%)", WebkitBackdropFilter: "blur(28px) saturate(180%)", border: "1px solid rgba(255,255,255,0.75)", boxShadow: "inset 0 1px 0 rgba(255,255,255,0.85), inset 0 -1px 0 rgba(0,0,0,0.04), 0 6px 24px rgba(0,0,0,0.05)", pointerEvents: "auto" }}>
          <div style={{ display: "flex", justifyContent: "flex-end", fontFamily: "var(--mono)", fontSize: 12, color: "var(--dim)" }}>[1]</div>
          <div style={{ marginTop: 12, fontFamily: "var(--mono)", fontWeight: 500, fontSize: 26, letterSpacing: "-0.01em", color: "var(--accent)", lineHeight: 1.1 }}>≥ 99,2%</div>
          <div style={{ marginTop: 8, fontFamily: "var(--sans)", fontWeight: 400, fontSize: 12, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--muted)", lineHeight: 1.4 }}>
            AI-предразметка по DROID
          </div>
        </article>
        <article style={{ position: "absolute", right: 24, top: 300, width: 260, padding: 18, borderRadius: 22, background: "rgba(248,247,243,0.62)", backdropFilter: "blur(28px) saturate(180%)", WebkitBackdropFilter: "blur(28px) saturate(180%)", border: "1px solid rgba(255,255,255,0.75)", boxShadow: "inset 0 1px 0 rgba(255,255,255,0.85), inset 0 -1px 0 rgba(0,0,0,0.04), 0 6px 24px rgba(0,0,0,0.05)", pointerEvents: "auto" }}>
          <div style={{ display: "flex", justifyContent: "flex-end", fontFamily: "var(--mono)", fontSize: 12, color: "var(--dim)" }}>[2]</div>
          <div style={{ marginTop: 12, fontFamily: "var(--mono)", fontWeight: 500, fontSize: 26, letterSpacing: "-0.01em", color: "var(--accent)", lineHeight: 1.1 }}>≤ 50 мс</div>
          <div style={{ marginTop: 8, fontFamily: "var(--sans)", fontWeight: 400, fontSize: 12, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--muted)", lineHeight: 1.4 }}>
            Sync video ↔ states
          </div>
        </article>
        </div>
      </div>

      {/* bottom row: H1 left, announcement pill right — overlaps the sphere bottom */}
      <div className="container" style={{ position: "relative", zIndex: 2, display: "grid", gridTemplateColumns: "1.5fr 1fr", gap: 32, alignItems: "end", paddingTop: 12, marginTop: -120 }}>
        <h1 className="hero-h1" style={{ fontSize: 60, lineHeight: 1.2, letterSpacing: "-0.035em", transform: "translateY(-391px)" }}>
          <Words>Суть в</Words>
          <br />
          <TypeLines words={["качестве", "точности", "скорости"]} />
        </h1>
        <a href="#Кейсы" className="card lift" data-cursor="text" data-cursor-label="Read" style={{ padding: "16px 20px", borderRadius: 18, display: "block", width: 360, justifySelf: "end", background: "var(--ink)", color: "#fff", transform: "translateY(-194px)" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, fontFamily: "var(--mono)", fontSize: 11, textTransform: "uppercase", letterSpacing: "0.1em", color: "rgba(255,255,255,0.65)" }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: "var(--accent)" }} />
            из пилотов
          </div>
          <div style={{ marginTop: 10, color: "#fff", fontSize: 14, lineHeight: 1.4 }}>
            «В короткие сроки подготовили 800 эпизодов полного цикла — от телеопа до eval, с Recovery-квотами.»
          </div>
        </a>
      </div>


    </section>);

}

/* ============== SCENARIOS (alt dark/light cards with decor) — section 01 ============== */
function Scenarios() {
  const ref = useReveal();
  const items = [
  { kind: "dark", vis: "humanoid", name: "Гуманоиды", desc: "VLA-датасеты для антропоморфных роботов с двуручной манипуляцией." },
  { kind: "light", vis: "mobile", name: "Мобильные манипуляторы", desc: "Сбор данных для роботов с подвижной базой и навигацией в пространстве." },
  { kind: "light", vis: "stationary", name: "Стационарные манипуляторы", desc: "Single и dual-arm на фиксированной базе для precision-задач." },
  { kind: "dark", vis: "umi", name: "Девайсы UMI / Gloves", desc: "Сбор демонстраций без робота — портативные манипуляторы и сенсорные перчатки." }];

  return (
    <section id="Применения" className="container" style={{ paddingTop: 9 }}>
      <div ref={ref} className="reveal" style={{ display: "grid", gridTemplateColumns: "1fr 1.6fr", gap: 32, alignItems: "end", marginBottom: 56 }}>
        <div>
          <SectionNum label="Применения" num="01" />
          <div className="body-mono" style={{ marginTop: 28, maxWidth: "36ch" }}>
            Строим data-фундамент для VLA под любую форму воплощения робота.
          </div>
        </div>
        <div>
          <p className="lead" style={{ maxWidth: "40ch" }}>
            Работаем с командами, которые стоят на пороге <Bracket accent>запуска</Bracket>, <Bracket accent>масштабирования</Bracket> или переосмысления своего robotics-стека.
          </p>
        </div>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 16 }}>
        {items.map((it, i) => {
          const dark = it.kind === "dark";
          return (
            <a key={i} href={`#Домены`} className={`card lift ${dark ? "card-dark" : ""}`} data-cursor="text" data-cursor-label="Открыть"
            style={{ aspectRatio: "1/1.05", display: "flex", flexDirection: "column", padding: 24, background: dark ? "#0A0A0A" : "var(--bg-card)", position: "relative", overflow: "hidden" }}>
              <ScenarioVisual kind={it.vis} dark={dark} />
              <div style={{ flex: 1 }} />
              <div style={{ position: "relative", zIndex: 2 }}>
                <div style={{ fontFamily: "var(--sans)", fontWeight: 500, fontSize: 22, letterSpacing: "-0.02em", color: dark ? "#FFFFFF" : "var(--ink)" }}>{it.name}</div>
                <div className="body-mono" style={{ marginTop: 8, color: dark ? "rgba(255,255,255,0.65)" : "var(--muted)", fontSize: 13 }}>{it.desc}</div>
              </div>
            </a>);

        })}
      </div>
    </section>);

}

/* ============== SCENARIO VISUAL — animated SVG per применение ============== */
function ScenarioVisual({ kind, dark }) {
  const stroke = dark ? "rgba(255,255,255,0.85)" : "rgba(10,10,10,0.75)";
  const fill = dark ? "rgba(255,255,255,0.08)" : "rgba(10,10,10,0.05)";
  const accent = "#EA0A23";
  const sw = 1.5;
  return (
    <div className="scn-vis" aria-hidden="true">
      <svg viewBox="0 0 200 160" fill="none" stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
        {kind === "humanoid" &&
        <>
            {/* head + sensors */}
            <rect x="85" y="22" width="30" height="28" rx="6" />
            <circle className="hum-eye" cx="94" cy="35" r="2.5" fill={accent} stroke="none" />
            <circle className="hum-eye e2" cx="106" cy="35" r="2.5" fill={accent} stroke="none" />
            <line x1="100" y1="50" x2="100" y2="56" />
            {/* torso */}
            <path d="M82 56 L118 56 L122 102 L78 102 Z" />
            {/* shoulders */}
            <circle cx="82" cy="58" r="4" fill={fill} />
            <circle cx="118" cy="58" r="4" fill={fill} />
            {/* arms — static, attached at shoulders */}
            <g>
              <line x1="82" y1="58" x2="68" y2="92" strokeWidth="2.2" />
              <line x1="68" y1="92" x2="58" y2="118" strokeWidth="2.2" />
              <rect x="51" y="118" width="14" height="9" rx="2" />
            </g>
            <g>
              <line x1="118" y1="58" x2="132" y2="92" strokeWidth="2.2" />
              <line x1="132" y1="92" x2="142" y2="118" strokeWidth="2.2" />
              <rect x="135" y="118" width="14" height="9" rx="2" />
            </g>
            {/* legs */}
            <line x1="92" y1="102" x2="89" y2="136" strokeWidth="1.8" />
            <line x1="108" y1="102" x2="111" y2="136" strokeWidth="1.8" />
            <line x1="83" y1="138" x2="95" y2="138" strokeWidth="2.2" />
            <line x1="105" y1="138" x2="117" y2="138" strokeWidth="2.2" />
            {/* data flow: dashed line from heart up to head */}
            <line className="hum-dataflow" x1="100" y1="76" x2="100" y2="50" stroke={accent} strokeOpacity="0.65" strokeWidth="1.4" strokeDasharray="3 3" />
            {/* pulse rings expanding from heart (training-data flowing through body) */}
            <circle className="hum-ring r1" cx="100" cy="76" r="5" stroke={accent} strokeWidth="1.4" fill="none" />
            <circle className="hum-ring r2" cx="100" cy="76" r="5" stroke={accent} strokeWidth="1.4" fill="none" />
            <circle className="hum-ring r3" cx="100" cy="76" r="5" stroke={accent} strokeWidth="1.4" fill="none" />
            {/* heart core */}
            <circle className="hum-heart" cx="100" cy="76" r="4" fill={accent} stroke="none" />
          </>
        }

        {kind === "mobile" &&
        <>
            {/* motion trails behind base */}
            <line className="mob-trail t1" x1="18" y1="120" x2="38" y2="120" stroke={accent} strokeOpacity="0.6" strokeWidth="1.8" />
            <line className="mob-trail t2" x1="18" y1="128" x2="32" y2="128" stroke={accent} strokeOpacity="0.45" strokeWidth="1.6" />
            <line className="mob-trail t3" x1="18" y1="135" x2="28" y2="135" stroke={accent} strokeOpacity="0.3" strokeWidth="1.4" />
            {/* base body */}
            <rect x="48" y="92" width="84" height="38" rx="4" />
            {/* wheels — spinning */}
            <g className="mob-wheel mw1">
              <circle cx="66" cy="138" r="11" />
              <line x1="55" y1="138" x2="77" y2="138" />
              <line x1="66" y1="127" x2="66" y2="149" />
            </g>
            <g className="mob-wheel mw2">
              <circle cx="114" cy="138" r="11" />
              <line x1="103" y1="138" x2="125" y2="138" />
              <line x1="114" y1="127" x2="114" y2="149" />
            </g>
            {/* vertical mast */}
            <rect x="86" y="46" width="14" height="46" />
            {/* sensor on mast */}
            <circle className="hum-eye" cx="93" cy="40" r="3" fill={accent} stroke="none" />
            <line x1="93" y1="37" x2="93" y2="32" stroke={accent} strokeWidth="1.2" />
            {/* arm — extending */}
            <g className="mob-arm">
              <line x1="100" y1="58" x2="152" y2="58" strokeWidth="2.4" />
              <circle cx="100" cy="58" r="3" fill={fill} />
              <line x1="152" y1="58" x2="164" y2="78" strokeWidth="2.4" />
              <rect x="157" y="78" width="14" height="10" rx="2" />
              {/* target dot */}
              <circle className="mob-target" cx="164" cy="100" r="3" fill={accent} stroke="none" />
            </g>
          </>
        }

        {kind === "stationary" &&
        <>
            {/* bolted base */}
            <line x1="72" y1="142" x2="128" y2="142" strokeWidth="2.4" />
            <line x1="76" y1="142" x2="74" y2="148" />
            <line x1="124" y1="142" x2="126" y2="148" />
            <rect x="82" y="122" width="36" height="20" rx="2" />
            {/* vertical column */}
            <rect x="92" y="56" width="16" height="66" />
            {/* shoulder joint */}
            <circle cx="100" cy="58" r="5.5" fill={fill} />
            <circle className="hum-eye" cx="100" cy="58" r="2" fill={accent} stroke="none" />
            {/* upper arm (rotating slowly) */}
            <g className="st-arm">
              <line x1="100" y1="58" x2="148" y2="58" strokeWidth="2.8" />
              <circle cx="148" cy="58" r="4.5" fill={fill} />
              {/* forearm */}
              <line x1="148" y1="58" x2="166" y2="86" strokeWidth="2.5" />
              {/* wrist + end-effector */}
              <rect x="159" y="86" width="14" height="10" rx="2" />
              <line x1="161" y1="96" x2="161" y2="102" strokeWidth="1.4" />
              <line x1="171" y1="96" x2="171" y2="102" strokeWidth="1.4" />
            </g>
            {/* target workpiece with pulsing crosshair */}
            <circle className="st-pulse" cx="166" cy="116" r="9" stroke={accent} strokeWidth="1.4" />
            <circle cx="166" cy="116" r="2.5" fill={accent} stroke="none" />
            <line x1="156" y1="116" x2="160" y2="116" stroke={accent} strokeWidth="1.4" />
            <line x1="172" y1="116" x2="176" y2="116" stroke={accent} strokeWidth="1.4" />
            <line x1="166" y1="106" x2="166" y2="110" stroke={accent} strokeWidth="1.4" />
            <line x1="166" y1="122" x2="166" y2="126" stroke={accent} strokeWidth="1.4" />
          </>
        }

        {kind === "umi" &&
        <>
            {/* wrist data strap */}
            <rect x="74" y="120" width="52" height="30" rx="5" />
            <line x1="74" y1="130" x2="126" y2="130" strokeDasharray="2 3" />
            <circle cx="100" cy="140" r="2" fill={accent} stroke="none" className="hum-heart" />
            {/* palm */}
            <path d="M74 120 L70 64 Q70 54 80 54 L120 54 Q130 54 130 64 L126 120 Z" />
            {/* index */}
            <g className="glove-finger">
              <rect x="80" y="20" width="9" height="36" rx="3.5" />
              <circle className="glove-tip g1" cx="84.5" cy="20" r="3.5" fill={accent} stroke="none" />
            </g>
            {/* middle */}
            <g className="glove-finger">
              <rect x="91" y="10" width="9" height="46" rx="3.5" />
              <circle className="glove-tip g2" cx="95.5" cy="10" r="3.5" fill={accent} stroke="none" />
            </g>
            {/* ring */}
            <g className="glove-finger">
              <rect x="102" y="14" width="9" height="42" rx="3.5" />
              <circle className="glove-tip g3" cx="106.5" cy="14" r="3.5" fill={accent} stroke="none" />
            </g>
            {/* pinky */}
            <g className="glove-finger">
              <rect x="113" y="22" width="9" height="34" rx="3.5" />
              <circle className="glove-tip g4" cx="117.5" cy="22" r="3.5" fill={accent} stroke="none" />
            </g>
            {/* thumb */}
            <g transform="rotate(-30 72 72)">
              <rect x="55" y="66" width="22" height="9" rx="3.5" />
              <circle className="glove-tip g5" cx="55" cy="70.5" r="3.5" fill={accent} stroke="none" />
            </g>
            {/* palm sensor */}
            <circle cx="100" cy="92" r="5" fill={fill} />
            <circle className="hum-eye" cx="100" cy="92" r="2.5" fill={accent} stroke="none" />
            {/* data link upward */}
            <line x1="100" y1="87" x2="100" y2="78" strokeDasharray="2 2" opacity="0.55" />
          </>
        }
      </svg>
    </div>);

}

/* ============== DOMAIN VISUAL — animated SVG per kind ============== */
function DomainVisual({ kind, stroke, dark }) {
  const accent = "#EA0A23";
  const sw = 1.4;
  return (
    <div className="dom-vis" aria-hidden="true">
      <svg viewBox="0 0 200 160" fill="none" stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
        {kind === "kitchen" &&
        <>
            {/* steam */}
            <path className="k-steam s1" d="M70 50 q5 -10 0 -20" />
            <path className="k-steam s2" d="M100 50 q5 -10 0 -20" />
            <path className="k-steam s3" d="M130 50 q5 -10 0 -20" />
            {/* lid + pot */}
            <g className="k-lid">
              <line x1="60" y1="62" x2="140" y2="62" />
              <circle cx="100" cy="58" r="3" fill={accent} stroke="none" />
            </g>
            <path d="M55 70 Q55 110 70 120 L130 120 Q145 110 145 70 Z" />
            {/* handles */}
            <line x1="45" y1="82" x2="55" y2="82" />
            <line x1="155" y1="82" x2="145" y2="82" />
            {/* stove */}
            <line x1="40" y1="135" x2="160" y2="135" />
            <line x1="40" y1="135" x2="40" y2="142" />
            <line x1="160" y1="135" x2="160" y2="142" />
          </>
        }

        {kind === "lab" &&
        <>
            {/* pipette */}
            <rect x="50" y="20" width="14" height="46" rx="3" />
            <line x1="57" y1="66" x2="57" y2="74" />
            {/* drop */}
            <circle className="lab-drop" cx="57" cy="74" r="3.6" fill={accent} stroke="none" />
            {/* test tube 1 */}
            <path d="M88 50 L88 130 Q88 142 100 142 Q112 142 112 130 L112 50" />
            <line x1="84" y1="50" x2="116" y2="50" />
            <rect className="lab-liquid" x="89" y="90" width="22" height="50" rx="11" fill={accent} fillOpacity={dark ? 0.6 : 0.4} stroke="none" />
            {/* test tube 2 */}
            <path d="M138 60 L138 130 Q138 140 148 140 Q158 140 158 130 L158 60" />
            <line x1="135" y1="60" x2="161" y2="60" />
            <rect x="139" y="100" width="18" height="40" rx="9" fill={stroke} fillOpacity="0.18" stroke="none" />
            {/* table */}
            <line x1="30" y1="150" x2="170" y2="150" />
          </>
        }

        {kind === "retail" &&
        <>
            {/* shelf */}
            <line x1="30" y1="40" x2="170" y2="40" />
            <rect x="40" y="22" width="18" height="18" />
            <rect x="65" y="18" width="22" height="22" />
            <rect x="94" y="24" width="20" height="16" />
            <rect x="121" y="20" width="18" height="20" fill={accent} stroke="none" />
            <rect x="146" y="22" width="18" height="18" />
            {/* scanner */}
            <rect x="68" y="92" width="64" height="14" rx="2" />
            <line className="r-scan" x1="100" y1="86" x2="100" y2="78" stroke={accent} strokeWidth="2" />
            <circle cx="100" cy="78" r="2" fill={accent} stroke="none" />
            {/* conveyor */}
            <line x1="30" y1="120" x2="170" y2="120" />
            <line x1="30" y1="130" x2="170" y2="130" />
            <g className="r-belt">
              <line x1="30" y1="125" x2="40" y2="125" />
              <line x1="55" y1="125" x2="65" y2="125" />
              <line x1="80" y1="125" x2="90" y2="125" />
              <line x1="105" y1="125" x2="115" y2="125" />
              <line x1="130" y1="125" x2="140" y2="125" />
              <line x1="155" y1="125" x2="165" y2="125" />
              <line x1="180" y1="125" x2="190" y2="125" />
            </g>
            <circle cx="36" cy="142" r="6" />
            <circle cx="164" cy="142" r="6" />
          </>
        }

        {kind === "cleaning" &&
        <>
            {/* surface */}
            <line x1="20" y1="120" x2="180" y2="120" />
            {/* streaks */}
            <line className="c-wipe w1" x1="40" y1="135" x2="80" y2="135" stroke={accent} strokeOpacity="0.6" />
            <line className="c-wipe w2" x1="40" y1="142" x2="70" y2="142" />
            <line className="c-wipe w3" x1="40" y1="149" x2="60" y2="149" />
            {/* arm */}
            <line x1="100" y1="20" x2="100" y2="70" />
            <circle cx="100" cy="20" r="5" />
            <line x1="100" y1="70" x2="120" y2="100" />
            {/* brush head */}
            <g className="c-brush" style={{ transformOrigin: "120px 110px" }}>
              <circle cx="120" cy="110" r="14" />
              <line x1="120" y1="96" x2="120" y2="124" />
              <line x1="106" y1="110" x2="134" y2="110" />
              <line x1="110" y1="100" x2="130" y2="120" />
              <line x1="130" y1="100" x2="110" y2="120" />
              <circle cx="120" cy="110" r="3" fill={accent} stroke="none" />
            </g>
          </>
        }

        {kind === "factory" &&
        <>
            {/* gantry */}
            <line x1="30" y1="30" x2="170" y2="30" strokeWidth="2" />
            <line x1="40" y1="30" x2="40" y2="140" />
            <line x1="160" y1="30" x2="160" y2="140" />
            {/* robot arm */}
            <g className="m-arm">
              <line x1="100" y1="30" x2="100" y2="80" strokeWidth="2.4" />
              <line x1="100" y1="80" x2="130" y2="100" strokeWidth="2.4" />
              <circle cx="100" cy="30" r="4" fill={accent} stroke="none" />
              <circle cx="100" cy="80" r="3" />
              <rect x="125" y="98" width="12" height="8" />
              {/* spark */}
              <circle className="m-spark" cx="135" cy="108" r="3" fill={accent} stroke="none" />
            </g>
            {/* conveyor */}
            <line x1="40" y1="140" x2="160" y2="140" />
            <g className="m-belt">
              <rect x="50" y="118" width="16" height="20" fill={dark ? "rgba(255,255,255,0.12)" : "rgba(10,10,10,0.18)"} stroke={stroke} />
              <rect x="80" y="118" width="16" height="20" fill={dark ? "rgba(255,255,255,0.12)" : "rgba(10,10,10,0.18)"} stroke={stroke} />
              <rect x="110" y="118" width="16" height="20" fill={accent} stroke="none" />
              <rect x="140" y="118" width="16" height="20" fill={dark ? "rgba(255,255,255,0.12)" : "rgba(10,10,10,0.18)"} stroke={stroke} />
            </g>
          </>
        }

        {kind === "office" &&
        <>
            {/* monitor */}
            <rect x="38" y="26" width="124" height="78" rx="4" />
            <line x1="80" y1="120" x2="120" y2="120" />
            <line x1="100" y1="104" x2="100" y2="120" />
            {/* lines of text */}
            <line className="o-line l1" x1="50" y1="42" x2="120" y2="42" strokeWidth="2" />
            <line className="o-line l2" x1="50" y1="56" x2="140" y2="56" strokeWidth="2" />
            <line className="o-line l3" x1="50" y1="70" x2="105" y2="70" strokeWidth="2" />
            {/* caret */}
            <line className="o-caret" x1="108" y1="68" x2="108" y2="76" stroke={accent} strokeWidth="2" />
            {/* tray (papers) */}
            <rect x="44" y="132" width="40" height="6" />
            <rect x="50" y="125" width="34" height="6" />
            {/* coffee */}
            <rect x="138" y="125" width="22" height="18" />
            <path d="M160 128 L168 128 L168 138 L160 138" />
          </>
        }

        {kind === "hard" &&
        <>
            {/* glass cylinder */}
            <ellipse cx="62" cy="40" rx="22" ry="6" />
            <line x1="40" y1="40" x2="40" y2="120" />
            <line x1="84" y1="40" x2="84" y2="120" />
            <ellipse cx="62" cy="120" rx="22" ry="6" />
            <ellipse cx="62" cy="120" rx="22" ry="6" fill={accent} fillOpacity="0.18" stroke="none" />
            {/* glass shine */}
            <line className="hs-shine" x1="48" y1="60" x2="48" y2="100" stroke={accent} strokeOpacity="0.7" strokeWidth="2" />
            {/* cloth wave */}
            <g className="hs-cloth">
              <path d="M110 80 Q140 60 170 80 Q190 95 170 110 Q140 130 110 110 Z" fill={dark ? "rgba(255,255,255,0.08)" : "rgba(10,10,10,0.06)"} />
            </g>
            <line x1="110" y1="80" x2="170" y2="80" strokeDasharray="3 3" />
            <line x1="110" y1="110" x2="170" y2="110" strokeDasharray="3 3" />
          </>
        }
      </svg>
    </div>);

}

/* ============== INDUSTRIES — large slider with header + arrows — section 02 ============== */
function Industries() {
  const ref = useReveal();
  const scroller = useRef(null);
  const items = [
  { name: "Кухня", desc: "Кухонный домен — эпизоды приготовления, сервировки и уборки. Деформируемые объекты и жидкости.", kind: "kitchen" },
  { name: "Лаборатория", desc: "Лабораторные задачи — пипетирование, работа с пробирками и прозрачными объектами.", kind: "lab" },
  { name: "Ритейл", desc: "Ритейл — сканирование, выкладка, работа с SKU. Кассовые и складские сценарии.", kind: "retail" },
  { name: "Уборка", desc: "Уборка — протирка, сбор мусора, взаимодействие с поверхностями и разными материалами.", kind: "cleaning" },
  { name: "Производство", desc: "Сборочные линии и станки — точные манипуляции, F/T-сенсорика, повторяемые операции.", kind: "factory" },
  { name: "Офис", desc: "Офисные сценарии — работа с документами, перемещения, взаимодействия с оргтехникой.", kind: "office" },
  { name: "Сложные сценарии", desc: "Ткани, прозрачные объекты, узкие допуски и бликующие материалы. Не менее 10% в каждой поставке.", kind: "hard" }];


  const scrollBy = (dir) => scroller.current?.scrollBy({ left: dir * 420, behavior: "smooth" });

  React.useEffect(() => {
    // ensure the slider starts at 0 — prevents scroll-snap from "eating" the left padding on mount
    const el = scroller.current;
    if (el) el.scrollLeft = 0;
  }, []);

  return (
    <section id="Домены" style={{ paddingTop: 140 }}>
      {/* header: left meta, right lead text + arrows */}
      <div ref={ref} className="reveal container" style={{ display: "grid", gridTemplateColumns: "1fr 1.6fr", gap: 48, alignItems: "end", marginBottom: 56 }}>
        <div>
          <SectionNum label="Домены" num="02" />
          <p className="body-mono" style={{ marginTop: 28, maxWidth: "32ch", lineHeight: 1.55, color: "var(--muted)" }}>
            Покрываем ключевые домены реального мира — от бытовых сценариев до промышленных сборочных линий.
          </p>
        </div>
        <div>
          <h2 style={{
            fontFamily: "var(--sans)",
            fontWeight: 500,


            letterSpacing: "-0.03em",
            color: "var(--ink)",
            maxWidth: "32ch",
            textWrap: "balance", fontSize: "25px", lineHeight: "1.15"
          }}>
            Собираем мультимодальные данные в доменах, где роботы выходят из лабораторий в реальность.
          </h2>
          <div style={{ display: "flex", gap: 12, marginTop: 32 }}>
            <button onClick={() => scrollBy(-1)} aria-label="prev" data-cursor="big" style={{ width: 60, height: 60, background: "var(--bg-card)", color: "var(--ink)", border: "1px solid var(--hair)", borderRadius: 16, display: "flex", alignItems: "center", justifyContent: "center" }}>
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M19 12 H5 M11 6 L5 12 L11 18" strokeLinecap="square" /></svg>
            </button>
            <button onClick={() => scrollBy(1)} aria-label="next" data-cursor="big" style={{ width: 60, height: 60, background: "var(--accent)", color: "#fff", border: "1px solid var(--accent)", borderRadius: 16, display: "flex", alignItems: "center", justifyContent: "center" }}>
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M5 12 H19 M13 6 L19 12 L13 18" strokeLinecap="square" /></svg>
            </button>
          </div>
        </div>
      </div>

      {/* horizontal slider with large square cards */}
      <div ref={scroller} className="h-scroll" style={{ display: "flex", overflowX: "auto", scrollSnapType: "x mandatory", scrollPaddingInlineStart: "24px", paddingLeft: 24, paddingRight: 24, paddingBottom: 24, gap: 16 }}>
        {items.map((it, i) => {
          const dark = i === 0;
          const accentText = dark ? "#fff" : "var(--ink)";
          const stroke = dark ? "rgba(255,255,255,0.55)" : "rgba(10,10,10,0.7)";
          return (
            <article key={i} className="lift" data-cursor="text" data-cursor-label="Домен"
            style={{
              flex: "0 0 auto", width: 384, height: 480, scrollSnapAlign: "start",
              background: dark ? "#0A0A0A" : "var(--bg-card)",
              color: accentText,
              borderRadius: 22, padding: 24,
              position: "relative", overflow: "hidden",
              display: "flex", flexDirection: "column", justifyContent: "space-between",
              border: "1px solid var(--hair)"
            }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", position: "relative", zIndex: 2 }}>
                <DotCluster size={72} color="var(--accent)" />
                <div className="upper-mono" style={{ color: dark ? "rgba(255,255,255,0.5)" : "var(--dim)" }}>
                  0{i + 1} / 0{items.length}
                </div>
              </div>

              {/* domain-specific animated visual */}
              <DomainVisual kind={it.kind} stroke={stroke} dark={dark} />

              <div style={{ position: "relative", zIndex: 2 }}>
                <div style={{ fontFamily: "var(--sans)", fontWeight: 500, fontSize: 36, letterSpacing: "-0.025em", lineHeight: 1, color: accentText }}>
                  {it.name}
                </div>
                <p className="body-mono" style={{ marginTop: 10, maxWidth: "32ch", color: dark ? "rgba(255,255,255,0.7)" : "var(--muted)", fontSize: 13, lineHeight: 1.5 }}>
                  {it.desc}
                </p>
              </div>
            </article>);

        })}
      </div>
    </section>);

}

/* ============== CASES — asymmetric Bento, each card own color — section 03 ============== */
function Cases() {
  const ref = useReveal();
  // each case in its own original color (not lifted from anyone)
  const palette = {
    mk: { bg: "#EA0A23", text: "#fff" }, // accent red
    fn: { bg: "#0A0A0A", text: "#fff" }, // black
    yu: { bg: "#1A1A1A", text: "#fff" }, // off-black
    hr: { bg: "#F2F1EC", text: "#0A0A0A" }, // light card
    sp: { bg: "#EFEDE6", text: "#0A0A0A" }, // light card 2
    op: { bg: "#EA0A23", text: "#fff" } // red big
  };
  return (
    <section id="Кейсы" style={{ paddingTop: 140 }}>
      <div className="container" style={{ display: "grid", gridTemplateColumns: "1fr 1.4fr", gap: 32, alignItems: "end", marginBottom: 48 }}>
        <div ref={ref} className="reveal">
          <SectionNum label="Кейсы" num="03" />
        </div>
        <p className="lead reveal" ref={useReveal()} style={{ maxWidth: "44ch" }}>
          Что уже сделали для первых партнёров — от пилотного <Bracket accent>эпизода</Bracket> до production-датасетов.
        </p>
      </div>

      <div className="container" style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 16 }}>
        <CaseCard color={palette.mk} tag="[VLA + СБОР ДАННЫХ + DATASET]" title="HMND 01 × Schaeffler: bin picking для Humanoid" sub="Сбор и разметка multimodal-данных для VLA-моделей KinetIQ System 1" kpi={{ v: "60 totes/час", l: "производительность в пилоте Siemens" }} chip="+ 1 000+ роботов в pipeline Schaeffler" href="cases/humanoid-hmnd-01.html" decoration="hand" big />
        <div style={{ display: "grid", gap: 16 }}>
          <CaseCard color={palette.fn} tag="[ГОТОВЫЕ ДАТАСЕТЫ]" title="Recovery Pack для humanoid OEM" sub="проскальзывание / столкновение / неуспешный захват / падение" kpi={{ v: "15%", l: "recovery в поставке" }} chip="+ 22,6% к успешности задач" href="cases/recovery-pack-humanoid.html" oneLine />
          <CaseCard color={palette.yu} tag="[ИТ-КОНСАЛТИНГ]" title="Аудит robotics-data pipeline" sub="Pre-scale assessment · NIST AI RMF · ISO/IEC 5259" kpi={{ v: "×5", l: "ускорение разметки" }} chip="+ < 5 мс sync · − 62 % re-work" href="cases/audit-robotics-data-pipeline.html" oneLine />
        </div>
        <div style={{ display: "grid", gap: 16 }}>
          <CaseCard color={palette.hr} tag="[VERTICAL SOLUTION]" title="Сервисная линия для мобильного робота" sub="Россия · RU/CIS партнёр" kpi={{ v: "≥ 99,2 %", l: "точность IoU" }} href="cases/moros-service-line.html" decoration="amr" />
          <CaseCard color={palette.sp} tag="[БЕНЧМАРК]" title="DROID vs наш AI Pre-Label" sub="5 000 эпизодов · Reproducible protocol" kpi={{ v: "× 8", l: "speedup аннотации" }} href="cases/droid-vs-ai-pre-label.html" decoration="race" />
        </div>
        <CaseCard color={palette.op} tag="[CV + MULTIMODAL + AGRITECH]" title="Русагро: AI-диагностика патологий конечностей у свиней" sub="Multimodal-датасет для ранней детекции хромоты — выше академического SOTA" kpi={{ v: "97,8%", l: "точность ранней детекции" }} chip="+ 3,1 п.п. над SOTA PoseGait-MT (94,7%)" href="cases/rusagro-pig-lameness.html" decoration="pig" big />
      </div>

      <div className="container" style={{ display: "flex", justifyContent: "flex-end", marginTop: 20 }}>
        <a href="#Кейсы" className="upper-mono" data-cursor="big" style={{ color: "var(--ink)" }}>Все кейсы →</a>
      </div>
    </section>);

}

function CaseCard({ color, tag, title, sub, kpi, big, href, chip, decoration, oneLine }) {
  const onDark = color.text === "#fff";
  const Tag = href ? "a" : "article";
  return (
    <Tag {...href ? { href } : {}} className="card lift" data-cursor="text" data-cursor-label="Кейс"
    style={{
      background: color.bg, color: color.text,
      minHeight: big ? 540 : 260,
      padding: 28,
      display: "flex", flexDirection: "column", justifyContent: "space-between",
      position: "relative", overflow: "hidden",
      border: "1px solid " + (onDark ? "rgba(255,255,255,0.08)" : "var(--hair-strong)"),
      textDecoration: "none"
    }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 12, position: "relative", zIndex: 2 }}>
        <span style={{ padding: "8px 14px", borderRadius: 999, background: onDark ? "rgba(255,255,255,0.14)" : "rgba(0,0,0,0.06)", fontFamily: "var(--mono)", fontSize: 11, textTransform: "uppercase", letterSpacing: "0.08em" }}>{tag}</span>
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M7 17 L17 7 M9 7 L17 7 L17 15" strokeLinecap="square" /></svg>
      </div>

      {big && decoration === "hand" ?
      <div style={{ position: "absolute", inset: 0, opacity: 0.5, pointerEvents: "none", backgroundImage: "radial-gradient(rgba(255,255,255,0.18) 1px, transparent 1px)", backgroundSize: "18px 18px" }} /> :
      null}

      {big && decoration !== "hand" ?
      <div style={{ position: "absolute", right: "-10%", top: "20%", opacity: 0.4, pointerEvents: "none" }}>
          <HalftoneRing size={420} color={onDark ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.08)"} />
        </div> :
      null}

      {decoration === "pig" ?
      <div className="case-anim-deco" style={{ position: "absolute", inset: 0, pointerEvents: "none", zIndex: 1, overflow: "hidden" }}>
          {/* dotted grid background */}
          <div style={{ position: "absolute", inset: 0, backgroundImage: "radial-gradient(rgba(255,255,255,0.10) 1px, transparent 1px)", backgroundSize: "14px 14px" }} />
          {/* heatmap glow bottom-right */}
          <div style={{ position: "absolute", right: -60, bottom: -60, width: 320, height: 320, borderRadius: "50%", background: "radial-gradient(circle, rgba(255,255,255,0.18) 0%, transparent 65%)" }} />
          {/* skeletal trace SVG */}
          <svg viewBox="0 0 360 220" style={{ position: "absolute", right: 0, top: "20%", width: "62%", opacity: 0.95 }} fill="none" stroke="rgba(255,255,255,0.35)" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
            {/* pig silhouette side-view */}
            <path d="M40 130 Q40 95 70 88 Q95 70 145 70 Q210 70 270 80 Q310 84 320 102 Q330 120 318 134 Q310 142 296 142 L80 142 Q50 142 40 130 Z" />
            {/* snout + ear */}
            <path d="M40 118 Q24 116 22 128 Q26 138 40 134" />
            <path d="M70 80 L66 60 L82 70 Z" fill="rgba(255,255,255,0.18)" stroke="none" />
            {/* legs */}
            <line x1="95" y1="142" x2="92" y2="190" strokeWidth="1.6" />
            <line x1="130" y1="142" x2="128" y2="190" strokeWidth="1.6" />
            <line x1="240" y1="142" x2="238" y2="190" strokeWidth="1.6" />
            <line x1="280" y1="142" x2="282" y2="190" strokeWidth="1.6" />
            {/* skeletal connecting lines */}
            <polyline points="40,124 70,90 145,72 220,76 296,92 318,118" stroke="rgba(255,255,255,0.45)" strokeDasharray="2 3" />
            <polyline points="95,142 92,190" stroke="rgba(255,255,255,0.45)" strokeDasharray="2 3" />
            <polyline points="130,142 128,190" stroke="rgba(255,255,255,0.45)" strokeDasharray="2 3" />
            <polyline points="240,142 238,190" stroke="rgba(255,255,255,0.45)" strokeDasharray="2 3" />
            <polyline points="280,142 282,190" stroke="rgba(255,255,255,0.45)" strokeDasharray="2 3" />
            {/* 15 keypoints — last one (right rear hoof) is the anomaly */}
            <circle className="pig-kp k1" cx="40" cy="124" r="3.5" fill="#fff" />
            <circle className="pig-kp k2" cx="70" cy="90" r="3.5" fill="#fff" />
            <circle className="pig-kp k3" cx="100" cy="78" r="3.5" fill="#fff" />
            <circle className="pig-kp k4" cx="145" cy="72" r="3.5" fill="#fff" />
            <circle className="pig-kp k5" cx="195" cy="74" r="3.5" fill="#fff" />
            <circle className="pig-kp k6" cx="245" cy="78" r="3.5" fill="#fff" />
            <circle className="pig-kp k7" cx="296" cy="92" r="3.5" fill="#fff" />
            <circle className="pig-kp k8" cx="318" cy="118" r="3.5" fill="#fff" />
            <circle className="pig-kp k9" cx="95" cy="142" r="3.5" fill="#fff" />
            <circle className="pig-kp k10" cx="92" cy="190" r="3.5" fill="#fff" />
            <circle className="pig-kp k11" cx="130" cy="142" r="3.5" fill="#fff" />
            <circle className="pig-kp k12" cx="128" cy="190" r="3.5" fill="#fff" />
            <circle className="pig-kp k13" cx="240" cy="142" r="3.5" fill="#fff" />
            <circle className="pig-kp k14" cx="238" cy="190" r="3.5" fill="#fff" />
            {/* anomaly: right rear hoof — stays red */}
            <circle className="pig-kp pig-anomaly k15" cx="282" cy="190" r="5" fill="#fff" />
            <circle className="pig-anomaly-ring" cx="282" cy="190" r="10" stroke="#fff" strokeWidth="1.2" />
          </svg>
        </div> :
      null}

      {decoration === "hand" ?
      <div className="hmnd-hand-deco" style={{ position: "absolute", right: -10, top: 40, opacity: 0.7, pointerEvents: "none", transform: "rotate(15deg)" }}>
          <svg width="320" height="320" viewBox="0 0 320 320" fill="none" stroke="#fff" strokeWidth="1.5">
            <rect x="120" y="160" width="110" height="115" rx="14" />
            <rect x="140" y="275" width="70" height="32" rx="8" />
            <g className="hmnd-finger" style={{ transformOrigin: "130px 160px" }}>
              <rect x="125" y="100" width="18" height="68" rx="6" />
              <circle cx="134" cy="100" r="5" />
            </g>
            <g className="hmnd-finger" style={{ transformOrigin: "155px 160px", animationDelay: "0.1s" }}>
              <rect x="150" y="80" width="18" height="88" rx="6" />
              <circle cx="159" cy="80" r="5" />
            </g>
            <g className="hmnd-finger" style={{ transformOrigin: "180px 160px", animationDelay: "0.2s" }}>
              <rect x="175" y="70" width="18" height="98" rx="6" />
              <circle cx="184" cy="70" r="5" />
            </g>
            <g className="hmnd-finger" style={{ transformOrigin: "205px 160px", animationDelay: "0.3s" }}>
              <rect x="200" y="85" width="18" height="83" rx="6" />
              <circle cx="209" cy="85" r="5" />
            </g>
            <g className="hmnd-thumb" style={{ transformOrigin: "230px 200px" }}>
              <rect x="222" y="186" width="55" height="20" rx="6" transform="rotate(35 230 200)" />
              <circle cx="270" cy="172" r="5" />
            </g>
            <rect x="80" y="200" width="56" height="38" rx="4" stroke="#fff" opacity="0.9" />
            <line x1="80" y1="210" x2="136" y2="210" />
          </svg>
        </div> :
      null}

      {decoration === "gripper" ?
      <div style={{ position: "absolute", right: -6, top: 28, opacity: 0.85, pointerEvents: "none" }}>
          <svg width="220" height="220" viewBox="0 0 200 200" fill="none">
            {/* wrist + mount */}
            <line x1="100" y1="20" x2="100" y2="52" stroke="#fff" strokeWidth="1.5" opacity="0.55" strokeLinecap="round" />
            <rect x="62" y="48" width="76" height="11" rx="4" stroke="#fff" strokeWidth="1.5" opacity="0.55" />
            {/* detection arc */}
            <circle className="rg-arc" cx="100" cy="104" r="48" stroke="#EA0A23" strokeWidth="1.6" strokeDasharray="2 6" opacity="0" />
            {/* re-grasp pulse */}
            <circle className="rg-pulse" cx="100" cy="104" r="26" stroke="#3FBF7F" strokeWidth="1.8" opacity="0" />
            {/* left jaw */}
            <g className="rg-jaw-l">
              <rect x="76" y="58" width="11" height="48" rx="4" stroke="#fff" strokeWidth="1.5" opacity="0.6" />
              <rect x="76" y="98" width="24" height="10" rx="3" stroke="#fff" strokeWidth="1.5" opacity="0.6" />
            </g>
            {/* right jaw */}
            <g className="rg-jaw-r">
              <rect x="113" y="58" width="11" height="48" rx="4" stroke="#fff" strokeWidth="1.5" opacity="0.6" />
              <rect x="100" y="98" width="24" height="10" rx="3" stroke="#fff" strokeWidth="1.5" opacity="0.6" />
            </g>
            {/* object */}
            <circle className="rg-obj" cx="100" cy="104" r="10" fill="#fff" opacity="0.85" />
            {/* slip marker */}
            <circle className="rg-slip" cx="126" cy="132" r="3.5" fill="#EA0A23" />
            {/* recovered check */}
            <path className="rg-rec" d="M132 150 l5 5 l10 -11" stroke="#3FBF7F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </div> :
      null}

      {decoration === "pipeline" ?
      <div className="pl-deco" style={{ position: "absolute", right: -2, top: 0, bottom: 0, width: "44%", display: "flex", alignItems: "center", justifyContent: "flex-end", opacity: 0.72, pointerEvents: "none", zIndex: 1, overflow: "hidden" }}>
          <svg width="150" height="184" viewBox="0 0 150 184" fill="none" style={{ maxWidth: "100%" }}>
            {/* connectors */}
            <g stroke="rgba(255,255,255,0.4)" strokeWidth="1.2">
              <line x1="60" y1="36" x2="60" y2="52" />
              <line x1="60" y1="82" x2="60" y2="98" />
              <line x1="60" y1="128" x2="60" y2="144" />
            </g>
            <g fill="rgba(255,255,255,0.4)">
              <path d="M57 49 L60 53 L63 49 Z" />
              <path d="M57 95 L60 99 L63 95 Z" />
              <path d="M57 141 L60 145 L63 141 Z" />
            </g>
            {/* blocks */}
            <g fill="none" stroke="rgba(255,255,255,0.5)" strokeWidth="1.5">
              <rect x="8" y="6" width="104" height="30" rx="4" />
              <rect x="8" y="52" width="104" height="30" rx="4" />
              <rect x="8" y="98" width="104" height="30" rx="4" />
              <rect x="8" y="144" width="104" height="30" rx="4" />
            </g>
            {/* labels */}
            <g fill="rgba(255,255,255,0.65)" fontFamily="'JetBrains Mono', monospace" fontSize="9" textAnchor="middle" letterSpacing="0.5">
              <text x="60" y="24">INGEST</text>
              <text x="60" y="70">STORE</text>
              <text x="60" y="116">LABEL</text>
              <text x="60" y="162">EXPORT</text>
            </g>
            {/* scan bar */}
            <g className="pl-scan">
              <rect x="2" y="0" width="116" height="14" rx="3" fill="rgba(234,10,35,0.18)" />
              <rect x="2" y="6" width="116" height="2.5" fill="#EA0A23" />
            </g>
            {/* PASS / WARN indicators */}
            <g className="pl-ind">
              <circle cx="124" cy="21" r="9" fill="#3FBF7F" />
              <path d="M120 21 l3 3 l5 -6" stroke="#fff" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round" />
            </g>
            <g className="pl-ind">
              <circle cx="124" cy="67" r="9" fill="#3FBF7F" />
              <path d="M120 67 l3 3 l5 -6" stroke="#fff" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round" />
            </g>
            <g className="pl-ind">
              <circle cx="124" cy="113" r="9" fill="#EA0A23" />
              <line x1="124" y1="108" x2="124" y2="114" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" />
              <circle cx="124" cy="117.6" r="1.2" fill="#fff" />
            </g>
            <g className="pl-ind">
              <circle cx="124" cy="159" r="9" fill="#3FBF7F" />
              <path d="M120 159 l3 3 l5 -6" stroke="#fff" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round" />
            </g>
          </svg>
          <div className="pl-status" style={{ position: "absolute", right: 8, bottom: 8, fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.06em", color: "rgba(255,255,255,0.85)", textAlign: "right", lineHeight: 1.5 }}>
            2 / 4 PASS · 1 / 4 WARN
          </div>
        </div> :
      null}

      {decoration === "amr" ?
      <div className="amr-deco case-anim-deco" style={{ position: "absolute", right: 22, top: "30%", width: "36%", opacity: 0.9, pointerEvents: "none", zIndex: 1 }}>
          <svg viewBox="0 0 200 150" fill="none" style={{ width: "100%", height: "auto" }}>
            {/* shelves (outline rectangles) */}
            <g stroke="rgba(0,0,0,0.32)" strokeWidth="1.4">
              <rect x="20" y="16" width="48" height="20" rx="2" />
              <rect x="128" y="22" width="48" height="20" rx="2" />
              <rect x="30" y="110" width="48" height="20" rx="2" />
              <rect x="126" y="106" width="48" height="20" rx="2" />
            </g>
            {/* dashed zig-zag route */}
            <path id="amr-path" d="M44 40 L92 58 L152 46 L120 84 L54 80 L120 110" stroke="rgba(234,10,35,0.42)" strokeWidth="1.6" strokeDasharray="4 5" />
            {/* station ticks (green, appear as AMR passes) */}
            <g strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" fill="none" stroke="#1F8A5B">
              <path className="amr-tick t1" d="M40 26 l3 3 l6 -7" opacity="0" />
              <path className="amr-tick t2" d="M148 32 l3 3 l6 -7" opacity="0" />
              <path className="amr-tick t3" d="M50 120 l3 3 l6 -7" opacity="0" />
              <path className="amr-tick t4" d="M146 116 l3 3 l6 -7" opacity="0" />
            </g>
            {/* the AMR dot travelling the route (CSS offset-path for hover control) */}
            <circle className="amr-bot" cx="0" cy="0" r="5" fill="#EA0A23" />
            <circle className="amr-bot amr-bot-ring" cx="0" cy="0" r="9" fill="none" stroke="rgba(234,10,35,0.35)" strokeWidth="1.2" />
          </svg>
        </div> :
      null}

      {decoration === "amr" ?
      <span className="amr-cta" style={{ position: "absolute", right: 24, bottom: 20, zIndex: 3, fontFamily: "var(--mono)", fontSize: 11, textTransform: "uppercase", letterSpacing: "0.1em", color: "var(--accent)", opacity: 0, transform: "translateY(6px)", transition: "opacity .3s, transform .3s", pointerEvents: "none", whiteSpace: "nowrap" }}>Смотреть кейс ↗</span> :
      null}

      {decoration === "race" ?
      <div className="race-deco case-anim-deco" style={{ position: "absolute", right: 0, top: 0, bottom: 0, width: "46%", display: "flex", alignItems: "center", justifyContent: "flex-end", opacity: 0.95, pointerEvents: "none", zIndex: 1, overflow: "hidden", paddingRight: 18 }}>
          <svg viewBox="0 0 214 150" fill="none" style={{ width: "100%", height: "auto", maxWidth: 260 }}>
            <defs>
              <clipPath id="race-clip-m"><rect x="4" y="44" width="158" height="15" rx="7.5" /></clipPath>
              <clipPath id="race-clip-a"><rect x="4" y="100" width="158" height="15" rx="7.5" /></clipPath>
            </defs>
            {/* MANUAL */}
            <text x="4" y="36" fontFamily="'JetBrains Mono', monospace" fontSize="9" fill="rgba(10,10,10,0.55)" letterSpacing="0.5">MANUAL</text>
            <rect x="4" y="44" width="158" height="15" rx="7.5" stroke="rgba(10,10,10,0.28)" strokeWidth="1.5" />
            <g clipPath="url(#race-clip-m)">
              <rect className="race-fill-m" x="4" y="44" width="158" height="15" fill="#EA0A23" />
            </g>
            {/* AI PRE-LABEL */}
            <text x="4" y="92" fontFamily="'JetBrains Mono', monospace" fontSize="9" fill="rgba(10,10,10,0.7)" letterSpacing="0.5">AI PRE-LABEL</text>
            <rect x="4" y="100" width="158" height="15" rx="7.5" stroke="rgba(10,10,10,0.5)" strokeWidth="1.5" />
            <g clipPath="url(#race-clip-a)">
              <rect className="race-fill-a" x="4" y="100" width="158" height="15" fill="#EA0A23" />
            </g>
            {/* ×8 result */}
            <text className="race-x8" x="172" y="113" fontFamily="'JetBrains Mono', monospace" fontWeight="600" fontSize="17" fill="#EA0A23">× 8</text>
          </svg>
        </div> :
      null}

      {decoration === "race" ?
      <span className="amr-cta" style={{ position: "absolute", right: 24, bottom: 20, zIndex: 3, fontFamily: "var(--mono)", fontSize: 11, textTransform: "uppercase", letterSpacing: "0.1em", color: "var(--accent)", opacity: 0, transform: "translateY(6px)", transition: "opacity .3s, transform .3s", pointerEvents: "none", whiteSpace: "nowrap" }}>Смотреть кейс ↗</span> :
      null}

      <div style={{ position: "relative", zIndex: 2 }}>
        <div style={{ fontFamily: "var(--sans)", fontWeight: 500, fontSize: big ? 40 : oneLine ? 19 : 26, letterSpacing: "-0.025em", lineHeight: 1.05, maxWidth: big ? "16ch" : oneLine ? "none" : "15ch", whiteSpace: oneLine ? "nowrap" : "normal" }}>
          {title}
        </div>
        <div className="body-mono" style={{ marginTop: 10, color: onDark ? "rgba(255,255,255,0.75)" : "var(--muted)", maxWidth: big ? "44ch" : undefined }}>{sub}</div>

        <div style={{ display: "flex", gap: 12, marginTop: 20, flexWrap: "wrap", alignItems: "stretch" }}>
          {kpi ?
          <div className="case-kpi" style={{ display: "inline-flex", flexDirection: "column", padding: "14px 18px", borderRadius: 16, background: "#fff", color: "#0A0A0A", border: "1px solid var(--hair-strong)", transition: "border-color .3s" }}>
              <div style={{ fontFamily: "var(--mono)", fontWeight: 500, fontSize: 30, letterSpacing: "-0.02em", color: "var(--accent)", lineHeight: 1 }}>{kpi.v}</div>
              <div className="upper-mono" style={{ marginTop: 4, color: "var(--muted)" }}>{kpi.l}</div>
            </div> :
          null}
          {chip ?
          <div style={{ display: "inline-flex", alignItems: "center", padding: "10px 14px", borderRadius: 999, background: "rgba(255,255,255,0.08)", border: "1px solid rgba(255,255,255,0.3)", color: "#fff", fontFamily: "var(--mono)", fontSize: 11, textTransform: "uppercase", letterSpacing: "0.08em", whiteSpace: "nowrap", maxWidth: "100%" }}>
              {chip}
            </div> :
          null}
        </div>
      </div>
    </Tag>);

}

/* ============== SERVICES (accordion: pill list left + colored panel right) — section 04 ============== */
function Services() {
  const services = [
  { key: "plat", name: "Платформа разметки", desc: "Self-serve среда: загрузка эпизодов, AI pre-label, human review, экспорт в LeRobot / RT-X / OXE. От 100 000 ₽/мес." },
  { key: "collect", name: "Сбор данных под ключ", desc: "Managed teleoperation + ego-recording + UMI + data gloves. Sync ≤ 50 мс, F/T sensors, tactile maps. От 7 000 ₽ за час." },
  { key: "curated", name: "Готовые датасеты", desc: "Готовые пакеты: Kitchen-1000h, Lab-Manipulation, Recovery-Pack, Deformables-Set. От 500 000 ₽ за 100 часов." },
  { key: "recovery", name: "Разметка recovery как услуга", desc: "Единственный SKU разметки ошибок: 5–15% recovery + 5% hard failures + 80–90% success. Add-on +30%." },
  { key: "consult", name: "ИТ-консалтинг", desc: "Аудит pipeline сбора и разметки. Интервью с CTO, анализ stack, документ с рекомендациями. По запросу." }];

  const [active, setActive] = useState("plat");
  const cur = services.find((s) => s.key === active);
  const idx = services.findIndex((s) => s.key === active);

  // Auto-cycle through services at a readable pace. Resets whenever `active`
  // changes — so a manual click restarts the timer instead of fighting it.
  useEffect(() => {
    const t = setTimeout(() => {
      setActive(services[(idx + 1) % services.length].key);
    }, 4600);
    return () => clearTimeout(t);
  }, [active, idx]);

  return (
    <section id="Услуги" className="container" style={{ paddingTop: 160 }}>
      <div style={{ textAlign: "center", marginBottom: 30 }}>
        <SectionNum label="Услуги" num="04" />
      </div>
      <h2 className="h2-mid reveal" ref={useReveal()} style={{ textAlign: "center", maxWidth: "22ch", margin: "0 auto 80px" }}>
        Работаем как технологический партнёр под <Bracket>VLA-данные</Bracket> — от первого эпизода до production.
      </h2>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1.1fr", gap: 32, alignItems: "stretch" }}>
        <div>
          {services.map((s) =>
          <button key={s.key} className={`svc-pill ${active === s.key ? "active" : ""}`} onClick={() => setActive(s.key)} data-cursor="big">
              {s.name}
            </button>
          )}
        </div>

        <article style={{ background: "var(--accent)", color: "#fff", borderRadius: 28, padding: 36, position: "relative", overflow: "hidden", minHeight: 480 }}>
          {/* corner decor: 4-dot */}
          <div style={{ position: "absolute", right: 28, bottom: 28, opacity: 0.95 }}>
            <DotCluster size={110} color="#fff" />
          </div>
          {/* top right diagonal dots */}
          <div style={{ position: "absolute", right: 28, top: 28, display: "flex", gap: 6 }}>
            {[1, 2, 3, 4].map((i) => <span key={i} style={{ width: 6, height: 6, borderRadius: "50%", background: "#fff", opacity: 0.85 - i * 0.15 }} />)}
          </div>

          <div style={{ display: "flex", flexDirection: "column", height: "100%", justifyContent: "space-between", position: "relative" }}>
            <div key={cur.key} className="svc-swap">
              <div className="upper-mono" style={{ color: "rgba(255,255,255,0.7)" }}>04 · услуга · {String(idx + 1).padStart(2, "0")} / {String(services.length).padStart(2, "0")}</div>
              <div style={{ marginTop: 18, fontFamily: "var(--sans)", fontWeight: 500, fontSize: "clamp(40px, 5.5vw, 76px)", letterSpacing: "-0.03em", lineHeight: 0.96, maxWidth: "11ch" }}>
                {cur.name}
              </div>
              <p style={{ marginTop: 20, fontSize: 16, lineHeight: 1.5, maxWidth: "40ch" }}>
                {cur.desc}
              </p>
            </div>
          </div>
        </article>
      </div>

      {/* ===== Featured product плашка: V1 Data Capture ===== */}
      <div className="reveal" ref={useReveal()} style={{ marginTop: 96 }}>
        <div style={{ textAlign: "center", marginBottom: 28 }}>
          <div className="upper-mono" style={{ color: "var(--accent)" }}>Новый продукт</div>
          <h3 style={{ fontFamily: "var(--sans)", fontWeight: 500, fontSize: "clamp(24px, 2.6vw, 34px)", letterSpacing: "-0.02em", marginTop: 12 }}>
            V1 для заработка — сбор POV-данных под AI
          </h3>
        </div>

        <a href="pages/v1-data-capture.html" className="lift" data-cursor="big"
          style={{ display: "block", background: "var(--ink)", color: "#fff", borderRadius: 28, padding: "clamp(28px, 4vw, 48px)", position: "relative", overflow: "hidden", textDecoration: "none" }}>
          <div style={{ position: "absolute", right: 28, top: 28, display: "flex", gap: 6 }}>
            {[1, 2, 3, 4].map((i) => <span key={i} style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--accent)", opacity: 1 - i * 0.18 }} />)}
          </div>

          <div className="vdc-grid" style={{ display: "grid", gridTemplateColumns: "1.3fr 1fr", gap: 40, alignItems: "flex-end" }}>
            <div>
              <div className="upper-mono" style={{ color: "rgba(255,255,255,0.6)" }}>[Станьте сопричастны к воплощению будущего]</div>
              <div style={{ marginTop: 16, fontFamily: "var(--sans)", fontWeight: 500, fontSize: "clamp(32px, 4.2vw, 58px)", letterSpacing: "-0.03em", lineHeight: 0.98, maxWidth: "16ch" }}>
                Снимайте обычную жизнь — <span style={{ color: "var(--accent)" }}>получайте&nbsp;деньги</span>
              </div>
              <p style={{ marginTop: 20, fontSize: 16, lineHeight: 1.5, color: "rgba(255,255,255,0.7)", maxWidth: "46ch" }}>
                iOS-приложение для сбора POV-видео и production-ready датасеты egocentric-данных для VLA-моделей. B2C и B2B в одном продукте.
              </p>
              <span style={{ display: "inline-flex", alignItems: "center", gap: 10, marginTop: 28, padding: "16px 28px", borderRadius: 999, background: "var(--accent)", color: "#fff", fontFamily: "var(--sans)", fontWeight: 500, fontSize: 13, letterSpacing: "0.1em", textTransform: "uppercase" }}>
                Подробнее →
              </span>
            </div>
            <div className="vdc-metrics" style={{ display: "flex", flexDirection: "column", gap: 16 }}>
              {[["500 ₽", "за час съёмки"], ["1 М ч", "план к Q1 2028"], ["до 80 000 ₽", "доход в месяц"]].map(([n, l]) =>
              <div key={l} style={{ borderTop: "1px solid rgba(255,255,255,0.15)", paddingTop: 14 }}>
                  <div style={{ fontFamily: "var(--sans)", fontWeight: 500, fontSize: 30, letterSpacing: "-0.02em" }}>{n}</div>
                  <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "rgba(255,255,255,0.55)", textTransform: "uppercase", letterSpacing: "0.06em", marginTop: 4 }}>{l}</div>
                </div>
              )}
            </div>
          </div>
        </a>
      </div>
    </section>);

}

/* ============== TEAM — auto-scrolling marquee of REAL people (synced with /pages/team) ============== */
function Team() {
  const ref = useReveal();
  // Same roster as on the team page. Initials are derived from "name".
  const employees = [
  { name: "Иван Голяков", role: "Генеральный директор · основатель", dept: "Лидерство", quote: "Данные — это язык, на котором роботы учатся понимать наш мир. Наша работа — написать его начисто." },
  { name: "Максим Орлов", role: "Технический директор", dept: "Лидерство", quote: "Модель не бывает умнее данных, на которых выросла. Качество разметки — это потолок интеллекта машины." },
  { name: "Анна Лебедева", role: "Руководитель операций по данным", dept: "Лидерство", quote: "За каждым уверенным движением робота стоит тысяча аккуратно размеченных кадров. Порядок здесь рождает надёжность." },
  { name: "Дмитрий Романов", role: "Руководитель информационной безопасности", dept: "Лидерство", quote: "Доверие к данным начинается с их защиты. Чистый и безопасный датасет — фундамент, на котором держится всё остальное." },
  { name: "Кирилл Громов", role: "Руководитель VLA-исследований", dept: "Робототехника · ML", quote: "Робот учится не на идеале, а на честно размеченной реальности — со всеми её ошибками и восстановлениями." },
  { name: "Егор Васильев", role: "Старший инженер-робототехник", dept: "Робототехника · ML", quote: "Самый сильный момент — когда железо впервые повторяет жест человека. Этот мост строится из данных." },
  { name: "Сергей Кузнецов", role: "Инженер симуляции", dept: "Робототехника · ML", quote: "В симуляции я создаю тысячи миров. Хорошая разметка — то, что делает их правдой для робота." },
  { name: "Полина Соловьёва", role: "ML-инженер · AI-предразметка", dept: "Робототехника · ML", quote: "Предразметка — это диалог человека и модели. Машина предлагает, человек выверяет — так рождается точность." },
  { name: "Никита Зайцев", role: "Руководитель инженерии платформы", dept: "Платформа", quote: "Хорошая платформа незаметна: она просто даёт данным течь чисто и быстро. В этой тишине — вся инженерия." },
  { name: "Татьяна Морозова", role: "Старший дата-инженер", dept: "Платформа", quote: "Данные любят дисциплину. Когда поток выстроен верно, из хаоса сырых логов рождается знание." },
  { name: "Роман Белов", role: "Frontend-инженер", dept: "Платформа", quote: "Разметчик проводит с интерфейсом часы. Сделать эти часы лёгкими — тихая, но важная работа." },
  { name: "Юрий Воронцов", role: "DevOps-инженер · SRE", dept: "Платформа", quote: "Надёжность — это когда конвейер данных работает, а о нём не вспоминают. Стабильность дороже скорости." },
  { name: "Виктор Тихонов", role: "Руководитель teleop-студии", dept: "Teleop-фабрика", quote: "Через телеоперацию мы передаём роботу человеческую интуицию. Каждый эпизод — урок, записанный руками." },
  { name: "Ольга Новикова", role: "Старший руководитель разметки", dept: "Teleop-фабрика", quote: "Разметка — это ремесло внимания. Один честно отмеченный кадр стоит сотни сделанных наспех." },
  { name: "Александр Беляев", role: "Специалист по recovery-сценариям", dept: "Teleop-фабрика", quote: "Робот взрослеет не тогда, когда не ошибается, а когда умеет исправиться. Этому мы его и учим." },
  { name: "Мария Дмитриева", role: "Аудитор качества", dept: "Teleop-фабрика", quote: "Качество не проверяют в конце — его выстраивают на каждом шаге. Я страж этой правды в данных." },
  { name: "Лилия Котова", role: "Руководитель Customer Success", dept: "Работа с клиентами", quote: "Лучшие данные — те, что решают реальную задачу клиента. Я слежу, чтобы наша точность становилась их результатом." },
  { name: "Артём Карпов", role: "Sales-инженер", dept: "Работа с клиентами", quote: "Я объясняю инженерам ценность чистых данных их же языком. Честная цифра убеждает лучше любых слов." },
  { name: "Ксения Жукова", role: "Менеджер партнёрств", dept: "Работа с клиентами", quote: "Робототехника движется вперёд сообща. Хорошее партнёрство, как и хорошие данные, строится на доверии." },
  { name: "Глеб Михайлов", role: "Старший продуктовый дизайнер", dept: "Работа с клиентами", quote: "Дизайн для разметки — забота о тех, кто часами всматривается в детали. Ясность снижает усталость и ошибки." }];

  // duplicate for seamless marquee loop
  const loop = [...employees, ...employees];

  const initial = (n) => (n.trim().charAt(0) || "·").toUpperCase();

  return (
    <section id="Команда" style={{ paddingTop: 160 }}>
      <div ref={ref} className="reveal container" style={{ textAlign: "center", marginBottom: 60 }}>
        <SectionNum label="Команда" num="05" />
        <h2 style={{ fontFamily: "var(--sans)", fontWeight: 500, fontSize: "clamp(28px,3.4vw,42px)", letterSpacing: "-0.025em", lineHeight: 1.15, marginTop: 22, maxWidth: "28ch", marginLeft: "auto", marginRight: "auto" }}>
          Робототехники, ML-исследователи, операторы teleop-станций и инженеры платформы. <a href="pages/team.html" style={{ color: "var(--accent)", textDecoration: "none", borderBottom: "1px solid var(--accent)" }}>Вся команда →</a>
        </h2>
      </div>

      <div className="card-marquee">
        <div className="card-marquee-track">
          {loop.map((e, i) =>
          <a key={i} href="pages/team.html" className="lift" data-cursor="big"
          style={{
            flex: "0 0 auto",
            width: 300, height: 360,
            background: "var(--bg-card)",
            borderRadius: 24,
            padding: 24,
            display: "flex",
            flexDirection: "column",
            gap: 14,
            textDecoration: "none",
            color: "var(--ink)",
            position: "relative",
            overflow: "hidden"
          }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
                <span style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--accent)", textTransform: "uppercase", letterSpacing: "0.08em" }}>{e.dept}</span>
                <span style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--dim)" }}>№ {String(i % employees.length + 1).padStart(2, "0")}</span>
              </div>
              <div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" }}>
                <blockquote style={{
                margin: 0, position: "relative",
                fontFamily: "var(--serif)", fontStyle: "italic", fontWeight: 400,
                fontSize: 17, lineHeight: 1.42, letterSpacing: "-0.005em",
                color: "var(--ink-2)", textWrap: "pretty", textAlign: "left",
                paddingTop: 28
              }}>
                  <span aria-hidden="true" style={{
                  position: "absolute", top: -10, left: -4,
                  fontFamily: "var(--serif)", fontStyle: "normal",
                  fontSize: 56, lineHeight: 1, color: "var(--accent)", opacity: 0.22
                }}>«</span>
                  {e.quote}
                </blockquote>
              </div>
              <div>
                <div style={{ fontFamily: "var(--sans)", fontWeight: 500, fontSize: 18, letterSpacing: "-0.01em", color: "var(--ink)", lineHeight: 1.15 }}>
                  {e.name}
                </div>
                <div className="upper-mono" style={{ marginTop: 6, color: "var(--muted)" }}>
                  {e.role}
                </div>
              </div>
            </a>
          )}
        </div>
      </div>
    </section>);

}

/* ============== MEDIA — horizontal slider (placeholders) — section 06 ============== */
function Media() {
  const scroller = useRef(null);
  const articles = [
  { tag: "ГАЙД", source: "PDF", title: "Как собрать первый VLA-датасет за 30 дней", date: "Май 2026", abstract: "Пошаговая инструкция для команд, которые раньше не собирали robotics-данных — от episode-структуры до экспорта.", href: "pages/media/vla-dataset-30-days.html" },
  { tag: "БЕНЧМАРК", source: "блог", title: "DROID vs наш AI Pre-Label: IoU и скорость", date: "Апрель 2026", abstract: "Открытый отчёт о том, как предразметка влияет на скорость и точность human review на DROID benchmark.", href: "pages/media/droid-vs-ai-pre-label.html" },
  { tag: "КЕЙС", source: "видео", title: "Pi0.5 в кухонном домене: 800 эпизодов за неделю", date: "Март 2026", abstract: "12-минутный разбор полного цикла — от постановки до eval и выводов по Recovery-квотам.", href: "pages/media/pi05-kitchen-domain.html" },
  { tag: "СТАТЬЯ", source: "внешка", title: "Разметка данных под робототехнику: почему generic-платформы не работают", date: "Февраль 2026", abstract: "О том, почему VLA требуют особого tooling для proprioception, F/T-сенсорики и episode-timecodes.", href: "pages/media/robot-native-data-labeling.html" },
  { tag: "ГАЙД", source: "блог", title: "Recovery-эпизоды: что и зачем размечать", date: "Январь 2026", abstract: "Таксономия failure-классов, протокол error → recovery → resume, производственные квоты.", href: "pages/media/recovery-episodes.html" },
  { tag: "ПОДКАСТ", source: "эфир", title: "Внутри .v1: разговор о сборе данных для VLA", date: "Декабрь 2025", abstract: "47-минутный эфир с CTO о платформе, требованиях клиентов и sync ≤ 50 мс.", href: "pages/media/v1-podcast-vla-data.html" },
  { tag: "WHITEPAPER", source: "PDF", title: "Sync ≤ 50 мс: как мы синхронизируем мультисенсорику", date: "Ноябрь 2025", abstract: "Hardware-и software-методы синхронизации видео, проприоцепции и F/T в едином временном пространстве.", href: "pages/media/sync-50ms-multimodal.html" }];

  const scrollBy = (dir) => scroller.current?.scrollBy({ left: dir * innerWidth * 0.7, behavior: "smooth" });
  return (
    <section id="Медиа" style={{ paddingTop: 160 }}>
      <div className="container" style={{ display: "grid", gridTemplateColumns: "1fr auto", alignItems: "end", marginBottom: 32 }}>
        <div>
          <SectionNum label="Медиа" num="06" />
          <h2 className="h2-big" style={{ marginTop: 18 }}>Медиацентр</h2>
        </div>
        <div style={{ display: "flex", gap: 12 }}>
          <button onClick={() => scrollBy(-1)} aria-label="prev" data-cursor="big" style={{ width: 60, height: 60, background: "var(--bg-card)", color: "var(--ink)", border: "1px solid var(--hair)", borderRadius: 20, display: "flex", alignItems: "center", justifyContent: "center" }}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M15 6l-6 6 6 6" /></svg>
          </button>
          <button onClick={() => scrollBy(1)} aria-label="next" data-cursor="big" style={{ width: 60, height: 60, background: "var(--accent)", color: "#fff", borderRadius: 20, display: "flex", alignItems: "center", justifyContent: "center" }}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M9 6l6 6-6 6" /></svg>
          </button>
        </div>
      </div>

      <div ref={scroller} className="h-scroll" style={{ display: "flex", overflowX: "auto", scrollSnapType: "x mandatory", paddingLeft: 24, paddingRight: 24, paddingBottom: 24, gap: 20 }}>
        {articles.map((a, i) =>
        <a key={i} href={a.href} className="lift" data-cursor="text" data-cursor-label="Открыть"
        style={{ flex: "0 0 auto", width: 460, scrollSnapAlign: "start", background: "#fff", border: "1px solid var(--hair-strong)", borderRadius: 28, padding: 18, display: "flex", flexDirection: "column", gap: 14, color: "inherit", textDecoration: "none" }}>
            {/* cover placeholder */}
            <div style={{ aspectRatio: "16/11", borderRadius: 18, background: "var(--ink)", position: "relative", overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center" }}>
              <div style={{ position: "absolute", inset: 0, background: "repeating-linear-gradient(135deg, transparent 0 22px, rgba(255,255,255,0.04) 22px 23px)" }} />
              <HalftoneRing size={220} color="rgba(255,255,255,0.4)" />
              <div style={{ position: "absolute", left: 14, top: 14, display: "flex", gap: 8 }}>
                <span style={{ padding: "6px 12px", borderRadius: 999, background: "var(--accent)", color: "#fff", fontFamily: "var(--mono)", fontSize: 11, textTransform: "uppercase", letterSpacing: "0.08em" }}>[{a.tag}]</span>
                <span style={{ padding: "6px 12px", borderRadius: 999, background: "#fff", color: "var(--ink)", fontFamily: "var(--mono)", fontSize: 11, textTransform: "uppercase", letterSpacing: "0.08em" }}>{a.source}</span>
              </div>
            </div>
            <h3 style={{ fontFamily: "var(--sans)", fontWeight: 500, fontSize: 22, letterSpacing: "-0.015em", lineHeight: 1.2, color: "var(--ink)" }}>{a.title}</h3>
            <p className="body-mono" style={{ fontSize: 14, color: "var(--muted)" }}>{a.abstract}</p>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: "auto" }}>
              <span className="upper-mono">{a.date}</span>
              <span className="upper-mono" data-cursor="big" style={{ color: "var(--accent)" }}>читать →</span>
            </div>
          </a>
        )}
      </div>
    </section>);

}

/* ============== ABOUT — two-column with dark particle side — section 07 ============== */
function About() {
  return (
    <section id="О компании" className="container" style={{ paddingTop: 160 }}>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1.2fr", gap: 16 }}>
        <article className="card" style={{ padding: 36, minHeight: 540, display: "flex", flexDirection: "column", justifyContent: "space-between", background: "var(--bg-soft)", border: "1px solid var(--hair)" }}>
          <div>
            <SectionNum label="О компании" num="07" />
            <h3 className="h2-mid" style={{ marginTop: 28 }}>
              <span style={{ color: "var(--accent)" }}>.</span>v1
            </h3>
            <p style={{ marginTop: 20, fontSize: 16, lineHeight: 1.55, color: "var(--ink-2)", maxWidth: "46ch" }}>
              Платформа разметки мультимодальных данных для VLA-моделей и гуманоидов.
              Помогаем roboticists получать качественные данные — от первого эпизода до миллиона часов.
            </p>
          </div>
          <a href="#Контакты" className="lift" data-cursor="big" style={{ display: "inline-flex", alignItems: "center", gap: 12, padding: 18, borderRadius: 18, background: "var(--accent)", color: "#fff", maxWidth: 320 }}>
            <span style={{ width: 36, height: 36, background: "#fff", borderRadius: 10, display: "inline-flex", alignItems: "center", justifyContent: "center", color: "var(--accent)", fontFamily: "var(--serif)", fontWeight: 700, fontSize: 22 }}>.v</span>
            <span>
              <span className="upper-mono" style={{ color: "rgba(255,255,255,0.7)" }}>Группа V1</span>
              <div style={{ marginTop: 4, color: "#fff", fontSize: 15 }}>О компании</div>
            </span>
          </a>
        </article>

        <article style={{ background: "#000", borderRadius: 28, minHeight: 540, position: "relative", overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid var(--hair)" }} data-cursor="text" data-cursor-label="Видео">
          <svg width="100%" height="100%" viewBox="0 0 600 540" preserveAspectRatio="xMidYMid slice" style={{ position: "absolute", inset: 0 }}>
            {Array.from({ length: 220 }, (_, i) => {
              const a = Math.random() * Math.PI * 2;
              const r = 80 + Math.random() * 220;
              const cx = 300 + Math.cos(a) * r;
              const cy = 270 + Math.sin(a) * r * 0.6;
              const sz = Math.random() * 2 + 0.5;
              return <circle key={i} cx={cx} cy={cy} r={sz} fill={Math.random() > 0.85 ? "var(--accent)" : "#fff"} opacity={0.3 + Math.random() * 0.5} />;
            })}
          </svg>
          <div style={{ position: "relative", fontFamily: "var(--serif)", fontWeight: 700, color: "#fff", fontSize: "clamp(40px,5.6vw,84px)", letterSpacing: "-0.03em" }}>
            <span style={{ color: "var(--accent)" }}>.</span>v1 / 2026
          </div>
          <div style={{ position: "absolute", left: 24, bottom: 24, display: "grid", gap: 6 }}>
            <div className="upper-mono" style={{ color: "rgba(255,255,255,0.5)" }}>Teleop фабрики</div>
            <div style={{ color: "#fff", fontFamily: "var(--mono)", fontSize: 13 }}>Москва</div>
          </div>
        </article>
      </div>
    </section>);

}

/* ============== TELEGRAM DELIVERY (via backend on Beget VPS) ============== */
const API_BASE = "https://v1data.ru"; // прод-домен; backend подключается через /api/*
async function sendToTelegram(payload) {
  try {
    const r = await fetch(`${API_BASE}/api/submit.php`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload)
    });
    const json = await r.json().catch(() => ({}));
    return { ok: r.ok && json.ok, error: json.error };
  } catch (e) {
    return { ok: false, error: "Сеть недоступна" };
  }
}

/* ============== CONTACT FORM (with success state) ============== */
function ContactForm() {
  const [state, setState] = useState("idle"); // idle | sending | sent | error
  const [errMsg, setErrMsg] = useState("");
  const submit = async (e) => {
    e.preventDefault();
    setState("sending");
    setErrMsg("");
    const f = e.target;
    const data = {
      name: f.elements.namedItem("name")?.value,
      company: f.elements.namedItem("company")?.value,
      inn: f.elements.namedItem("inn")?.value,
      phone: f.elements.namedItem("phone")?.value,
      email: f.elements.namedItem("email")?.value,
      robotType: f.elements.namedItem("robotType")?.value,
      dataVolume: f.elements.namedItem("dataVolume")?.value,
      comment: f.elements.namedItem("comment")?.value,
      source: "Главная · /#contact",
      _hp: f.elements.namedItem("_hp")?.value // honeypot
    };
    const result = await sendToTelegram(data);
    if (result.ok) {
      setState("sent");
    } else {
      setState("error");
      setErrMsg(result.error || "Не удалось отправить заявку. Попробуйте позже.");
    }
  };
  if (state === "sent") {
    return (
      <div style={{ background: "#fff", border: "1px solid var(--accent)", borderRadius: 28, padding: 40, display: "flex", flexDirection: "column", gap: 14, alignItems: "flex-start" }}>
        <span style={{ width: 48, height: 48, borderRadius: "50%", background: "var(--accent)", color: "#fff", display: "inline-flex", alignItems: "center", justifyContent: "center" }}>
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M5 12 L10 17 L19 7" strokeLinecap="round" strokeLinejoin="round" /></svg>
        </span>
        <h3 style={{ fontFamily: "var(--sans)", fontWeight: 500, fontSize: 28, letterSpacing: "-0.02em", color: "var(--ink)" }}>Заявка отправлена</h3>
        <p style={{ color: "var(--ink-2)", fontSize: 15, lineHeight: 1.55 }}>Спасибо! Мы свяжемся в течение 24 часов в рабочие дни. Ответ придёт с office@v1data.ru.</p>
        <button type="button" onClick={() => setState("idle")} style={{ marginTop: 12, padding: "14px 24px", borderRadius: 999, border: "1px solid var(--hair-strong)", background: "transparent", color: "var(--ink)", fontFamily: "var(--mono)", fontSize: 12, textTransform: "uppercase", letterSpacing: "0.1em", cursor: "pointer" }} data-cursor="big">Отправить ещё</button>
      </div>);

  }
  return (
    <form onSubmit={submit} style={{ display: "grid", gap: 12 }} noValidate>
      {/* honeypot — скрыт от пользователя, заполняется ботами */}
      <input name="_hp" type="text" tabIndex={-1} autoComplete="off" aria-hidden="true" style={{ position: "absolute", left: "-9999px", width: 1, height: 1, opacity: 0 }} />

      <input name="name" className="pill-input" placeholder="Имя *" required data-cursor="text" data-cursor-label="Имя" />
      <input name="company" className="pill-input" placeholder="Компания / лаборатория" data-cursor="text" data-cursor-label="Компания" />
      <input name="inn" className="pill-input" placeholder="ИНН компании (10 или 12 цифр)" inputMode="numeric" pattern="[0-9]{10,12}" data-cursor="text" data-cursor-label="ИНН" />
      <input name="phone" className="pill-input" placeholder="+7 (000) 000-00-00 *" type="tel" required data-cursor="text" data-cursor-label="Телефон" />
      <input name="email" className="pill-input" placeholder="Email (корп.) *" type="email" required data-cursor="text" data-cursor-label="Email" />
      <select name="robotType" className="pill-input" defaultValue="" data-cursor="big" style={{ appearance: "none" }}>
        <option value="" disabled>Тип робота / задачи</option>
        <option>Гуманоид</option>
        <option>Мобильный манипулятор</option>
        <option>Стационарный манипулятор</option>
        <option>Девайс UMI / Gloves</option>
        <option>Другое</option>
      </select>
      <select name="dataVolume" className="pill-input" defaultValue="" data-cursor="big" style={{ appearance: "none" }}>
        <option value="" disabled>Объём данных (часы)</option>
        <option>&lt; 100</option>
        <option>100 — 500</option>
        <option>500 — 2000</option>
        <option>2000+</option>
        <option>Не знаю</option>
      </select>
      <textarea name="comment" className="pill-input pill-textarea" placeholder="Комментарий / задача" data-cursor="text" data-cursor-label="Бриф" />
      <label style={{ display: "flex", gap: 10, alignItems: "flex-start", fontFamily: "var(--mono)", fontSize: 12, color: "var(--muted)", marginTop: 8, lineHeight: 1.5 }}>
        <input type="checkbox" defaultChecked required style={{ width: 16, height: 16, accentColor: "var(--accent)", marginTop: 2, flexShrink: 0 }} />
        <span>
          Отправляя форму, я даю согласие на обработку{" "}
          <a href="legal/privacy.html" target="_blank" rel="noopener" style={{ textDecoration: "underline", color: "var(--ink)" }}>персональных данных</a>{" "}
          и принимаю{" "}
          <a href="legal/terms.html" target="_blank" rel="noopener" style={{ textDecoration: "underline", color: "var(--ink)" }}>пользовательское соглашение</a>.
        </span>
      </label>
      {state === "error" ?
      <div style={{ padding: "12px 18px", borderRadius: 14, background: "rgba(234,10,35,0.08)", border: "1px solid rgba(234,10,35,0.3)", color: "var(--accent)", fontSize: 13 }}>
          {errMsg}
        </div> :
      null}
      <button type="submit" disabled={state === "sending"} className="btn-pill btn-accent" data-cursor="text" data-cursor-label="Старт" style={{ marginTop: 8, width: "100%", padding: "22px 32px", fontSize: 14, opacity: state === "sending" ? 0.7 : 1, cursor: state === "sending" ? "wait" : "pointer", border: 0 }}>
        {state === "sending" ? "Отправляем…" : "Связаться с нами"}
        <span style={{ width: 8, height: 8, borderRadius: "50%", background: "#fff" }} />
      </button>
    </form>);

}

/* ============== CONTACT SECTION ============== */
function Contact() {
  return (
    <section id="contact" className="container" style={{ paddingTop: 160, scrollMarginTop: 80 }}>
      <span id="Контакты" style={{ position: "relative", top: -80, visibility: "hidden" }} aria-hidden="true" />
      <div className="card" style={{ background: "var(--bg-card)", padding: "60px 48px", borderRadius: 36, position: "relative", overflow: "hidden", border: "1px solid var(--hair)" }}>
        <div style={{ position: "absolute", left: "-6%", top: "10%", opacity: 0.6, pointerEvents: "none" }}>
          <HalftoneRing size={500} color="rgba(255,255,255,0.06)" />
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1.2fr 1fr", gap: 48, position: "relative" }}>
          <div>
            <h2 className="h2-big">
              Готовы к <Bracket accent>изменениям</Bracket>?
            </h2>
            <p className="lead" style={{ marginTop: 24, color: "var(--muted)", maxWidth: "34ch" }}>
              Опишите проект и оставьте контакты. Команда свяжется в течение 24 часов.
            </p>
          </div>

          <ContactForm />
        </div>
      </div>
    </section>);

}

/* ============== FOOTER — mono columns + GIANT serif wordmark ============== */
function Footer() {
  return (
    <footer style={{ marginTop: 80 }}>
      <div className="container" style={{ display: "grid", gridTemplateColumns: "1.3fr 1fr 1fr", gap: 32, padding: "60px 24px 40px", borderTop: "1px solid var(--hair-strong)" }}>
        <div className="body-mono" style={{ color: "var(--ink)" }}>
          <div style={{ fontFamily: "var(--mono)", fontSize: 13 }}><a href="mailto:office@v1data.ru" style={{ color: "inherit", textDecoration: "none" }}>OFFICE@V1DATA.RU</a></div>
          <div style={{ fontFamily: "var(--mono)", fontSize: 13, marginTop: 4 }}><a href="tel:+74991170087" style={{ color: "inherit", textDecoration: "none" }}>+7 (499) 117-00-87</a></div>
          <div style={{ fontFamily: "var(--mono)", fontSize: 13, marginTop: 4, color: "var(--muted)" }}>partners@v1data.ru</div>
          <a href="#Контакты" className="btn-pill btn-accent" style={{ marginTop: 22, width: "100%", maxWidth: 320, padding: "20px 28px", textDecoration: "none" }} data-cursor="big">
            Связаться с нами
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: "#fff" }} />
          </a>
        </div>
        <div className="body-mono" style={{ fontFamily: "var(--mono)", fontSize: 13, lineHeight: 1.6, color: "var(--ink)" }}>
          <div style={{ color: "var(--muted)" }}>Продукт</div>
          <div style={{ marginTop: 6 }}>Платформа разметки мультимодальных данных для VLA и гуманоидов</div>
          <div style={{ marginTop: 14, color: "var(--muted)" }}>Экспорт</div>
          <div style={{ marginTop: 6 }}>LeRobot v3 · Open X-Embodiment · RT-X · DROID</div>
          <div style={{ marginTop: 14, color: "var(--muted)" }}>Облако / on-prem</div>
          <div style={{ marginTop: 6 }}>AWS · GCP · Yandex Cloud · VK Cloud</div>
        </div>
        <div className="body-mono" style={{ fontFamily: "var(--mono)", fontSize: 13, lineHeight: 1.6, color: "var(--ink)" }}>
          <div style={{ color: "var(--muted)" }}>Оператор</div>
          <div style={{ marginTop: 6 }}>АО «НПО «Восход»</div>
          <div style={{ marginTop: 2, color: "var(--muted)", fontSize: 12 }}>ИНН 9705223475 · КПП 772501001</div>
          <div style={{ marginTop: 2, color: "var(--muted)", fontSize: 12 }}>ОГРН 1247700314971</div>
          <div style={{ marginTop: 14, color: "var(--muted)" }}>Офис</div>
          <div style={{ marginTop: 6 }}>Москва, ул. Ленинская Слобода, д. 26, помещ. 32/43</div>
          <div style={{ marginTop: 14, color: "var(--muted)" }}>Партнёрство</div>
          <div style={{ marginTop: 6 }}><a href="mailto:partners@v1data.ru" style={{ color: "inherit", textDecoration: "none" }}>partners@v1data.ru</a></div>
        </div>
      </div>

      <div className="container" style={{ padding: "10px 24px 30px", display: "flex", justifyContent: "center" }}>
        <svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="var(--accent)" strokeWidth="1.4">
          <line x1="10" y1="2" x2="10" y2="18" /><line x1="2" y1="10" x2="18" y2="10" />
        </svg>
      </div>

      <div className="container" style={{ padding: "0 24px 30px" }}>
        <div className="foot-mark">
          <span className="dot">.</span>v1
        </div>
      </div>

      <div className="container" style={{ display: "flex", justifyContent: "space-between", padding: "16px 24px 28px", borderTop: "1px solid var(--hair)", flexWrap: "wrap", gap: 16 }}>
        <div className="upper-mono">© 2026 АО «НПО «Восход» — разметка данных под робототехнику · <a href="pages/v1-capabilities.html" className="upper-mono" data-cursor="big" style={{ color: "var(--accent)" }}>Что мы умеем</a></div>
        <div style={{ display: "flex", gap: 24, flexWrap: "wrap" }}>
          <a href="legal/terms.html" className="upper-mono" data-cursor="big">Пользовательское соглашение</a>
          <a href="legal/privacy.html" className="upper-mono" data-cursor="big">Политика конфиденциальности</a>
          <a href="legal/security.html" className="upper-mono" data-cursor="big">Информационная безопасность</a>
        </div>
      </div>
    </footer>);

}

/* ============== COOKIE CONSENT BANNER ============== */
function CookieConsent() {
  const [show, setShow] = useState(false);
  useEffect(() => {
    try {
      if (!localStorage.getItem("v1-cookie-consent")) {
        // delay slightly so it doesn't compete with the loader
        const t = setTimeout(() => setShow(true), 1800);
        return () => clearTimeout(t);
      }
    } catch {}
  }, []);
  const accept = () => {
    try {localStorage.setItem("v1-cookie-consent", JSON.stringify({ accepted: true, at: new Date().toISOString() }));} catch {}
    setShow(false);
  };
  if (!show) return null;
  return (
    <div role="region" aria-label="Согласие на использование cookies" style={{
      position: "fixed", left: 16, right: 16, bottom: 16, zIndex: 170,
      maxWidth: 720, margin: "0 auto",
      background: "rgba(248,247,243,0.78)",
      backdropFilter: "blur(28px) saturate(180%)",
      WebkitBackdropFilter: "blur(28px) saturate(180%)",
      border: "1px solid rgba(255,255,255,0.75)",
      boxShadow: "inset 0 1px 0 rgba(255,255,255,0.85), 0 12px 36px rgba(0,0,0,0.10)",
      borderRadius: 22,
      padding: 18,
      display: "flex", gap: 14, alignItems: "center", flexWrap: "wrap"
    }}>
      <div style={{ flex: "1 1 320px", minWidth: 240, fontSize: 13, lineHeight: 1.5, color: "var(--ink-2)" }}>
        <strong style={{ color: "var(--ink)", fontWeight: 500 }}>Мы используем cookies.</strong>{" "}
        Продолжая использовать сайт, вы соглашаетесь на обработку обезличенных данных cookies в соответствии с{" "}
        <a href="legal/privacy.html" target="_blank" rel="noopener" style={{ color: "var(--ink)", textDecoration: "underline" }}>Политикой конфиденциальности</a>.
      </div>
      <div style={{ display: "flex", gap: 10, flexShrink: 0 }}>
        <a href="legal/privacy.html" target="_blank" rel="noopener" style={{ padding: "12px 18px", borderRadius: 999, border: "1px solid var(--hair-strong)", fontFamily: "var(--mono)", fontSize: 11, textTransform: "uppercase", letterSpacing: "0.1em", color: "var(--ink)", textDecoration: "none" }} data-cursor="big">Подробнее</a>
        <button onClick={accept} style={{ padding: "12px 22px", borderRadius: 999, border: 0, background: "var(--accent)", color: "#fff", fontFamily: "var(--mono)", fontSize: 11, textTransform: "uppercase", letterSpacing: "0.1em", cursor: "pointer" }} data-cursor="big">Принять</button>
      </div>
    </div>);

}

/* ============== SCROLL-TO-TOP ============== */
function ScrollTop() {
  const [show, setShow] = useState(false);
  useEffect(() => {
    const on = () => setShow(window.scrollY > 600);
    document.addEventListener("scroll", on, { passive: true });on();
    return () => document.removeEventListener("scroll", on);
  }, []);
  return (
    <button
      onClick={() => window.scrollTo({ top: 0, behavior: "smooth" })}
      aria-label="Наверх"
      data-cursor="big"
      style={{
        position: "fixed", right: 20, bottom: 20, zIndex: 90,
        width: 48, height: 48, borderRadius: "50%",
        background: "var(--ink)", color: "#fff", border: 0,
        display: "flex", alignItems: "center", justifyContent: "center",
        cursor: "pointer",
        opacity: show ? 1 : 0, transform: show ? "none" : "translateY(20px) scale(0.85)",
        pointerEvents: show ? "auto" : "none",
        transition: "opacity .3s, transform .3s, background .25s",
        boxShadow: "0 8px 20px rgba(0,0,0,0.18)"
      }}>
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="6 15 12 9 18 15" /></svg>
    </button>);

}

/* ============== APP ============== */
function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [loaderActive, setLoaderActive] = useState(t.loader);
  const [menu, setMenu] = useState(false);

  useEffect(() => {document.documentElement.style.setProperty("--accent", t.accent);}, [t.accent]);
  useEffect(() => {if (!t.loader) setLoaderActive(false);}, [t.loader]);
  useEffect(() => {
    document.body.style.overflow = menu ? "hidden" : "";
  }, [menu]);

  // Hash navigation: React mounts after the browser tries to resolve #hash,
  // and the loader keeps body locked for ~1.5s. Retry scroll several times
  // until the target id is in the DOM AND the loader is gone.
  useEffect(() => {
    let cancelled = false;
    const scrollToHash = () => {
      const h = decodeURIComponent(window.location.hash || "").replace(/^#/, "");
      if (!h) return false;
      const el = document.getElementById(h);
      if (!el) return false;
      el.scrollIntoView({ behavior: "smooth", block: "start" });
      return true;
    };

    // Retry up to 30 times over ~3s — covers loader + Babel compile delay.
    let attempts = 0;
    const tick = () => {
      if (cancelled) return;
      const done = scrollToHash();
      attempts += 1;
      if (!done && attempts < 30) setTimeout(tick, 100);
    };
    // Wait one frame so React layout settles, then start retries.
    requestAnimationFrame(() => setTimeout(tick, 50));

    const onChange = () => scrollToHash();
    window.addEventListener("hashchange", onChange);
    return () => {cancelled = true;window.removeEventListener("hashchange", onChange);};
  }, []);

  return (
    <>
      <Cursor enabled={t.cursor} />
      <Loader active={loaderActive} onDone={() => setLoaderActive(false)} />
      <Nav onMenu={() => setMenu(true)} />
      <MenuOverlay open={menu} onClose={() => setMenu(false)} />

      <Hero />
      <Scenarios />
      <Industries />
      <Cases />
      <Services />
      <Team />
      <Media />
      <About />
      <Contact />
      <Footer />
      <CookieConsent />
      <ScrollTop />

      <TweaksPanel title="Tweaks · .v1">
        <TweakSection label="Акцент">
          <TweakColor
            label="Цвет акцента"
            value={t.accent}
            onChange={(v) => setTweak("accent", v)}
            options={["#EA0A23", "#FF1F36", "#1F4FE8", "#00C896", "#FFFFFF"]} />
          
        </TweakSection>
        <TweakSection label="Эффекты">
          <TweakToggle label="Кастомный курсор" value={t.cursor} onChange={(v) => setTweak("cursor", v)} />
          <TweakToggle label="Лоадер при входе" value={t.loader} onChange={(v) => setTweak("loader", v)} />
          <TweakButton label="Показать лоадер сейчас" onClick={() => setLoaderActive(true)} />
        </TweakSection>
      </TweaksPanel>
    </>);

}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);