// OnCatch illustration library — editorial line-on-paper SVGs.
// Aesthetic: technical-manual / risograph / blueprint. Brand palette only.
// Every illustration is data-driven and earns its place — no decorative slop.

// ============================================================
// 1. NetCatch — the "catching bugs" hero motif
//    A net diagram catching geometric "bugs" (squares, dots).
//    Used in home-page A's hero stage and as a brand glyph.
// ============================================================
const NetCatch = ({ width = 560, height = 360 }) => (
  <svg className="illo" viewBox={`0 0 ${width} ${height}`} width={width} height={height} aria-hidden="true">
    {/* paper grid */}
    <defs>
      <pattern id="dot-grid" width="24" height="24" patternUnits="userSpaceOnUse">
        <circle cx="0.5" cy="0.5" r="0.5" className="fill-rule" />
      </pattern>
    </defs>
    <rect x="0" y="0" width={width} height={height} fill="url(#dot-grid)" opacity="0.5" />

    {/* arc that "rains" submissions toward the net */}
    <g className="stroke stroke-fine stroke-dash stroke-muted">
      <path d={`M 60 30 Q ${width / 2} 80 ${width - 90} ${height / 2 - 20}`} />
      <path d={`M 100 20 Q ${width / 2 - 30} 110 ${width - 140} ${height / 2 - 50}`} />
      <path d={`M 30 70 Q ${width / 2 - 80} 130 ${width - 200} ${height / 2 - 30}`} />
    </g>

    {/* falling 'bugs' — small geometric markers */}
    <g>
      <rect x="80" y="22" width="10" height="10" className="fill-ink" transform="rotate(20 85 27)" />
      <circle cx="180" cy="60" r="5" className="fill-accent" />
      <path d="M 240 30 l 6 -6 l 6 6 l -6 6 z" className="fill-ink" />
      <circle cx="320" cy="42" r="4" className="fill-teal" />
      <rect x="370" y="80" width="8" height="8" className="fill-accent" transform="rotate(40 374 84)" />
    </g>

    {/* the "catch" — a stylised funnel/net made of fine lines */}
    <g transform={`translate(${width / 2 - 40}, ${height / 2 - 40})`}>
      <g className="stroke stroke-fine">
        {/* funnel mouth */}
        <path d="M -120 0 L 120 0" className="stroke" />
        {/* mesh diagonals */}
        {Array.from({ length: 11 }).map((_, i) => {
          const x = -120 + i * 24;
          return <path key={`d1-${i}`} d={`M ${x} 0 L ${x * 0.5} 90`} />;
        })}
        {Array.from({ length: 11 }).map((_, i) => {
          const x = -120 + i * 24;
          return <path key={`d2-${i}`} d={`M ${x} 0 L ${x * 0.7 + 20} 90`} />;
        })}
        {/* horizontal bands */}
        <path d="M -100 30 L 100 30" />
        <path d="M -80 60 L 80 60" />
        <path d="M -55 90 L 55 90" />
      </g>
      {/* pinch knot */}
      <circle cx="0" cy="92" r="6" className="fill-accent" />
      {/* outflow into queue */}
      <path d="M 0 98 L 0 130" className="stroke stroke-accent stroke-2" />
      <path d="M -8 124 L 0 132 L 8 124" className="stroke stroke-accent stroke-2" />

      {/* labelled bracket */}
      <g transform="translate(140, -10)">
        <path d="M 0 10 L 8 10 L 8 80 L 0 80" className="stroke stroke-fine stroke-muted" />
        <text x="14" y="44" className="label-ink">queue</text>
        <text x="14" y="58">on admin page</text>
      </g>
      <g transform="translate(-220, -10)">
        <path d="M 16 10 L 8 10 L 8 60 L 16 60" className="stroke stroke-fine stroke-muted" />
        <text x="-44" y="34" className="label-ink">widget</text>
        <text x="-50" y="48">on your site</text>
      </g>
    </g>
  </svg>
);

