// Direction A — Launchpad.
// Real app-gallery feel: grid of large tiles with live previews up front,
// minimal hero, fast scan. No editorial drag.
const aStyles = {
  root: {
    position: "relative",
    width: "100%",
    height: "100%",
    overflow: "hidden",
    background: "#f5f3ee",
    color: "#141414",
    fontFamily: "'Geist', 'Inter', sans-serif"
  }
};

function DirectionA({ tweaks }) {
  const accent = getAccent(tweaks.accent);
  const ref = React.useRef(null);
  const spot = useSpotlight(ref);
  const card = tweaks.cardVariant || "stack";

  const bg = (() => {
    if (tweaks.background === "plain") return "#f5f3ee";
    if (tweaks.background === "data") {
      return `#f5f3ee url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='600' height='200' viewBox='0 0 600 200'><g fill='none' stroke='%23141414' stroke-opacity='0.05' stroke-width='0.6'><path d='M0,100 Q40,60 80,100 T160,100 T240,100 T320,100 T400,100 T480,100 T560,100 T640,100' /><path d='M0,120 Q40,90 80,120 T160,120 T240,120 T320,120 T400,120 T480,120 T560,120' /><path d='M0,80 Q40,50 80,80 T160,80 T240,80 T320,80 T400,80 T480,80 T560,80' /></g></svg>")`;
    }
    return `#f5f3ee url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='22'><circle cx='1' cy='1' r='1' fill='%23141414' fill-opacity='0.06'/></svg>")`;
  })();

  // Card variant → grid layout (we now have 3 apps)
  const grid = card === "list" ?
  { cols: "1fr", gap: 14, big: false } :
  card === "framed" ?
  { cols: "repeat(2, 1fr)", gap: 18, big: false } :
  { cols: "repeat(2, 1fr)", gap: 20, big: false };

  const liveCount = HUB_APPS.filter((a) => a.statusKind === "live").length;
  const totalCount = HUB_APPS.length;

  return (
    <div ref={ref} style={{ ...aStyles.root, background: bg }}>
      {/* Top bar — minimal, gets out of the way */}
      <header style={{ position: "relative", padding: "20px 40px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
          <Mark accent={accent.color} />
          <span style={{ width: 1, height: 16, background: "rgba(20,20,20,0.15)" }} />
          <span style={{ fontSize: 12, fontWeight: 600, letterSpacing: "0.16em", textTransform: "uppercase", color: "#505050" }}>Geotechnical Engineering Platform</span>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
          <SearchPill />
          <span style={{ display: "inline-flex", alignItems: "center", gap: 8, padding: "6px 12px", borderRadius: 999, background: "rgba(255,255,255,0.7)", border: "1px solid rgba(20,20,20,0.08)" }}>
            <span style={{ width: 6, height: 6, borderRadius: 999, background: "#10b981", animation: "hubPing 1.8s ease-out infinite" }} />
            <span style={{ fontSize: 11, fontWeight: 600, color: "#0f9460", letterSpacing: "0.08em", textTransform: "uppercase" }}>{liveCount} of {totalCount} live</span>
          </span>
          <a href={HUB_AUTHOR.linkedin} target="_blank" rel="noreferrer" title="LinkedIn"
            style={{
              width: 30, height: 30, borderRadius: 8,
              background: "rgba(255,255,255,0.7)", border: "1px solid rgba(20,20,20,0.08)",
              display: "flex", alignItems: "center", justifyContent: "center",
              color: "#0a66c2", textDecoration: "none"
            }}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.05-1.86-3.05-1.86 0-2.15 1.45-2.15 2.95v5.67H9.32V9h3.42v1.56h.05c.48-.9 1.64-1.86 3.37-1.86 3.6 0 4.27 2.37 4.27 5.45v6.3zM5.34 7.43a2.07 2.07 0 1 1 0-4.14 2.07 2.07 0 0 1 0 4.14zm1.78 13.02H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z" /></svg>
          </a>
          <a href={"mailto:" + HUB_AUTHOR.email} title={HUB_AUTHOR.email}
            style={{
              padding: "6px 12px", borderRadius: 8,
              background: "#141414", color: "#fff",
              fontSize: 12, fontWeight: 600, letterSpacing: "0.02em",
              textDecoration: "none", display: "inline-flex", alignItems: "center", gap: 6
            }}>
            Get in touch
          </a>
        </div>
      </header>

      {/* Hero strip — who + what */}
      <section style={{ position: "relative", padding: "32px 40px 24px 40px", display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 40 }}>
        <div style={{ maxWidth: 880 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 18 }}>
            <span style={{ fontSize: 11, fontWeight: 700, color: accent.color, letterSpacing: "0.22em", textTransform: "uppercase" }}>Quin Li · Portfolio</span>
            <span style={{ width: 18, height: 1, background: "rgba(20,20,20,0.2)" }} />
            <span style={{ fontSize: 11, fontWeight: 600, color: "#6b6b6b", letterSpacing: "0.14em", textTransform: "uppercase" }}>Geotechnical Engineer · Digital / AI Specialist</span>
          </div>
          <h1 style={{
            margin: 0,
            fontSize: 46,
            lineHeight: 1.05,
            letterSpacing: "-0.025em",
            fontWeight: 600,
            color: "#141414"
          }}>
            A platform of geotechnical<br />engineering tools.
          </h1>
          <p style={{ margin: "16px 0 0 0", fontSize: 15, lineHeight: 1.55, color: "#5a5a5a", maxWidth: 620 }}>
            Three production apps for working geotechnical and geophysical data — built from the field up, shipped end-to-end. Pick a tool to launch.
          </p>
        </div>
        <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4, paddingBottom: 6 }}>
          <span style={{ fontSize: 10, fontWeight: 700, color: "#9a9a9a", letterSpacing: "0.18em", textTransform: "uppercase" }}>Applications</span>
          <span style={{ fontSize: 44, fontWeight: 600, color: "#141414", letterSpacing: "-0.04em", lineHeight: 1 }}>0{HUB_APPS.length}</span>
        </div>
      </section>

      {/* Tile grid — the gallery */}
      <section style={{ position: "relative", padding: "12px 40px 80px 40px" }}>
        <div style={{ display: "grid", gridTemplateColumns: grid.cols, gap: grid.gap }}>
          {HUB_APPS.map((app, i) =>
          <LaunchTile key={app.id} app={app} accent={accent} index={i} variant={card} big={grid.big} />
          )}
        </div>
      </section>

      {/* Footer */}
      <footer style={{ position: "absolute", bottom: 0, left: 0, right: 0, padding: "16px 40px", borderTop: "1px solid rgba(20,20,20,0.08)", background: "rgba(245,243,238,0.85)", backdropFilter: "blur(8px)", display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: 12, color: "#6b6b6b" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <span style={{ fontWeight: 600, color: "#141414" }}>{HUB_AUTHOR.name}</span>
          <span>·</span>
          <span>{HUB_AUTHOR.role}</span>
        </div>
        <div style={{ display: "none" }}>{HUB_AUTHOR.org}</div>
        <div style={{ display: "flex", gap: 18 }}>
          <span>{HUB_AUTHOR.email}</span>
          <span>linkedin.com/in/quin-li</span>
        </div>
      </footer>
    </div>);

}

function Mark({ accent }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
      <svg width="22" height="22" viewBox="0 0 22 22" style={{ display: "block" }}>
        <rect x="0" y="0" width="22" height="22" fill={accent} rx="4" />
        <path d="M5 6h12M5 11h8M5 16h12" stroke="#fff" strokeWidth="1.6" />
      </svg>
      <span style={{ fontSize: 13, fontWeight: 700, letterSpacing: "0.18em", color: "#141414" }}>QUIN&nbsp;LI</span>
    </div>);

}

function SearchPill() {
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 8,
      padding: "7px 12px",
      background: "rgba(255,255,255,0.7)",
      border: "1px solid rgba(20,20,20,0.08)",
      borderRadius: 999,
      width: 220,
      fontSize: 12,
      color: "#9a9a9a"
    }}>
      <svg width="13" height="13" viewBox="0 0 13 13" fill="none">
        <circle cx="5.5" cy="5.5" r="4" stroke="#9a9a9a" strokeWidth="1.4" />
        <path d="M9 9l3 3" stroke="#9a9a9a" strokeWidth="1.4" />
      </svg>
      <span>Search applications</span>
      <span style={{ marginLeft: "auto", fontSize: 10, padding: "1px 6px", border: "1px solid rgba(20,20,20,0.12)", borderRadius: 4, color: "#6b6b6b", fontFamily: "'JetBrains Mono', monospace" }}>⌘K</span>
    </div>);

}

