diff --git a/strix/viewer_src/src/components/ProCta.tsx b/strix/viewer_src/src/components/ProCta.tsx
new file mode 100644
index 00000000..bd205ee5
--- /dev/null
+++ b/strix/viewer_src/src/components/ProCta.tsx
@@ -0,0 +1,148 @@
+import React, { useState } from "react";
+import { ArrowUpRight } from "lucide-react";
+import { SIGNUP_URL, trackCta } from "@/lib/cta";
+
+/**
+ * Shared Pro CTA primitives. Every Pro item is a direct link-out to the cloud
+ * sign-up in a new tab with a hover tooltip one-liner (no modal, no lock icon).
+ * Built once here and reused by the sidebar Platform section, the top upsell
+ * row, and the inline CTAs in the tabs.
+ */
+
+/** Small "Pro" pill. Deliberately not a padlock. */
+export function ProTag({ className = "" }: { className?: string }) {
+ return (
+
+ Pro
+
+ );
+}
+
+/**
+ * Lightweight hover tooltip. Wraps a trigger and reveals `text` above it on
+ * hover/focus. Plain Tailwind + local state (no radix vendored).
+ */
+export function Tooltip({
+ text,
+ children,
+ className = "",
+}: {
+ text: string;
+ children: React.ReactNode;
+ className?: string;
+}) {
+ const [open, setOpen] = useState(false);
+ return (
+ setOpen(true)}
+ onMouseLeave={() => setOpen(false)}
+ onFocus={() => setOpen(true)}
+ onBlur={() => setOpen(false)}
+ >
+ {children}
+ {open && (
+
+ {text}
+
+ )}
+
+ );
+}
+
+export interface ProItem {
+ title: string;
+ desc: string;
+ slug: string;
+ icon: React.ElementType;
+}
+
+/**
+ * Card-style Pro feature tile: icon + name + one-liner + Pro tag + arrow.
+ * Used in the top upsell row and inline CTA grids.
+ */
+export function ProTile({ item }: { item: ProItem }) {
+ const Icon = item.icon;
+ return (
+ trackCta(item.slug)}
+ title={item.desc}
+ className="group block cursor-pointer rounded-xl border border-[#222] bg-[rgba(255,255,255,0.02)] p-4 text-left transition-colors hover:border-[#444]"
+ >
+
+ {item.title}
+ {item.desc}
+
+ );
+}
+
+/**
+ * Sidebar-row Pro item: icon + label + Pro tag, with the one-liner as a hover
+ * tooltip. Link-out to sign-up in a new tab.
+ */
+export function ProNavItem({ item }: { item: ProItem }) {
+ const Icon = item.icon;
+ return (
+
+ trackCta(item.slug)}
+ className="group flex w-full items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm text-[#888] transition-colors hover:bg-[rgba(255,255,255,0.06)] hover:text-white"
+ >
+
+ {item.title}
+
+
+
+ );
+}
+
+/**
+ * Inline Pro CTA button (compact). Used in the finding detail and per-surface
+ * rows where a full card is too heavy.
+ */
+export function ProInlineCta({
+ label,
+ desc,
+ slug,
+ icon: Icon,
+}: {
+ label: string;
+ desc: string;
+ slug: string;
+ icon: React.ElementType;
+}) {
+ return (
+
+ trackCta(slug)}
+ className="group inline-flex items-center gap-2 rounded-lg border border-[#222] bg-[rgba(255,255,255,0.02)] px-3 py-2 text-sm text-[#aaa] transition-colors hover:border-[#444] hover:text-white"
+ >
+
+ {label}
+
+
+
+ );
+}
diff --git a/strix/viewer_src/src/components/Sidebar.tsx b/strix/viewer_src/src/components/Sidebar.tsx
new file mode 100644
index 00000000..b27d1d32
--- /dev/null
+++ b/strix/viewer_src/src/components/Sidebar.tsx
@@ -0,0 +1,218 @@
+import React from "react";
+import {
+ FileText,
+ Bug,
+ Waypoints,
+ History,
+ Mail,
+ GitPullRequest,
+ Search,
+ Layers,
+ Globe,
+ Puzzle,
+ Users,
+ LayoutDashboard,
+ AlertTriangle,
+ MessageSquare,
+ Network,
+ Database,
+ ArrowUpRight,
+ LogOut,
+ ShieldCheck,
+} from "lucide-react";
+import { SIGNUP_URL, trackCta } from "@/lib/cta";
+import { ProNavItem, type ProItem } from "@/components/ProCta";
+import type { View } from "@/App";
+
+/**
+ * Persistent left rail. Mirrors the cloud app's nav: real in-page content
+ * ("This run"), local email-gated features ("Your runs"), and org-oriented
+ * Pro link-outs ("Platform"). Matches App.tsx's dark palette.
+ */
+
+// Org-signal items lead the list (PR Reviews, Repositories, Domains,
+// Integrations, Members), then the rest of the platform surface.
+const PLATFORM_ITEMS: ProItem[] = [
+ { title: "PR Reviews", desc: "Pentest every pull request your team opens.", slug: "pr_reviews", icon: GitPullRequest },
+ { title: "Repositories", desc: "Connect your org's repositories and domains.", slug: "repositories", icon: Layers },
+ { title: "Domains", desc: "Connect your org's repositories and domains.", slug: "domains", icon: Globe },
+ { title: "Integrations", desc: "Two-way sync findings to Jira, Linear, and Slack.", slug: "integrations", icon: Puzzle },
+ { title: "Members", desc: "Invite your team and set roles.", slug: "members", icon: Users },
+ { title: "Pentests", desc: "Launch deeper pentests on managed infra.", slug: "pentests", icon: Search },
+ { title: "Dashboard", desc: "See every project and finding in one place.", slug: "dashboard", icon: LayoutDashboard },
+ { title: "Issues", desc: "Track and triage findings across your org.", slug: "platform_issues", icon: AlertTriangle },
+ { title: "Chat", desc: "Ask the agents about any finding.", slug: "chat", icon: MessageSquare },
+ { title: "Networks", desc: "Map and monitor your network exposure.", slug: "networks", icon: Network },
+ { title: "Knowledge", desc: "Give agents context about your systems.", slug: "knowledge", icon: Database },
+];
+
+interface SidebarProps {
+ view: View;
+ onSelectView: (view: View) => void;
+ issuesCount: number;
+ agentCount: number;
+ runCount: number;
+ verified: boolean;
+ email: string | null;
+ onOpenEmail: () => void;
+ onOpenHistory: () => void;
+ onForget: () => void;
+}
+
+export default function Sidebar({
+ view,
+ onSelectView,
+ issuesCount,
+ agentCount,
+ runCount,
+ verified,
+ email,
+ onOpenEmail,
+ onOpenHistory,
+ onForget,
+}: SidebarProps) {
+ return (
+
+ );
+}
+
+function Section({ label, children }: { label: string; children: React.ReactNode }) {
+ return (
+
+
+ {label}
+
+
{children}
+
+ );
+}
+
+function NavItem({
+ icon: Icon,
+ label,
+ count,
+ active,
+ onClick,
+}: {
+ icon: React.ElementType;
+ label: string;
+ count?: number;
+ active?: boolean;
+ onClick: () => void;
+}) {
+ return (
+
+ );
+}
diff --git a/strix/viewer_src/src/components/vulnerability/VulnerabilityDetail.tsx b/strix/viewer_src/src/components/vulnerability/VulnerabilityDetail.tsx
index a3312023..9bff0c65 100644
--- a/strix/viewer_src/src/components/vulnerability/VulnerabilityDetail.tsx
+++ b/strix/viewer_src/src/components/vulnerability/VulnerabilityDetail.tsx
@@ -1,7 +1,8 @@
"use client";
import React, { useState } from "react";
-import { Clock, CheckCircle2, Ban, History, BellOff } from "lucide-react";
+import { Clock, CheckCircle2, Ban, History, BellOff, Wrench, GitMerge, GitPullRequest } from "lucide-react";
+import { ProInlineCta } from "@/components/ProCta";
import { Vulnerability, VulnerabilityStatus, SEVERITY_COLORS, STATUS_META, isSeverityOverridden } from "@/types/issues";
import { formatTimeAgo } from "@/lib/utils";
import { getSeverityDot } from "@/lib/vulnerability-utils";
@@ -228,6 +229,34 @@ export default function VulnerabilityDetail({ vulnerability }: VulnerabilityDeta
/>
+
+ {/* Team-workflow CTAs (Pro). Highest-intent surface: act on this finding. */}
+
+
Ship the fix with your team
+
+ Take this finding into your team's workflow.
+
+
+
);
}