// ============================================================
// 2. WidgetAnatomy — exploded view of the embedded widget
//    Shows: shadow-DOM boundary, form fields, attachment slot
// ============================================================
const WidgetAnatomy = ({ width = 560, height = 360 }) => (
  <svg className="illo" viewBox={`0 0 ${width} ${height}`} width={width} height={height} aria-hidden="true">
    {/* host page background */}
    <rect x="20" y="20" width={width - 40} height={height - 40} className="fill-paper-2" rx="6" />
    <text x="32" y="42">host page · your dom</text>

    {/* dotted shadow-DOM boundary */}
    <g transform="translate(180, 70)">
      <rect x="0" y="0" width="220" height="240" rx="10"
        className="stroke stroke-dash stroke-accent" fill="rgba(255,255,255,0.6)" />
      <text x="6" y="-6" className="label-accent">shadow-dom · isolated</text>

      {/* widget header */}
      <rect x="12" y="16" width="196" height="28" rx="5" className="fill-paper" />
      <text x="22" y="34" className="label-ink">Report a problem</text>
      <text x="186" y="34" className="label-ink" style={{ fontSize: 14 }}>×</text>

      {/* fields */}
      <g transform="translate(12, 56)">
        {[
          { y: 0, label: "Title" },
          { y: 36, label: "Description" },
          { y: 90, label: "Name (optional)" },
          { y: 126, label: "Email (optional)" },
        ].map((f) => (
          <g key={f.label} transform={`translate(0, ${f.y})`}>
            <rect width="196" height="24" rx="4" className="fill-paper" />
            <rect width="196" height="24" rx="4" className="stroke stroke-fine" />
            <text x="6" y="9" className="label-accent" style={{ fontSize: 7 }}>{f.label.toUpperCase()}</text>
          </g>
        ))}
      </g>

      {/* attach slot */}
      <g transform="translate(12, 198)">
        <rect width="196" height="24" rx="4" className="stroke stroke-fine stroke-dash" />
        <text x="98" y="15" textAnchor="middle" className="label-ink" style={{ fontSize: 9 }}>+ attach screenshot or video</text>
      </g>
    </g>

    {/* Annotation arrows pointing in */}
    <g className="stroke stroke-fine stroke-muted">
      <path d="M 80 100 L 175 100" />
      <path d="M 168 96 L 178 100 L 168 104" />
    </g>
    <text x="32" y="92" className="label-ink">closed</text>
    <text x="32" y="106">scope</text>

    <g className="stroke stroke-fine stroke-muted">
      <path d="M 80 200 L 175 200" />
      <path d="M 168 196 L 178 200 L 168 204" />
    </g>
    <text x="32" y="192" className="label-ink">no css</text>
    <text x="32" y="206">leakage</text>

    <g className="stroke stroke-fine stroke-muted">
      <path d="M 480 140 L 405 140" />
      <path d="M 412 136 L 402 140 L 412 144" />
    </g>
    <text x="468" y="132" className="label-ink">75 KB</text>
    <text x="468" y="146">gzipped</text>

    <g className="stroke stroke-fine stroke-muted">
      <path d="M 480 240 L 405 240" />
      <path d="M 412 236 L 402 240 L 412 244" />
    </g>
    <text x="468" y="232" className="label-ink">redacts</text>
    <text x="468" y="246">pii client-side</text>
  </svg>
);

// ============================================================
// 3. TriageFlow — the report's journey, as an annotated timeline
// ============================================================
const TriageFlow = ({ width = 920, height = 220 }) => {
  // Sub-labels kept short so each fits in the ~170px space between dots
  // (longer phrasing was overlapping into the next stop).
  const stops = [
    { x: 80, label: "Reporter\nclicks icon",  sub: "0s" },
    { x: 250, label: "Submit form",           sub: "+30s · redacted" },
    { x: 420, label: "Token-gated\ningestion",sub: "+50ms · origin OK" },
    { x: 590, label: "Admin queue",           sub: "real-time" },
    { x: 760, label: "Pushed to\ntracker",    sub: "→ jira / linear / etc." },
  ];
  return (
    <svg className="illo" viewBox={`0 0 ${width} ${height}`} width={width} height={height} aria-hidden="true">
      {/* baseline */}
      <line x1="40" y1="120" x2={width - 40} y2="120" className="stroke stroke-fine" />
      {/* origin tick + end arrow */}
      <line x1="40" y1="110" x2="40" y2="130" className="stroke stroke-fine" />
      <path d={`M ${width - 50} 114 L ${width - 40} 120 L ${width - 50} 126`} className="stroke stroke-accent stroke-2" />

      {stops.map((s, i) => (
        <g key={s.label} transform={`translate(${s.x}, 120)`}>
          {/* tick */}
          <line x1="0" y1="-6" x2="0" y2="6" className="stroke" />
          {/* dot */}
          <circle cx="0" cy="0" r="6" className={i === stops.length - 1 ? "fill-accent" : "fill-paper"} />
          <circle cx="0" cy="0" r="6" className="stroke" />

          {/* label above */}
          {s.label.split("\n").map((line, j) => (
            <text key={j} x="0" y={-30 + j * 12} textAnchor="middle" className="label-ink">{line}</text>
          ))}
          {/* sub below */}
          <text x="0" y="28" textAnchor="middle">{s.sub}</text>

          {/* index */}
          <text x="0" y="-58" textAnchor="middle" className="label-accent">{`0${i + 1}`}</text>
        </g>
      ))}

      {/* (margin annotations removed — they duplicated the
          infographic-foot "Reporter side / Server side / Your side" labels
          and visually hung under stops 01 / 05 specifically) */}
    </svg>
  );
};