function FilterChip({ children, active, accent }) {
  return (
    <span style={{
      padding: "6px 12px",
      fontSize: 12,
      fontWeight: 500,
      borderRadius: 999,
      background: active ? "#141414" : "rgba(255,255,255,0.7)",
      color: active ? "#fff" : "#3a3a3a",
      border: active ? "1px solid #141414" : "1px solid rgba(20,20,20,0.08)",
      cursor: "default"
    }}>
      {children}
    </span>);

}

function LaunchTile({ app, accent, index, variant, big }) {
  const [hover, setHover] = React.useState(false);
  const isList = variant === "list";

  if (isList) return <ListRow app={app} accent={accent} index={index} hover={hover} setHover={setHover} />;

  return (
    <a
      href={app.url || "#"}
      target={app.external ? "_blank" : undefined}
      rel={app.external ? "noreferrer" : undefined}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        position: "relative",
        display: "flex",
        flexDirection: "column",
        background: "#fff",
        border: "1px solid rgba(20,20,20,0.08)",
        borderRadius: 16,
        textDecoration: "none",
        color: "inherit",
        overflow: "hidden",
        boxShadow: hover ? "0 1px 2px rgba(20,20,20,0.04), 0 24px 50px -28px rgba(20,20,20,0.36)" : "0 1px 2px rgba(20,20,20,0.03)",
        transform: hover ? "translateY(-3px)" : "none",
        transition: "all 260ms cubic-bezier(.4,0,.2,1)"
      }}>
      
      {/* Live preview — the gallery's visual anchor */}
      <AppPreview app={app} accent={accent} hover={hover} big={big} />

      <div style={{ padding: "20px 22px 22px 22px", display: "flex", flexDirection: "column", gap: 8 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <span style={{ fontSize: 10, fontWeight: 700, color: "#9a9a9a", letterSpacing: "0.16em", textTransform: "uppercase" }}>
            {String(index + 1).padStart(2, "0")} · {app.stack.split(" · ")[0]}
          </span>
          <span style={{ color: "#0f9460" }}><StatusPing kind={app.statusKind} label={app.status} /></span>
        </div>
        <h3 style={{ margin: 0, fontSize: 22, fontWeight: 600, letterSpacing: "-0.018em", color: "#141414", lineHeight: 1.15 }}>
          {app.name}
          {app.betaSup && (
            <sup style={{ fontSize: "0.5em", fontWeight: 600, color: "#9a9a9a", marginLeft: 4, fontFamily: "'JetBrains Mono', monospace", letterSpacing: "0.05em", verticalAlign: "super" }}>(beta)</sup>
          )}
        </h3>
        <p style={{ margin: 0, fontSize: 13.5, color: "#3a3a3a", lineHeight: 1.5, fontWeight: 500 }}>{app.tagline}</p>
        <p style={{ margin: "2px 0 0 0", fontSize: 11.5, color: "#9a9a9a", lineHeight: 1.4, fontFamily: "'JetBrains Mono', monospace" }}>
          {app.stack}
        </p>
        <div style={{ marginTop: 10, display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <span style={{ fontSize: 11, color: "#9a9a9a", fontFamily: "'JetBrains Mono', monospace" }}>{app.version}</span>
          <span style={{
            display: "inline-flex", alignItems: "center", gap: 6,
            padding: "7px 13px",
            fontSize: 12, fontWeight: 600,
            color: hover ? "#fff" : "#141414",
            background: hover ? accent.color : "rgba(20,20,20,0.06)",
            borderRadius: 999,
            transition: "all 200ms"
          }}>
            Launch
            <span style={{ display: "inline-block", transform: hover ? "translateX(2px)" : "none", transition: "transform 200ms" }}>→</span>
          </span>
        </div>
      </div>
    </a>);

}

function ListRow({ app, accent, index, hover, setHover }) {
  return (
    <a
      href={app.url || "#"}
      target={app.external ? "_blank" : undefined}
      rel={app.external ? "noreferrer" : undefined}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        display: "grid",
        gridTemplateColumns: "56px 1fr 200px 100px 110px",
        gap: 18,
        alignItems: "center",
        padding: "14px 18px",
        background: "#fff",
        border: "1px solid rgba(20,20,20,0.08)",
        borderRadius: 12,
        textDecoration: "none",
        color: "inherit",
        boxShadow: hover ? "0 1px 2px rgba(20,20,20,0.04), 0 14px 30px -20px rgba(20,20,20,0.28)" : "0 1px 2px rgba(20,20,20,0.03)",
        transition: "all 200ms"
      }}>
      
      <div style={{
        width: 44, height: 44, borderRadius: 10,
        background: `linear-gradient(135deg, ${accent.color}, ${accent.deep})`,
        display: "flex", alignItems: "center", justifyContent: "center",
        color: "#fff", fontWeight: 700, fontFamily: "'Fraunces', serif", fontSize: 20
      }}>{app.glyph}</div>
      <div>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <h3 style={{ margin: 0, fontSize: 16, fontWeight: 600, color: "#141414" }}>
            {app.name}
            {app.betaSup && (
              <sup style={{ fontSize: "0.55em", fontWeight: 600, color: "#9a9a9a", marginLeft: 3, fontFamily: "'JetBrains Mono', monospace", letterSpacing: "0.05em", verticalAlign: "super" }}>(beta)</sup>
            )}
          </h3>
          <span style={{ color: "#0f9460" }}><StatusPing kind={app.statusKind} label={app.status} /></span>
        </div>
        <p style={{ margin: "2px 0 0 0", fontSize: 12, color: "#6b6b6b" }}>{app.tagline}</p>
      </div>
      <span style={{ fontSize: 11, color: "#9a9a9a", fontFamily: "'JetBrains Mono', monospace" }}>{app.stack}</span>
      <span style={{ fontSize: 11, color: "#9a9a9a", fontFamily: "'JetBrains Mono', monospace" }}>{app.version}</span>
      <span style={{
        justifySelf: "end",
        padding: "7px 13px",
        fontSize: 12, fontWeight: 600,
        color: hover ? "#fff" : "#141414",
        background: hover ? accent.color : "rgba(20,20,20,0.06)",
        borderRadius: 999,
        transition: "all 200ms"
      }}>
        Launch →
      </span>
    </a>);

}

function AppPreview({ app, accent, hover, big }) {
  const h = big ? 360 : 300;
  if (app.id === "geophysics") return <GeophysicsPreview app={app} hover={hover} h={h} />;
  if (app.id === "geotech-viz") return <GeotechVizPreview app={app} accent={accent} hover={hover} h={h} />;
  if (app.id === "boreholeai") return <BoreholeAIPreview app={app} hover={hover} h={h} />;
  return null;
}

// ── Geophysics: cream-paper theme — Vs section + BH sticks + survey-line map inset ──
function GeophysicsPreview({ hover, h, big = false }) {
  const bhs = [
    { x: 60,  label: "BH-1", segs: [["#cdbb95", 16], ["#9aa6b4", 26], ["#8c7458", 18]] },
    { x: 175, label: "BH-2", segs: [["#cdbb95", 12], ["#9aa6b4", 22], ["#c98b5b", 18], ["#8c7458", 22]] },
    { x: 295, label: "BH-3", segs: [["#cdbb95", 14], ["#9aa6b4", 28], ["#c98b5b", 14], ["#3a3a3a", 18]] },
    { x: 410, label: "BH-4", segs: [["#cdbb95", 12], ["#9aa6b4", 30], ["#8c7458", 14], ["#3a3a3a", 14]] },
  ];
  return (
    <div style={{ position: "relative", height: h, overflow: "hidden", background: "#fbfaf6", borderBottom: "1px solid rgba(20,20,20,0.05)" }}>
      <svg viewBox="0 0 480 280" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" style={{ position: "absolute", inset: 0 }}>
        <defs>
          <linearGradient id="velCalm" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"  stopColor="#3d6fa0" />
            <stop offset="22%" stopColor="#5b8c92" />
            <stop offset="42%" stopColor="#a8a378" />
            <stop offset="62%" stopColor="#c98b5b" />
            <stop offset="82%" stopColor="#9a5236" />
            <stop offset="100%" stopColor="#6e3a26" />
          </linearGradient>
          <clipPath id="velSection">
            <path d="M 30,52 L 450,52 L 450,180 Q 360,220 240,216 Q 120,220 30,180 Z" />
          </clipPath>
        </defs>
        <text x="36" y="28" fontSize="8" fontWeight="700" fill="#5a5a5a" fontFamily="'JetBrains Mono', monospace" letterSpacing="0.12em">VELOCITY SECTION · S-WAVE × BOREHOLE</text>
        <g stroke="rgba(20,20,20,0.06)" strokeWidth="0.5">
          {[60,90,120,150,180,210].map((y) => <line key={"g"+y} x1="30" x2="450" y1={y} y2={y} />)}
          {[100,180,260,340,420].map((x) => <line key={"gv"+x} x1={x} y1="52" x2={x} y2="218" />)}
        </g>
        <rect x="0" y="0" width="480" height="280" fill="url(#velCalm)" clipPath="url(#velSection)" opacity="0.85" />
        <g clipPath="url(#velSection)" fill="none" stroke="#1a1a1a" strokeOpacity="0.18" strokeWidth="0.5">
          <path d="M 30,80 Q 240,72 450,82" />
          <path d="M 30,108 Q 240,98 450,112" />
          <path d="M 30,138 Q 240,128 450,144" />
          <path d="M 30,168 Q 240,160 450,172" />
        </g>
        <path d="M 30,52 L 450,52 L 450,180 Q 360,220 240,216 Q 120,220 30,180 Z" fill="none" stroke="#1a1a1a" strokeOpacity="0.45" strokeWidth="0.7" />
        <text x="22" y="58" fontSize="6" fill="#7a7a7a" fontFamily="'JetBrains Mono', monospace" textAnchor="end">0</text>
        <text x="22" y="120" fontSize="6" fill="#7a7a7a" fontFamily="'JetBrains Mono', monospace" textAnchor="end">25</text>
        <text x="22" y="180" fontSize="6" fill="#7a7a7a" fontFamily="'JetBrains Mono', monospace" textAnchor="end">50</text>
        <text x="14" y="118" fontSize="6.5" fill="#5a5a5a" fontFamily="'JetBrains Mono', monospace" transform="rotate(-90,14,118)" textAnchor="middle">depth (m)</text>
        {bhs.map((bh, i) => {
          let y = 52;
          return (
            <g key={i}>
              {bh.segs.map(([color, len], j) => {
                const seg = <rect key={j} x={bh.x - 4} y={y} width="8" height={len} fill={color} stroke="#fff" strokeWidth="0.4" />;
                y += len;
                return seg;
              })}
              <line x1={bh.x} y1={y} x2={bh.x} y2={y + 4} stroke="#1a1a1a" strokeWidth="0.6" />
              <circle cx={bh.x} cy={y + 5.5} r="1.4" fill="#1a1a1a" />
            </g>
          );
        })}
        {bhs.map((bh, i) => (
          <g key={"l"+i}>
            <line x1={bh.x} y1="42" x2={bh.x} y2="52" stroke="#1a1a1a" strokeOpacity="0.4" strokeWidth="0.5" />
            <rect x={bh.x - 14} y="36" width="28" height="10" rx="1" fill="#fff" stroke="#1a1a1a" strokeOpacity="0.35" strokeWidth="0.5" />
            <text x={bh.x} y="43" textAnchor="middle" fill="#1a1a1a" fontSize="6" fontWeight="700" fontFamily="'JetBrains Mono', monospace">{bh.label}</text>
          </g>
        ))}

        <g transform="translate(298,200)">
          <rect x="0" y="0" width="158" height="68" fill="#fff" stroke="#1a1a1a" strokeOpacity="0.4" strokeWidth="0.7" />
          {/* contour-style topo lines */}
          <g stroke="#a8a378" strokeOpacity="0.55" strokeWidth="0.5" fill="none">
            <path d="M 6,56 C 32,48 56,44 84,42 C 116,40 140,34 154,28" />
            <path d="M 6,46 C 30,38 60,32 92,28 C 122,24 142,20 154,16" />
            <path d="M 6,36 C 32,26 64,20 100,16 C 130,14 144,10 154,6" />
            <path d="M 6,26 C 36,16 70,10 108,6 C 132,4 146,2 154,1" />
          </g>
          {/* river / coastline */}
          <path d="M 0,52 C 32,58 64,44 102,54 C 130,62 146,52 158,58" stroke="#5b8c92" strokeOpacity="0.6" strokeWidth="1" fill="none" />
          {/* survey line A–A' (thicker, more prominent) */}
          <line x1="12" y1="48" x2="146" y2="20" stroke="#9a5236" strokeWidth="1.6" />
          <line x1="12" y1="48" x2="146" y2="20" stroke="#9a5236" strokeWidth="0.6" strokeDasharray="2.5 2" />
          {/* borehole dots along the line */}
          {[0.10, 0.36, 0.64, 0.92].map((t, i) => {
            const x = 12 + (146 - 12) * t;
            const y = 48 + (20 - 48) * t;
            return <circle key={i} cx={x} cy={y} r="2.4" fill="#1a1a1a" stroke="#fff" strokeWidth="0.7" />;
          })}
          {/* A and A' markers */}
          <text x="9" y="44" fontSize="7" fontWeight="800" fill="#9a5236" fontFamily="'JetBrains Mono', monospace">A</text>
          <text x="148" y="17" fontSize="7" fontWeight="800" fill="#9a5236" fontFamily="'JetBrains Mono', monospace" textAnchor="end">A′</text>
          {/* header strip */}
          <rect x="0" y="0" width="158" height="11" fill="#f0e8d6" />
          <text x="4" y="7.6" fontSize="6" fontWeight="800" fill="#5a4a2c" fontFamily="'JetBrains Mono', monospace" letterSpacing="0.14em">SURVEY PLAN · A–A′</text>
          {/* North arrow */}
          <g transform="translate(146,24)">
            <polygon points="0,-5.5 3.4,4 0,1.5 -3.4,4" fill="#1a1a1a" />
            <text x="0" y="11.5" fontSize="5.4" fill="#3a3a3a" fontFamily="'JetBrains Mono', monospace" textAnchor="middle">N</text>
          </g>
          {/* Scale bar */}
          <g transform="translate(10,62)">
            <rect x="0" y="0" width="20" height="2.2" fill="#1a1a1a" />
            <rect x="20" y="0" width="20" height="2.2" fill="#fff" stroke="#1a1a1a" strokeWidth="0.4" />
            <text x="0" y="-1.4" fontSize="4.4" fill="#5a5a5a" fontFamily="'JetBrains Mono', monospace">0</text>
            <text x="40" y="-1.4" fontSize="4.4" fill="#5a5a5a" fontFamily="'JetBrains Mono', monospace">200m</text>
          </g>
        </g>
      </svg>
      <div style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, transparent 60%, rgba(20,20,20,0.08) 100%)", opacity: hover ? 1 : 0, transition: "opacity 200ms", pointerEvents: "none" }} />
    </div>
  );
}