// ============================================================
// 4. DataMap — what flows where (trust diagram)
// ============================================================
const DataMap = ({ width = 920, height = 360 }) => (
  <svg className="illo" viewBox={`0 0 ${width} ${height}`} width={width} height={height} aria-hidden="true">
    {/* three regions */}
    <g>
      <rect x="40" y="40" width="240" height="280" rx="10" className="stroke stroke-fine fill-paper-2" />
      <text x="56" y="64" className="label-ink">REPORTER'S BROWSER</text>
      <text x="56" y="80">on your site</text>
    </g>
    <g>
      <rect x="340" y="40" width="240" height="280" rx="10" className="stroke stroke-fine fill-paper" />
      <text x="356" y="64" className="label-ink">ONCATCH SERVICE</text>
      <text x="356" y="80">supabase + vercel</text>
    </g>
    <g>
      <rect x="640" y="40" width="240" height="280" rx="10" className="stroke stroke-fine fill-paper-2" />
      <text x="656" y="64" className="label-ink">YOUR SYSTEMS</text>
      <text x="656" y="80">tracker · slack · webhook</text>
    </g>

    {/* widget icon */}
    <g transform="translate(140, 130)">
      <rect x="-44" y="-30" width="88" height="60" rx="8" className="stroke fill-paper" />
      <line x1="-30" y1="-12" x2="30" y2="-12" className="stroke stroke-fine" />
      <line x1="-30" y1="0" x2="30" y2="0" className="stroke stroke-fine" />
      <line x1="-30" y1="12" x2="14" y2="12" className="stroke stroke-fine" />
      <text x="0" y="48" textAnchor="middle">widget</text>
    </g>

    {/* PII redaction filter */}
    <g transform="translate(280, 160)">
      <circle cx="0" cy="0" r="22" className="stroke stroke-accent fill-accent-soft" />
      <text x="0" y="3" textAnchor="middle" className="label-accent">redact</text>
      <text x="0" y="42" textAnchor="middle">client-side</text>
    </g>

    {/* server box */}
    <g transform="translate(460, 140)">
      <rect x="-50" y="-40" width="100" height="80" rx="6" className="stroke fill-paper-2" />
      <line x1="-40" y1="-20" x2="40" y2="-20" className="stroke stroke-fine" />
      <line x1="-40" y1="0" x2="40" y2="0" className="stroke stroke-fine" />
      <line x1="-40" y1="20" x2="20" y2="20" className="stroke stroke-fine" />
      <text x="0" y="58" textAnchor="middle">workspace-isolated</text>
      <text x="0" y="72" textAnchor="middle">row-level security</text>
    </g>

    {/* HMAC signing */}
    <g transform="translate(610, 160)">
      <circle cx="0" cy="0" r="22" className="stroke stroke-teal fill-teal-soft" />
      <text x="0" y="3" textAnchor="middle" style={{ fill: "var(--teal)" }}>hmac</text>
      <text x="0" y="42" textAnchor="middle">signed webhook</text>
    </g>

    {/* tracker */}
    <g transform="translate(760, 130)">
      <rect x="-44" y="-30" width="88" height="60" rx="8" className="stroke fill-paper" />
      <text x="0" y="-8" textAnchor="middle" className="label-ink">JIRA</text>
      <text x="0" y="6" textAnchor="middle" className="label-ink">LINEAR</text>
      <text x="0" y="20" textAnchor="middle" className="label-ink">GITHUB</text>
      <text x="0" y="48" textAnchor="middle">your tracker</text>
    </g>

    {/* arrows */}
    <g className="stroke stroke-2 stroke-accent">
      <path d="M 188 160 L 256 160" />
      <path d="M 248 156 L 258 160 L 248 164" fill="none" />
    </g>
    <g className="stroke stroke-2 stroke-accent">
      <path d="M 304 160 L 408 160" />
      <path d="M 400 156 L 410 160 L 400 164" fill="none" />
    </g>
    <g className="stroke stroke-2 stroke-teal">
      <path d="M 514 160 L 586 160" />
      <path d="M 578 156 L 588 160 L 578 164" fill="none" />
    </g>
    <g className="stroke stroke-2 stroke-teal">
      <path d="M 634 160 L 712 160" />
      <path d="M 704 156 L 714 160 L 704 164" fill="none" />
    </g>

    {/* footer captions */}
    <text x="160" y="280" textAnchor="middle">tls 1.2+</text>
    <text x="460" y="280" textAnchor="middle">encrypted at rest</text>
    <text x="760" y="280" textAnchor="middle">webhook v1 · stable</text>
  </svg>
);