// ── Geotech Data Visualisation: scatter top-left + PSD bottom-right (diagonal) ──
function GeotechVizPreview({ hover, h, big = false }) {
  const grid = "rgba(20,20,20,0.08)";
  const ax = "#1a1a1a";
  return (
    <div style={{ position: "relative", height: h, overflow: "hidden", background: "#fbfaf6", borderBottom: "1px solid rgba(20,20,20,0.05)" }}>
      <svg viewBox="0 0 480 280" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" style={{ position: "absolute", inset: 0 }}>
        {/* Top-left: SPT × Depth scatter */}
        <g transform="translate(36,24)">
          <text x="0" y="-6" fontSize="7.5" fontWeight="700" fill="#5a5a5a" fontFamily="'JetBrains Mono', monospace" letterSpacing="0.12em">CROSS-PLOT · SPT × DEPTH</text>
          <line x1="0" y1="0" x2="0" y2="110" stroke={ax} strokeWidth="0.7" />
          <line x1="0" y1="110" x2="200" y2="110" stroke={ax} strokeWidth="0.7" />
          {[0,1,2,3,4,5].map((i) => <line key={"h"+i} x1="0" y1={i*22} x2="200" y2={i*22} stroke={grid} strokeWidth="0.4" />)}
          {[0,1,2,3,4].map((i) => <line key={"v"+i} x1={i*50} y1="0" x2={i*50} y2="110" stroke={grid} strokeWidth="0.4" />)}
          <text x="-4" y="6" fontSize="6" fill="#7a7a7a" fontFamily="'JetBrains Mono', monospace" textAnchor="end">d (m)</text>
          <text x="200" y="122" fontSize="6" fill="#7a7a7a" fontFamily="'JetBrains Mono', monospace" textAnchor="end">N (blows)</text>
          {[
            ["#3d6fa0", [[10,8],[18,14],[26,10],[34,18],[42,22],[52,15],[60,28],[70,20],[80,24],[92,30]]],
            ["#a8a378", [[24,46],[36,52],[46,42],[60,60],[70,50],[84,66],[100,58]]],
            ["#9a5236", [[68,82],[88,90],[104,98],[122,86],[138,102],[156,94],[174,100]]]
          ].flatMap(([c, pts], gi) => pts.map(([x,y], i) => (
            <circle key={gi+"-"+i} cx={x} cy={y} r="2.4" fill={c} fillOpacity="0.85" stroke="#fff" strokeWidth="0.5" />
          )))}
          <path d="M 6,8 Q 80,52 200,100" fill="none" stroke="#1a1a1a" strokeOpacity="0.32" strokeWidth="0.6" strokeDasharray="2 2" />
        </g>

        {/* Bottom-right: PSD curves */}
        <g transform="translate(176,158)">
          <text x="0" y="-6" fontSize="7.5" fontWeight="700" fill="#5a5a5a" fontFamily="'JetBrains Mono', monospace" letterSpacing="0.12em">PARTICLE SIZE DISTRIBUTION</text>
          <line x1="0" y1="0" x2="0" y2="92" stroke={ax} strokeWidth="0.7" />
          <line x1="0" y1="92" x2="270" y2="92" stroke={ax} strokeWidth="0.7" />
          {[0,1,2,3,4,5].map((i) => (
            <g key={"tk"+i}>
              <line x1={i*54} y1="92" x2={i*54} y2="95" stroke={ax} strokeWidth="0.6" />
              <line x1={i*54} y1="0" x2={i*54} y2="92" stroke={grid} strokeWidth="0.4" />
            </g>
          ))}
          {[0,1,2,3,4].map((i) => <line key={"gh"+i} x1="0" y1={i*23} x2="270" y2={i*23} stroke={grid} strokeWidth="0.4" />)}
          <rect x="0" y="0" width="54" height="92" fill="#9a5236" fillOpacity="0.05" />
          <rect x="54" y="0" width="54" height="92" fill="#c98b5b" fillOpacity="0.06" />
          <rect x="108" y="0" width="54" height="92" fill="#e4b85e" fillOpacity="0.05" />
          <rect x="162" y="0" width="54" height="92" fill="#a8a378" fillOpacity="0.05" />
          <rect x="216" y="0" width="54" height="92" fill="#3d6fa0" fillOpacity="0.04" />
          {[["CLAY",27],["SILT",81],["F.SAND",135],["C.SAND",189],["GRAVEL",243]].map(([t,x]) => (
            <text key={t} x={x} y="6" fontSize="5.2" fill="#7a7a7a" fontFamily="'JetBrains Mono', monospace" textAnchor="middle">{t}</text>
          ))}
          {[
            ["#3d6fa0", "M 0,88 C 22,86 44,80 72,68 C 100,52 142,30 200,16 C 230,10 256,6 270,4"],
            ["#a8a378", "M 0,90 C 28,86 56,78 92,62 C 128,42 170,22 214,12 C 240,8 262,6 270,4"],
            ["#9a5236", "M 0,82 C 22,72 44,58 78,40 C 114,24 158,14 200,8 C 230,6 258,4 270,3"]
          ].map(([c,d], i) => (
            <path key={i} d={d} fill="none" stroke={c} strokeWidth="1.6" strokeOpacity="0.9" />
          ))}
          <text x="270" y="105" fontSize="6" fill="#7a7a7a" fontFamily="'JetBrains Mono', monospace" textAnchor="end">grain (mm, log)</text>
          <text x="-4" y="6" fontSize="6" fill="#7a7a7a" fontFamily="'JetBrains Mono', monospace" textAnchor="end">% pass</text>
        </g>

        {/* Shared legend, top-right */}
        <g transform="translate(310,30)" fontFamily="'JetBrains Mono', monospace" fontSize="6.4">
          <text x="0" y="-3" fontSize="6.5" fontWeight="700" fill="#5a5a5a" letterSpacing="0.12em">SOIL UNIT</text>
          {[["#3d6fa0","ALLUVIUM"],["#a8a378","FILL"],["#9a5236","RESIDUAL"]].map(([c,l], i) => (
            <g key={l} transform={"translate(0," + (i*13) + ")"}>
              <circle cx="3" cy="3" r="2.6" fill={c} stroke="#fff" strokeWidth="0.5" />
              <text x="9" y="5.4" fill="#3a3a3a">{l}</text>
            </g>
          ))}
        </g>

        {/* Diagonal connector with hairline */}
        <line x1="190" y1="120" x2="200" y2="148" stroke="#9a5236" strokeOpacity="0.35" strokeWidth="0.6" strokeDasharray="2 2" />
      </svg>
      <span style={{ position: "absolute", bottom: 12, right: 14, fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5, color: "#5a4a2c", letterSpacing: "0.18em", textTransform: "uppercase", fontWeight: 800, background: "#f0e8d6", padding: "3px 8px", borderRadius: 2, border: "1px solid rgba(90,74,44,0.18)" }}>
        Lab + field · cross-plot · PSD
      </span>
      <div style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, transparent 60%, rgba(20,20,20,0.08) 100%)", opacity: hover ? 1 : 0, transition: "opacity 200ms", pointerEvents: "none" }} />
    </div>
  );
}

// ── BoreholeAI: isometric cartoon — digital box scanning a borehole log PDF (static, blue palette) ──
function BoreholeAIPreview({ hover, h, big = false }) {
  return (
    <div style={{ position: "relative", height: h, overflow: "hidden", background: "linear-gradient(180deg,#fbfaf6 0%,#f0e8d6 100%)", borderBottom: "1px solid rgba(20,20,20,0.05)" }}>
      <svg viewBox="0 0 480 280" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" style={{ position: "absolute", inset: 0 }}>
        <defs>
          <linearGradient id="boxFront2" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#fff" />
            <stop offset="100%" stopColor="#e6dfcf" />
          </linearGradient>
          <linearGradient id="boxSide2" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#bcae8e" />
            <stop offset="100%" stopColor="#8a7a58" />
          </linearGradient>
        </defs>

        <g stroke="#bcae8e" strokeOpacity="0.35" strokeWidth="0.5" fill="none">
          {[0,1,2,3,4,5,6].map((i) => <line key={'gl'+i} x1="0" x2="480" y1={150 + i*22} y2={150 + i*22} />)}
        </g>

        {/* PDF borehole log */}
        <g transform="translate(34,30)">
          <rect x="4" y="6" width="170" height="220" fill="#1a1a1a" opacity="0.18" />
          <rect x="0" y="0" width="170" height="220" fill="#fff" stroke="#bcae8e" strokeWidth="0.8" />
          <polygon points="158,0 170,0 170,12" fill="#f0e8d6" stroke="#bcae8e" strokeWidth="0.6" />
          <rect x="0" y="0" width="170" height="14" fill="#f0e8d6" />
          <text x="6" y="9.5" fontSize="5.5" fontWeight="800" fill="#5a4a2c" fontFamily="'JetBrains Mono', monospace" letterSpacing="0.08em">BOREHOLE LOG · BH-LP142</text>
          <line x1="0" y1="18" x2="170" y2="18" stroke="#c4baa6" strokeWidth="0.4" />
          <text x="4" y="24" fontSize="4.2" fontWeight="700" fill="#7a6a4e" fontFamily="'JetBrains Mono', monospace">DEPTH</text>
          <text x="28" y="24" fontSize="4.2" fontWeight="700" fill="#7a6a4e" fontFamily="'JetBrains Mono', monospace">LITH</text>
          <text x="56" y="24" fontSize="4.2" fontWeight="700" fill="#7a6a4e" fontFamily="'JetBrains Mono', monospace">DESCRIPTION</text>
          <text x="146" y="24" fontSize="4.2" fontWeight="700" fill="#7a6a4e" fontFamily="'JetBrains Mono', monospace">SPT</text>
          <line x1="0" y1="27" x2="170" y2="27" stroke="#c4baa6" strokeWidth="0.4" />
          {[
            ["#cdbb95", 22, "0.0–1.4",  "FILL — sandy gravel",       "—"],
            ["#9aa6b4", 32, "1.4–4.8",  "Silty CLAY, soft",          "4·6"],
            ["#c98b5b", 38, "4.8–9.5",  "Med-dense SAND",            "12·18"],
            ["#8c7458", 42, "9.5–14.0", "Wx SILTSTONE",              "30·R"],
            ["#3a3a3a", 56, "14.0–22",  "Fresh SILTSTONE, R3",       "R·R"]
          ].map(([c,hh,dep,desc,spt], i, arr) => {
            const y = 30 + arr.slice(0,i).reduce((a,r)=>a+r[1], 0);
            return (
              <g key={i}>
                <rect x="28" y={y} width="18" height={hh} fill={c} stroke="#fff" strokeWidth="0.3" />
                <text x="3" y={y + hh/2 + 1.5} fontSize="4" fill="#3a3a3a" fontFamily="'JetBrains Mono', monospace">{dep}</text>
                <text x="56" y={y + 6} fontSize="4" fill="#3a3a3a">{desc}</text>
                {[10,14,18].filter(d => d < hh - 2).map((dy) => <line key={dy} x1="56" y1={y+dy} x2="140" y2={y+dy} stroke="#bcbcbc" strokeWidth="0.3" />)}
                <text x="146" y={y + 6} fontSize="4" fill="#3a3a3a" fontFamily="'JetBrains Mono', monospace">{spt}</text>
              </g>
            );
          })}
          <rect x="56" y="98" width="80" height="32" fill="#d97757" fillOpacity="0.18" />
          <rect x="56" y="98" width="80" height="32" fill="none" stroke="#d97757" strokeWidth="0.7" strokeDasharray="2.5 1.5" />
        </g>

        {/* Scanner box */}
        <g transform="translate(238,80)">
          <ellipse cx="80" cy="142" rx="80" ry="6" fill="#1a1a1a" opacity="0.16" />
          <polygon points="14,18 130,18 154,38 38,38" fill="#fff" stroke="#1a1a1a" strokeWidth="1" />
          <rect x="14" y="38" width="116" height="100" fill="url(#boxFront2)" stroke="#1a1a1a" strokeWidth="1" />
          <polygon points="130,38 154,38 154,138 130,138" fill="url(#boxSide2)" stroke="#1a1a1a" strokeWidth="1" />
          {[0,1,2,3,4,5,6,7,8,9].map((i) => (
            <line key={'r'+i} x1="132" x2="152" y1={42 + i*10} y2={42 + i*10} stroke="#fff" strokeOpacity="0.35" strokeWidth="0.4" />
          ))}
          <rect x="24" y="50" width="92" height="58" fill="#0d2238" stroke="#1a1a1a" strokeWidth="0.8" />
          <rect x="26" y="52" width="88" height="54" fill="#13314c" />
          {[["#cdbb95",6],["#9aa6b4",9],["#c98b5b",11],["#8c7458",12],["#3a3a3a",16]].map(([c, hh], i, arr) => {
            const yy = 54 + arr.slice(0,i).reduce((a,r) => a + r[1], 0);
            return <rect key={i} x="32" y={yy} width="14" height={hh} fill={c} />;
          })}
          <g fontFamily="'JetBrains Mono', monospace" fill="#9bbfe6">
            <text x="52" y="60" fontSize="4.4">DEPTH_M  : 4.8–9.5</text>
            <text x="52" y="68" fontSize="4.4">MATERIAL : SP</text>
            <text x="52" y="76" fontSize="4.4">N_SPT    : 12 / 18</text>
            <text x="52" y="84" fontSize="4.4">GW       : 7.4 m</text>
            <text x="52" y="92" fontSize="4.4">CONF     : 0.96</text>
            <text x="52" y="100" fontSize="4.4" fill="#9bd87a">✓ CITED</text>
          </g>
          <text x="72" y="124" textAnchor="middle" fontSize="9" fontWeight="800" fill="#1a1a1a" fontFamily="'Inter', sans-serif" letterSpacing="0.04em">BoreholeAI</text>
          <text x="72" y="132" textAnchor="middle" fontSize="5" fill="#7a6a4e" fontFamily="'JetBrains Mono', monospace" letterSpacing="0.18em">PDF → STRUCTURED</text>
          <rect x="22" y="135" width="8" height="4" fill="#1a1a1a" />
          <rect x="116" y="135" width="8" height="4" fill="#1a1a1a" />
        </g>

        <g stroke="#d97757" strokeWidth="0.7" strokeDasharray="3 2" fill="none" opacity="0.8">
          <path d="M 262,140 Q 230,135 204,138" />
          <path d="M 262,148 Q 230,148 204,150" />
        </g>
        <circle cx="204" cy="138" r="1.6" fill="#d97757" />
        <circle cx="204" cy="150" r="1.6" fill="#d97757" />
        <circle cx="262" cy="140" r="1.6" fill="#d97757" />
        <circle cx="262" cy="148" r="1.6" fill="#d97757" />

        <g transform="translate(264,236)">
          <rect x="0" y="0" width="124" height="14" rx="3" fill="#fff" stroke="#bcae8e" strokeWidth="0.6" />
          <text x="62" y="9" textAnchor="middle" fontSize="6.5" fontWeight="800" fill="#5a4a2c" fontFamily="'JetBrains Mono', monospace" letterSpacing="0.18em">.PDF → .XLSX · .AGS</text>
        </g>
      </svg>

      <span style={{
        position: "absolute", top: 14, left: 16,
        fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5,
        color: "#5a4a2c", letterSpacing: "0.18em", textTransform: "uppercase",
        background: "rgba(255,255,255,0.92)", padding: "4px 8px", borderRadius: 2, fontWeight: 700,
        border: "1px solid rgba(188,174,142,0.5)"
      }}>
        Visual grounding · agentic extraction
      </span>

      <div style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, transparent 60%, rgba(20,20,20,0.10) 100%)", opacity: hover ? 1 : 0, transition: "opacity 200ms", pointerEvents: "none" }} />
    </div>
  );
}