// ============================================================
// 5. BundleAnatomy — pie/bar showing 75 KB breakdown
// ============================================================
const BundleAnatomy = ({ width = 360, height = 240 }) => {
  // bytes (illustrative): ui 32k, capture 18k, network 12k, redaction 8k, init 5k
  const segments = [
    { label: "UI / shadow DOM", kb: 32, cls: "fill-accent" },
    { label: "Screenshot capture", kb: 18, cls: "fill-ink" },
    { label: "Network / submit", kb: 12, cls: "fill-teal" },
    { label: "Redaction", kb: 8, cls: "fill-paper-2" },
    { label: "Init + boot", kb: 5, cls: "fill-rule" },
  ];
  const total = segments.reduce((a, b) => a + b.kb, 0); // 75
  let acc = 0;
  return (
    <svg className="illo" viewBox={`0 0 ${width} ${height}`} width={width} height={height} aria-hidden="true">
      {/* horizontal stacked bar */}
      <g transform="translate(20, 40)">
        <rect x="0" y="0" width={width - 40} height="36" className="fill-paper-2 stroke stroke-fine" />
        {segments.map((s) => {
          const w = (s.kb / total) * (width - 40);
          const x = (acc / total) * (width - 40);
          acc += s.kb;
          return <rect key={s.label} x={x} y="0" width={w} height="36" className={s.cls} />;
        })}
        {/* end-cap */}
        <rect x="0" y="0" width={width - 40} height="36" className="stroke" fill="none" />
        {/* axis ticks */}
        {[0, 25, 50, 75].map((t) => (
          <g key={t} transform={`translate(${(t / total) * (width - 40)}, 36)`}>
            <line x1="0" y1="0" x2="0" y2="6" className="stroke stroke-fine" />
            <text x="0" y="20" textAnchor="middle">{t} KB</text>
          </g>
        ))}
      </g>
      {/* legend */}
      <g transform="translate(20, 120)">
        {segments.map((s, i) => (
          <g key={s.label} transform={`translate(${(i % 3) * 110}, ${Math.floor(i / 3) * 22})`}>
            <rect x="0" y="0" width="10" height="10" className={s.cls + " stroke stroke-fine"} />
            <text x="16" y="9" className="label-ink" style={{ fontSize: 9 }}>{s.label}</text>
          </g>
        ))}
      </g>
      <text x={width - 20} y="22" textAnchor="end" className="label-accent" style={{ fontSize: 12 }}>75 KB · gzipped</text>
      <text x="20" y="22" className="label-ink" style={{ fontSize: 11 }}>Bundle breakdown</text>
    </svg>
  );
};