// ── Removed old BoreholeAI preview body — wrapper continues below ──
function _BoreholeAIPreviewLegacy({ hover, h }) {
  return (
    <div style={{ display: "none" }}>
      <svg viewBox="0 0 480 220" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" style={{ position: "absolute", inset: 0 }}>
        <defs>
          <linearGradient id="aiBoxFace" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#3d2e7a" />
            <stop offset="100%" stopColor="#231a4f" />
          </linearGradient>
          <linearGradient id="aiBoxTop" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#5a47a8" />
            <stop offset="100%" stopColor="#3d2e7a" />
          </linearGradient>
          <radialGradient id="aiBeam" cx="0.5" cy="0" r="0.7">
            <stop offset="0%" stopColor="#c4b5fd" stopOpacity="0.95" />
            <stop offset="60%" stopColor="#a78bfa" stopOpacity="0.35" />
            <stop offset="100%" stopColor="#a78bfa" stopOpacity="0" />
          </radialGradient>
          <pattern id="aiDots" width="14" height="14" patternUnits="userSpaceOnUse">
            <circle cx="1.5" cy="1.5" r="1" fill="#7c3aed" fillOpacity="0.10" />
          </pattern>
        </defs>
        <rect width="480" height="220" fill="url(#aiDots)" />

        {/* === Cartoon scanner box (BoreholeAI) — top half === */}
        <g transform="translate(160,18)">
          {/* Soft drop shadow */}
          <ellipse cx="80" cy="92" rx="76" ry="6" fill="#1a1530" fillOpacity="0.18" />
          {/* Box body */}
          <rect x="6" y="6" width="148" height="80" rx="14" fill="url(#aiBoxFace)" stroke="#1a1240" strokeWidth="1.5" />
          {/* Top highlight strip */}
          <rect x="6" y="6" width="148" height="14" rx="14" fill="url(#aiBoxTop)" />
          <rect x="6" y="14" width="148" height="6" fill="url(#aiBoxTop)" />
          {/* Antenna / status pip */}
          <line x1="80" y1="6" x2="80" y2="-4" stroke="#1a1240" strokeWidth="1.5" />
          <circle cx="80" cy="-6" r="3" fill="#34d399">
            <animate attributeName="r" values="2.5;3.6;2.5" dur="1.6s" repeatCount="indefinite" />
          </circle>
          {/* Screen */}
          <rect x="20" y="26" width="120" height="48" rx="6" fill="#0d0a24" stroke="#5a47a8" strokeWidth="1" />
          {/* Eyes / lens — friendly cartoon */}
          <circle cx="50" cy="50" r="9" fill="#f0e9ff" />
          <circle cx="50" cy="50" r="5" fill="#a78bfa" />
          <circle cx="48" cy="48" r="1.6" fill="#fff" />
          <circle cx="110" cy="50" r="9" fill="#f0e9ff" />
          <circle cx="110" cy="50" r="5" fill="#a78bfa" />
          <circle cx="108" cy="48" r="1.6" fill="#fff" />
          {/* Smile bar (subtle scanning indicator) */}
          <rect x="68" y="60" width="24" height="4" rx="2" fill="#a78bfa" opacity="0.75">
            <animate attributeName="width" values="6;24;6" dur="1.4s" repeatCount="indefinite" />
            <animate attributeName="x" values="77;68;77" dur="1.4s" repeatCount="indefinite" />
          </rect>
          {/* Wordmark on box face */}
          <text x="80" y="84" textAnchor="middle" fontSize="8.5" fontWeight="800" fill="#e9deff"
          fontFamily="'JetBrains Mono', monospace" style={{ letterSpacing: "0.18em" }}>BOREHOLE·AI</text>
        </g>

        {/* === Scanning beam — light cone from box down to PDF === */}
        <path d="M 192,118 L 288,118 L 322,210 L 158,210 Z" fill="url(#aiBeam)" opacity="0.85">
          <animate attributeName="opacity" values="0.5;0.95;0.5" dur="2.2s" repeatCount="indefinite" />
        </path>
        {/* Beam edge lines */}
        <line x1="192" y1="118" x2="158" y2="210" stroke="#a78bfa" strokeWidth="1" strokeOpacity="0.55" strokeDasharray="3 4" />
        <line x1="288" y1="118" x2="322" y2="210" stroke="#a78bfa" strokeWidth="1" strokeOpacity="0.55" strokeDasharray="3 4" />

        {/* === Floating data chips emerging from box === */}
        <g>
          {/* Left chip: lithology */}
          <g transform="translate(40,108)">
            <rect x="0" y="0" width="78" height="22" rx="11" fill="#fff" stroke="#a78bfa" strokeWidth="1.2" />
            <circle cx="11" cy="11" r="4" fill="#3aa1ff" />
            <text x="20" y="14" fontSize="8" fill="#3d2e7a" fontFamily="'JetBrains Mono', monospace" fontWeight="700">SILTY CLAY</text>
            <animateTransform attributeName="transform" type="translate" values="40,108; 40,104; 40,108" dur="3s" repeatCount="indefinite" />
          </g>
          {/* Right chip: SPT */}
          <g transform="translate(360,90)">
            <rect x="0" y="0" width="76" height="22" rx="11" fill="#fff" stroke="#a78bfa" strokeWidth="1.2" />
            <text x="8" y="14" fontSize="8" fill="#7c3aed" fontFamily="'JetBrains Mono', monospace">SPT N=</text>
            <text x="42" y="14" fontSize="8" fontWeight="800" fill="#3d2e7a" fontFamily="'JetBrains Mono', monospace">4·6·8</text>
            <animateTransform attributeName="transform" type="translate" values="360,90; 360,86; 360,90" dur="3.2s" repeatCount="indefinite" />
          </g>
          {/* Right chip 2: depth */}
          <g transform="translate(370,128)">
            <rect x="0" y="0" width="64" height="22" rx="11" fill="#fff" stroke="#a78bfa" strokeWidth="1.2" />
            <text x="8" y="14" fontSize="8" fill="#7c3aed" fontFamily="'JetBrains Mono', monospace">3.5 m</text>
            <text x="34" y="14" fontSize="8" fill="#9a85d0" fontFamily="'JetBrains Mono', monospace">→ 9.2</text>
            <animateTransform attributeName="transform" type="translate" values="370,128; 370,124; 370,128" dur="2.6s" repeatCount="indefinite" />
          </g>
          {/* Left chip 2: confidence */}
          <g transform="translate(50,154)">
            <rect x="0" y="0" width="74" height="22" rx="11" fill="#fff" stroke="#a78bfa" strokeWidth="1.2" />
            <circle cx="11" cy="11" r="4" fill="#34d399" />
            <text x="20" y="14" fontSize="8" fill="#3d2e7a" fontFamily="'JetBrains Mono', monospace" fontWeight="700">0.97 conf</text>
            <animateTransform attributeName="transform" type="translate" values="50,154; 50,150; 50,154" dur="2.8s" repeatCount="indefinite" />
          </g>
        </g>

        {/* === PDF document being scanned (bottom) — slightly tilted === */}
        <g transform="translate(186,140) rotate(-3 54 35)">
          {/* Stack effect (back paper) */}
          <rect x="6" y="6" width="108" height="74" rx="3" fill="#e8e3d8" stroke="#b0a690" strokeWidth="0.8" />
          {/* Front PDF */}
          <rect x="0" y="0" width="108" height="74" rx="3" fill="#fdfaf3" stroke="#3d2e7a" strokeWidth="1.2" />
          {/* Header bar */}
          <rect x="0" y="0" width="108" height="11" rx="3" fill="#e6dfd1" />
          <text x="6" y="8" fontSize="5.5" fontWeight="700" fill="#5a4a2c" fontFamily="'JetBrains Mono', monospace">BOREHOLE LOG · BH-LP142</text>
          {/* Mini lithology column */}
          {[["#3aa1ff", 8], ["#1f6fc4", 14], ["#39c46b", 10], ["#d63838", 16]].map(([c, len], i, arr) => {
            const y = 16 + arr.slice(0, i).reduce((a, r) => a + r[1], 0);
            return <rect key={i} x="6" y={y} width="10" height={len} fill={c} stroke="#fff" strokeWidth="0.3" />;
          })}
          {/* Text lines */}
          {[20, 27, 34, 41, 48, 55, 62, 69].map((y, i) =>
          <rect key={i} x="22" y={y} width={70 - i % 3 * 12} height="1.6" fill="#5a5a5a" fillOpacity="0.55" />
          )}
          {/* Highlighted scan line moving on PDF */}
          <rect x="0" y="20" width="108" height="3" fill="#a78bfa" opacity="0.55">
            <animate attributeName="y" values="14;68;14" dur="2.2s" repeatCount="indefinite" />
          </rect>
          {/* Page corner fold */}
          <path d="M 100,0 L 108,0 L 108,8 Z" fill="#e6dfd1" />
        </g>
      </svg>

      {/* Bottom-right tag */}
      <span style={{
        position: "absolute", bottom: 12, right: 14,
        fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
        color: "#3d2e7a", letterSpacing: "0.18em", textTransform: "uppercase",
        background: "rgba(255,255,255,0.85)", padding: "3px 7px", borderRadius: 3,
        border: "1px solid rgba(61,46,122,0.18)"
      }}>
        Scanning · ground profile from PDF
      </span>

      <div style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, transparent 60%, rgba(61,46,122,0.18) 100%)", opacity: hover ? 1 : 0, transition: "opacity 200ms", pointerEvents: "none" }} />
    </div>);

}

function PlaceholderTile() {
  return (
    <div style={{
      display: "flex", flexDirection: "column",
      alignItems: "center", justifyContent: "center",
      background: "rgba(255,255,255,0.4)",
      border: "1.5px dashed rgba(20,20,20,0.16)",
      borderRadius: 16,
      minHeight: 280,
      gap: 10,
      color: "#9a9a9a"
    }}>
      <svg width="34" height="34" viewBox="0 0 34 34" fill="none">
        <circle cx="17" cy="17" r="16" stroke="rgba(20,20,20,0.18)" strokeDasharray="2 4" />
        <path d="M17 11v12M11 17h12" stroke="rgba(20,20,20,0.4)" strokeWidth="1.5" />
      </svg>
      <span style={{ fontSize: 13, fontWeight: 600, color: "#6b6b6b" }}>Next application</span>
      <span style={{ fontSize: 11, color: "#9a9a9a" }}>Pin a new tool here</span>
    </div>);

}

window.DirectionA = DirectionA;
window.AppPreview = AppPreview;
window.GeophysicsPreview = GeophysicsPreview;
window.GeotechVizPreview = GeotechVizPreview;
window.BoreholeAIPreview = BoreholeAIPreview;