// ============================================================
// 6. RetentionTimeline — horizontal calendar of retention periods
// ============================================================
const RetentionTimeline = ({ width = 760, height = 280 }) => {
  const rows = [
    { label: "Submissions (text)", days: 730, cls: "fill-ink" },
    { label: "Screenshots", days: 365, cls: "fill-accent" },
    { label: "Screen recordings", days: 90, cls: "fill-teal" },
    { label: "Audit logs", days: 365, cls: "fill-muted" },
    { label: "Auth logs", days: 365, cls: "fill-muted" },
    { label: "Webhook DLQ", days: 30, cls: "fill-rule" },
  ];
  const max = 730;
  const labelCol = 180;
  const trackW = width - labelCol - 80;
  return (
    <svg className="illo" viewBox={`0 0 ${width} ${height}`} width={width} height={height} aria-hidden="true">
      {/* axis */}
      <g transform={`translate(${labelCol}, ${height - 40})`}>
        <line x1="0" y1="0" x2={trackW} y2="0" className="stroke stroke-fine" />
        {[0, 30, 90, 365, 730].map((t) => (
          <g key={t} transform={`translate(${(t / max) * trackW}, 0)`}>
            <line x1="0" y1="-4" x2="0" y2="4" className="stroke stroke-fine" />
            <text x="0" y="20" textAnchor="middle">{t}d</text>
          </g>
        ))}
      </g>
      {rows.map((r, i) => (
        <g key={r.label} transform={`translate(0, ${28 + i * 32})`}>
          <text x={labelCol - 12} y="14" textAnchor="end" className="label-ink" style={{ fontSize: 11 }}>{r.label}</text>
          <line x1={labelCol} y1="10" x2={labelCol + trackW} y2="10" className="stroke stroke-fine stroke-dash" />
          <rect x={labelCol} y="4" width={(r.days / max) * trackW} height="14" className={r.cls} />
          <text x={labelCol + (r.days / max) * trackW + 8} y="14" className="label-ink" style={{ fontSize: 10 }}>{r.days}d</text>
        </g>
      ))}
      <text x="20" y="22" className="label-ink" style={{ fontSize: 11 }}>Default retention</text>
      <text x={width - 20} y="22" textAnchor="end" className="label-accent" style={{ fontSize: 11 }}>customer-configurable</text>
    </svg>
  );
};

// ============================================================
// 7. SubprocessorMap — concentric circles, OnPath at center
// ============================================================
const SubprocessorMap = ({ width = 560, height = 420 }) => {
  const subs = [
    { name: "Supabase", role: "DB · storage · auth", angle: 0 },
    { name: "Vercel", role: "Hosting · CDN", angle: 60 },
    { name: "Resend", role: "Transactional email", angle: 120 },
    { name: "Cloudflare", role: "Turnstile · DNS", angle: 180 },
    { name: "GitHub", role: "Source · CI", angle: 240 },
    { name: "Sentry*", role: "Errors · planned", angle: 300, planned: true },
  ];
  const cx = width / 2, cy = height / 2 + 10, r = 150;
  return (
    <svg className="illo" viewBox={`0 0 ${width} ${height}`} width={width} height={height} aria-hidden="true">
      <text x="20" y="22" className="label-ink" style={{ fontSize: 11 }}>Subprocessors</text>
      <text x={width - 20} y="22" textAnchor="end">Updated 2026-04-19</text>

      {/* outer ring */}
      <circle cx={cx} cy={cy} r={r} className="stroke stroke-fine stroke-dash" />
      <circle cx={cx} cy={cy} r={r - 60} className="stroke stroke-fine stroke-dash" />

      {/* center */}
      <g transform={`translate(${cx}, ${cy})`}>
        <circle cx="0" cy="0" r="42" className="fill-ink" />
        <text x="0" y="-2" textAnchor="middle" style={{ fill: "var(--paper)" }} className="label-ink">OnPath</text>
        <text x="0" y="14" textAnchor="middle" style={{ fill: "var(--paper-3)" }}>processor</text>
      </g>

      {subs.map((s) => {
        const rad = (s.angle * Math.PI) / 180;
        const x = cx + r * Math.cos(rad);
        const y = cy + r * Math.sin(rad);
        return (
          <g key={s.name}>
            <line x1={cx + 42 * Math.cos(rad)} y1={cy + 42 * Math.sin(rad)} x2={x} y2={y}
              className={`stroke stroke-fine ${s.planned ? "stroke-dash stroke-muted" : ""}`} />
            <circle cx={x} cy={y} r="34" className={s.planned ? "fill-paper-2" : "fill-paper"} />
            <circle cx={x} cy={y} r="34" className={`stroke stroke-fine ${s.planned ? "stroke-dash" : ""}`} />
            <text x={x} y={y - 2} textAnchor="middle" className="label-ink" style={{ fontSize: 11 }}>{s.name}</text>
            <text x={x} y={y + 12} textAnchor="middle" style={{ fontSize: 8 }}>{s.role}</text>
          </g>
        );
      })}
    </svg>
  );
};

// ============================================================
// 8. PrivacyControls — checklist diagram of what stays in-browser vs server
// ============================================================
const StorageMap = ({ width = 560, height = 320 }) => {
  const browser = [
    "icon position",
    "form draft",
    "name / email prefill",
    "devtools warning ack",
  ];
  const server = [
    "submitted reports (text)",
    "screenshots / recordings",
    "redacted console logs",
    "browser metadata",
  ];
  const cell = (items, title, x, fillCls) => (
    <g transform={`translate(${x}, 60)`}>
      <rect x="0" y="0" width="240" height="220" rx="10" className={`stroke stroke-fine ${fillCls}`} />
      <text x="16" y="22" className="label-ink" style={{ fontSize: 11 }}>{title}</text>
      {items.map((it, i) => (
        <g key={it} transform={`translate(16, ${52 + i * 32})`}>
          <rect x="0" y="0" width="208" height="22" rx="4" className="fill-paper" />
          <rect x="0" y="0" width="208" height="22" rx="4" className="stroke stroke-fine" />
          <circle cx="14" cy="11" r="3" className="fill-accent" />
          <text x="26" y="14" className="label-ink" style={{ fontSize: 10 }}>{it}</text>
        </g>
      ))}
    </g>
  );
  return (
    <svg className="illo" viewBox={`0 0 ${width} ${height}`} width={width} height={height} aria-hidden="true">
      <text x="20" y="32" className="label-ink" style={{ fontSize: 11 }}>Where data lives</text>
      <text x={width - 20} y="32" textAnchor="end">never crosses without submit</text>
      {cell(browser, "BROWSER · localStorage", 20, "fill-paper-2")}
      {cell(server, "SERVER · oncatch", 300, "fill-paper-2")}
      {/* divider */}
      <line x1={width / 2} y1="80" x2={width / 2} y2={height - 20} className="stroke stroke-fine stroke-dash stroke-accent" />
      <text x={width / 2} y={height - 6} textAnchor="middle" className="label-accent">submit boundary</text>
    </svg>
  );
};

// ============================================================
// 9. SmallSpotIcons — line glyphs used in trust grid, integrations, etc.
// ============================================================
const Glyph = ({ kind, size = 56 }) => {
  const s = size;
  switch (kind) {
    case "shield":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <path d="M 28 6 L 48 14 V 30 C 48 42 38 50 28 52 C 18 50 8 42 8 30 V 14 Z" className="stroke stroke-2" />
          <path d="M 18 28 L 26 36 L 40 22" className="stroke stroke-2 stroke-accent" />
        </svg>
      );
    case "lock":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <rect x="14" y="24" width="28" height="22" rx="3" className="stroke stroke-2" />
          <path d="M 20 24 V 18 a 8 8 0 0 1 16 0 V 24" className="stroke stroke-2" />
          <circle cx="28" cy="34" r="3" className="fill-accent" />
        </svg>
      );
    case "redact":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <line x1="8" y1="18" x2="48" y2="18" className="stroke stroke-2" />
          <rect x="8" y="26" width="22" height="6" className="fill-ink" />
          <rect x="8" y="36" width="34" height="6" className="fill-accent" />
        </svg>
      );
    case "isolate":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <rect x="6" y="6" width="44" height="44" rx="4" className="stroke stroke-fine stroke-dash" />
          <rect x="18" y="18" width="20" height="20" rx="3" className="stroke stroke-2 stroke-accent" />
        </svg>
      );
    case "rate":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <circle cx="28" cy="28" r="20" className="stroke stroke-2" />
          <path d="M 28 28 L 28 14" className="stroke stroke-2 stroke-accent" />
          <path d="M 28 28 L 38 32" className="stroke stroke-2" />
          <circle cx="28" cy="28" r="3" className="fill-ink" />
        </svg>
      );
    case "webhook":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <circle cx="18" cy="22" r="8" className="stroke stroke-2" />
          <circle cx="40" cy="36" r="8" className="stroke stroke-2 stroke-accent" />
          <circle cx="18" cy="44" r="6" className="stroke stroke-2" />
          <line x1="22" y1="28" x2="36" y2="36" className="stroke stroke-2" />
          <line x1="22" y1="44" x2="34" y2="38" className="stroke stroke-2" />
        </svg>
      );
    case "queue":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <rect x="8" y="12" width="40" height="8" rx="2" className="fill-accent-soft stroke stroke-fine" />
          <rect x="8" y="24" width="40" height="8" rx="2" className="fill-paper-2 stroke stroke-fine" />
          <rect x="8" y="36" width="40" height="8" rx="2" className="fill-paper-2 stroke stroke-fine" />
          <circle cx="14" cy="16" r="1.5" className="fill-accent" />
        </svg>
      );
    case "doc":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <path d="M 14 6 H 36 L 44 14 V 50 H 14 Z" className="stroke stroke-2 fill-paper" />
          <path d="M 36 6 V 14 H 44" className="stroke stroke-2" />
          <line x1="20" y1="24" x2="38" y2="24" className="stroke stroke-fine" />
          <line x1="20" y1="32" x2="38" y2="32" className="stroke stroke-fine" />
          <line x1="20" y1="40" x2="32" y2="40" className="stroke stroke-fine" />
        </svg>
      );
    case "globe":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <circle cx="28" cy="28" r="20" className="stroke stroke-2" />
          <ellipse cx="28" cy="28" rx="8" ry="20" className="stroke stroke-fine" />
          <line x1="8" y1="28" x2="48" y2="28" className="stroke stroke-fine" />
          <path d="M 14 16 Q 28 22 42 16" className="stroke stroke-fine" />
          <path d="M 14 40 Q 28 34 42 40" className="stroke stroke-fine" />
        </svg>
      );
    case "eye":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <path d="M 4 28 Q 28 8 52 28 Q 28 48 4 28 Z" className="stroke stroke-2" />
          <circle cx="28" cy="28" r="6" className="stroke stroke-2 stroke-accent" />
          <circle cx="28" cy="28" r="2" className="fill-ink" />
        </svg>
      );
    case "clock":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <circle cx="28" cy="28" r="20" className="stroke stroke-2" />
          <path d="M 28 14 V 28 L 38 34" className="stroke stroke-2 stroke-accent" />
        </svg>
      );
    case "stamp":
      return (
        <svg className="illo" viewBox="0 0 56 56" width={s} height={s}>
          <rect x="6" y="14" width="44" height="28" rx="2" className="stroke stroke-2 stroke-dash" />
          <text x="28" y="32" textAnchor="middle" className="label-accent" style={{ fontSize: 11, letterSpacing: ".15em" }}>DRAFT</text>
        </svg>
      );
    default:
      return null;
  }
};

// ============================================================
// 10. Banner — wide editorial illustration combining several motifs
// Width tightened to 880 (was 1200) so the queue sits closer to the sites,
// reducing the dead whitespace between them. The viewBox scales to fit the
// container CSS, so visual size at the page is unchanged — only proportions
// inside tighten.
// ============================================================
const HomeBanner = ({ width = 880, height = 280 }) => {
  // Queue panel position — pulled left so it sits visibly close to the sites
  const queueX = width - 360;        // 520 with width=880
  const arcEndX = queueX + 14;       // 534 — lands just inside the queue's left edge
  return (
    <svg className="illo" viewBox={`0 0 ${width} ${height}`} width={width} height={height} aria-hidden="true">
      <rect x="0" y="0" width={width} height={height} className="fill-paper-2" />
      {/* horizon */}
      <line x1="0" y1={height - 60} x2={width} y2={height - 60} className="stroke stroke-fine" />
      <line x1="0" y1={height - 60} x2={width} y2={height - 60} className="stroke stroke-fine stroke-dash" transform="translate(0, 4)" />

      {/* far site silhouettes */}
      <g transform={`translate(60, ${height - 60})`}>
        <rect x="0" y="-90" width="80" height="90" className="fill-paper" />
        <rect x="0" y="-90" width="80" height="90" className="stroke stroke-fine" />
        <rect x="100" y="-130" width="60" height="130" className="fill-paper" />
        <rect x="100" y="-130" width="60" height="130" className="stroke stroke-fine" />
        <rect x="180" y="-110" width="100" height="110" className="fill-paper" />
        <rect x="180" y="-110" width="100" height="110" className="stroke stroke-fine" />
        <text x="40" y="-100" textAnchor="middle" className="label-ink" style={{ fontSize: 9 }}>your site A</text>
        <text x="130" y="-138" textAnchor="middle" className="label-ink" style={{ fontSize: 9 }}>your site B</text>
        <text x="230" y="-118" textAnchor="middle" className="label-ink" style={{ fontSize: 9 }}>your site C</text>
      </g>

      {/* widget icons — one per site, positioned ABOVE each building's label.
          Building label centers (after parent translate +60): A=100, B=190, C=290.
          Building B is tallest so its label sits highest (y=82); all !s placed
          above that line at y≤60 to clear all three labels. */}
      {[
        { cx: 100, cy: 58 }, // bug from site A
        { cx: 190, cy: 46 }, // bug from site B (slightly higher to acknowledge the tallest building)
        { cx: 290, cy: 54 }, // bug from site C
      ].map(({ cx, cy }) => (
        <g key={cx} transform={`translate(${cx}, ${cy})`}>
          <circle cx="0" cy="0" r="14" className="fill-accent" />
          <text x="0" y="3" textAnchor="middle" style={{ fill: "var(--paper)", fontSize: 10 }}>!</text>
        </g>
      ))}

      {/* arc from each ! into a DIFFERENT row of the (now taller, 4-pill) queue.
          Staggered endpoints prevent crossing. Lifecycle metaphor:
            C (newest) → row 0 NEW       (y=111)
            B          → row 1 TRIAGED   (y=141)
            A (oldest) → row 2 FORWARDED (y=171)
          The 4th queue row (CLOSED at y=201) is shown as historical context
          but has no arc — it represents an earlier bug from before this scene. */}
      <g fill="none" stroke="var(--accent)" strokeWidth="1.5" strokeDasharray="6 4" strokeLinecap="round" opacity="0.85">
        {/* C → row 0 NEW. Short arc, moderate peak. */}
        <path d={`M 304 54 Q ${(304 + arcEndX) / 2} 25 ${arcEndX} 111`} />
        {/* B → row 1 TRIAGED. Medium arc, higher peak so it passes above C's arc. */}
        <path d={`M 204 46 Q ${(204 + arcEndX) / 2} 8 ${arcEndX} 141`} />
        {/* A → row 2 FORWARDED. Long arc, highest peak — crowns over both. */}
        <path d={`M 114 58 Q ${(114 + arcEndX) / 2} -8 ${arcEndX} 171`} />
      </g>

      {/* central "queue" — list rows with all 4 status pills as they appear in
          the real admin (NEW, TRIAGED, FORWARDED, CLOSED). Newest at top. */}
      <g transform={`translate(${queueX}, 60)`}>
        <rect x="0" y="0" width="280" height="180" rx="10" className="fill-paper stroke" />
        <text x="14" y="22" className="label-ink" style={{ fontSize: 10 }}>OnCatch · admin</text>
        {[
          { code: "OC-7F2K-94", status: "NEW",       pillFill: "var(--accent-soft)", pillStroke: "var(--accent)",  pillText: "var(--accent-deep)", rowFill: "var(--accent-soft)", dotFill: "var(--accent)" },
          { code: "OC-7F2K-93", status: "TRIAGED",   pillFill: "var(--teal-soft)",   pillStroke: "var(--teal)",    pillText: "var(--teal)",        rowFill: "var(--paper-2)",     dotFill: "var(--rule)" },
          { code: "OC-7F2K-92", status: "FORWARDED", pillFill: "#D6EFD8",            pillStroke: "var(--ok)",      pillText: "var(--ok)",          rowFill: "var(--paper-2)",     dotFill: "var(--rule)" },
          { code: "OC-7F2K-91", status: "CLOSED",    pillFill: "var(--paper-3)",     pillStroke: "var(--muted-2)", pillText: "var(--muted)",       rowFill: "var(--paper-2)",     dotFill: "var(--rule)" },
        ].map((row, i) => (
          <g key={i} transform={`translate(14, ${40 + i * 30})`}>
            {/* row background */}
            <rect x="0" y="0" width="252" height="22" rx="4" fill={row.rowFill} />
            {/* status dot */}
            <circle cx="12" cy="11" r="3" fill={row.dotFill} />
            {/* reference code */}
            <text x="24" y="14" style={{ fontSize: 9, fill: "var(--ink)" }}>{row.code}</text>
            {/* status pill — wider for "FORWARDED" (longest label) */}
            <rect
              x={row.status === "FORWARDED" ? 178 : (row.status === "TRIAGED" ? 192 : 200)}
              y="4"
              width={row.status === "FORWARDED" ? 70 : (row.status === "TRIAGED" ? 56 : 48)}
              height="14"
              rx="3"
              fill={row.pillFill}
              stroke={row.pillStroke}
              strokeWidth="0.5"
            />
            <text
              x={row.status === "FORWARDED" ? 213 : (row.status === "TRIAGED" ? 220 : 224)}
              y="13"
              textAnchor="middle"
              style={{ fontSize: 7, fontWeight: 600, letterSpacing: "0.05em", fill: row.pillText }}
            >{row.status}</text>
          </g>
        ))}
      </g>
    </svg>
  );
};

Object.assign(window, {
  NetCatch, WidgetAnatomy, TriageFlow, DataMap, BundleAnatomy,
  RetentionTimeline, SubprocessorMap, StorageMap, Glyph, HomeBanner,
});
