\definecolor{heroBlue}{RGB}{212, 228, 255}
\definecolor{heroBlueBorder}{RGB}{64, 103, 178}
\definecolor{heroGreen}{RGB}{214, 238, 216}
\definecolor{heroGreenBorder}{RGB}{48, 133, 66}
\definecolor{heroAmber}{RGB}{246, 230, 202}
\definecolor{heroAmberBorder}{RGB}{166, 121, 51}
\definecolor{heroGray}{RGB}{236, 236, 236}
\definecolor{heroGrayBorder}{RGB}{120, 120, 120}

% Panels occupy y = 2.2 .. 10.0
% Cross-panel connector gutter lives at y = 1.0 .. 2.2  (clearly below all nodes)
\begin{tikzpicture}[
    >=Stealth,
    font=\small,
    panel/.style={draw=black!65, dashed, rounded corners=4pt, line width=0.85pt},
    bB/.style={rectangle, rounded corners=3pt, draw=heroBlueBorder,  fill=heroBlue,
               line width=0.9pt, align=center, minimum height=0.85cm},
    bG/.style={rectangle, rounded corners=3pt, draw=heroGreenBorder, fill=heroGreen,
               line width=0.9pt, align=center, minimum height=0.85cm},
    bA/.style={rectangle, rounded corners=3pt, draw=heroAmberBorder, fill=heroAmber,
               line width=0.9pt, align=center, minimum height=0.85cm},
    bY/.style={rectangle, rounded corners=3pt, draw=heroGrayBorder,  fill=heroGray,
               line width=0.9pt, align=center, minimum height=0.85cm},
    pill/.style={ellipse, draw=black!50, fill=black!4, line width=0.75pt,
                 align=center, minimum width=1.6cm, minimum height=0.68cm},
    arr/.style={->, draw=black!80, line width=0.88pt},
    darr/.style={->, draw=black!60, line width=0.80pt, densely dashed},
    crossA/.style={->, draw=heroAmberBorder!90!black, line width=1.15pt, dash pattern=on 3.5pt off 2pt},
    crossG/.style={->, draw=heroGreenBorder!90!black, line width=1.15pt, dash pattern=on 3.5pt off 2pt},
    arrG/.style={->, draw=heroGreenBorder!90!black, line width=1.15pt},
    lbl/.style={font=\scriptsize, align=center, fill=white, inner sep=1.5pt, text=black}
]

%% ============================================================
%%  Panel A   x: 0.2–11.2    y: 2.2–10.0
%% ============================================================
\draw[panel] (0.2,2.2) rectangle (11.2,10.0);
\node[anchor=west, font=\small\bfseries] at (0.45,9.72) {(a) Online platform and data plane};

\node[pill] (human) at (1.3, 8.55) {Human};
\node[pill] (agent) at (1.3, 7.45) {Agent};

\node[bB, minimum width=2.75cm] (web)      at (4.2,  8.0)  {Next.js\\Web App};
\node[bB, minimum width=2.75cm] (provider) at (7.35, 8.0)  {Pricing\\Provider};
\node[bY, minimum width=1.85cm] (redis)    at (9.85, 8.0)  {Redis};

\node[bG, minimum width=3.1cm]  (kBehav)  at (4.0,  6.2)  {Kafka stream\\Behavior events};
\node[bG, minimum width=3.0cm]  (kQuotes) at (7.5,  6.2)  {Kafka stream\\Price quotes};

\node[bA, minimum width=3.1cm]  (worker)   at (4.0, 4.4)  {Worker / ETL\\Feature jobs};
\node[bA, minimum width=2.65cm] (registry) at (8.45, 4.4)  {Model\\Registry};

% service row
\draw[arr]   (human.east)    -- (web.west);
\draw[arr]   (agent.east)    -- (web.west);
\draw[arr]   (web.east)      -- (provider.west);
% single arrow: bidir on a short edge stacks two tips and reads as a messy cross
\draw[arr] (provider.east) -- (redis.west);

% web/provider -> kafka
\draw[arr] (web.south)      -- (kBehav.north)
    node[midway, left, lbl] {$e=(a,i,t,\mu,\delta)$};
\draw[arr] (provider.south) -- (kQuotes.north)
    node[midway, right, lbl] {$(i,p,\mathrm{sid},\phi,t)$};

% kafka -> worker: behavior stream vertical; price quotes L-shaped so both meet worker without a diagonal across the panel
\draw[arr] (kBehav.south) -- (worker.north);
\draw[arr, rounded corners=3pt] (kQuotes.south) -- (7.5, 5.35) -| (worker.north);

% worker -> registry
\draw[arr] (worker.east) -- (registry.west);

% model refresh: registry east -> goes right to x=11.0, north to y=9.2, left to provider
% this keeps it entirely inside panel A with no crossing of nodes
\draw[crossA, rounded corners=6pt]
    (registry.east) -- (11.0, 4.4)
    -- (11.0, 9.2)
    -- node[midway, lbl] {model refresh} (provider.north |- 0, 9.2)
    -- (provider.north);

%% ============================================================
%%  Panel B   x: 11.6–20.0    y: 2.2–10.0
%% ============================================================
\draw[panel] (11.6,2.2) rectangle (20.0,10.0);
\node[anchor=west, font=\small\bfseries] at (11.85,9.72) {(b) Distinguishability layer};

% x positions shifted +0.3 from the original layout (between left-heavy and +0.55 which hugged the right edge)
\node[bG, minimum width=2.4cm]  (session) at (14.3, 8.9)  {Session prefix\\$\tau'$};
\node[bB, minimum width=2.4cm]  (empKern) at (13.95,7.45) {Empirical kernel\\$\hat T'$};
\node[bY, minimum width=2.4cm]  (weakLab) at (17.85,8.9) {Weak labels\\$\mathcal{D}_H,\mathcal{D}_A$};
\node[bY, minimum width=2.2cm]  (protoH)  at (13.1, 5.9)  {Prototype\\$\bar T_H$};
\node[bA, minimum width=2.4cm]  (kldist)  at (15.85,5.9)  {KL distances\\$\Delta_H,\Delta_A$};
\node[bY, minimum width=2.2cm]  (protoA)  at (18.6, 5.9)  {Prototype\\$\bar T_A$};
\node[bB, minimum width=2.9cm]  (calHead) at (13.85,4.25) {Contrastive\\calibration head};
\node[bG, minimum width=2.55cm] (score)   at (18.05,4.25) {Session score\\$f(\tau'),\hat\alpha(\tau')$};

\node[lbl] at (15.85, 3.15) {$\hat\alpha(\tau')=\sigma\!\left(\beta(\Delta_H-\Delta_A)\right)$};

\draw[arr, rounded corners=4pt] (session.south)  -- (empKern.north);
\draw[arr, rounded corners=4pt] (empKern.south) -- (13.95, 6.8) -| (protoH.north);
\draw[arr, rounded corners=4pt] (weakLab.south) -- (17.85, 6.8) -| (protoA.north);
% weak labels -> protoH: go south then hard-left below weakLab
\draw[arr, rounded corners=4pt] (weakLab.south) -- (17.85,6.8) -| (protoH.north);
\draw[arr] (protoH.east)    -- (kldist.west);
\draw[arr] (protoA.west)    -- (kldist.east);
\draw[arr, rounded corners=4pt] (kldist.south)  -- (calHead.north);
\draw[arr] (calHead.east)   -- (score.west);

%% ============================================================
%%  Panel C   x: 20.4–31.0    y: 2.2–10.0
%% ============================================================
\draw[panel] (20.4,2.2) rectangle (31.0,10.0);
\node[anchor=west, font=\small\bfseries] at (21.05,9.72) {(c) Distributionally robust control};

\node[bB, minimum width=3.1cm] (state)    at (23.15, 8.9)
    {State summary\\$[p_{t-1},\hat q_{t-1},f(\tau')]$};
\node[bY, minimum width=2.9cm] (ambSet)   at (23.15, 7.45) {Ambiguity set\\$\mathcal U_\epsilon(\hat P_N)$};
\node[bG, minimum width=2.9cm] (innerMin) at (28.55, 7.45) {Inner minimisation\\$\min_{Q\in\mathcal U_\epsilon}$};
\node[bY, minimum width=8.2cm] (contScen) at (25.9,  5.9)
    {Contamination scenarios $\;\alpha_k\in\mathcal A_{\epsilon_\alpha}(\alpha_0)$};
\node[bA, minimum width=8.8cm] (reward)   at (25.9,  4.45)
    {$r_t = R(p_t,\hat q_t) - \lambda\,\mathrm{COI}_{\mathrm{leak}}(p_t,\tau_t') - \eta\,UX_t$};
\node[bB, minimum width=2.85cm] (policy)  at (22.75, 3.05) {Robust policy $\pi^*$};
\node[bG, minimum width=2.85cm] (publish) at (29.05, 3.05) {Publish price\\vector $p_t$};

\node[lbl] at (25.9, 2.55) {$\pi^*=\arg\max_\pi\min_{Q\in\mathcal U_\epsilon}\mathbb{E}[r_t]$};

\draw[arr] (state.south)      -- (ambSet.north);
\draw[arr] (ambSet.east)      -- (innerMin.west);
\draw[arr, rounded corners=4pt] (ambSet.south) -- (23.15, 6.6) -| ([xshift=-2cm]contScen.north);
\draw[arr, rounded corners=4pt] (innerMin.south) -- (28.55, 6.6) -| ([xshift=2cm]contScen.north);
\draw[arr] (contScen.south)   -- (reward.north);
% join reward to policy along policy.north y so the last segment never approaches north from below (avoids upward arrowhead on top edge)
\draw[arr, rounded corners=4pt] (reward.south) -- (reward.south |- policy.north) -- (policy.north);
\draw[arr] (policy.east)      -- (publish.west);
% market response: up the right edge, then left into state summary from the east
\draw[arrG, rounded corners=6pt] (publish.east) -- (30.6, 3.05)
    -- (30.6, 8.9)
    -- node[midway, above, lbl] {market response} (state.east);

%% ============================================================
%%  Cross-panel connectors – gutter at y = 1.0..2.2
%%  Three separate depths: 1.85, 1.45, 1.05  (no overlaps)
%% ============================================================

% 1. Worker -> Session  (depth y=1.85, shallowest)
\draw[crossA, rounded corners=6pt]
    (worker.south) -- (worker.south |- 0, 1.85)
    -- node[pos=0.5, lbl] {offline extraction} (11.4, 1.85)
    -- (11.4, 8.9)
    -- (session.west);

% 2. Score -> State  (depth y=1.45)
\draw[crossG, rounded corners=6pt]
    (score.south) -- (score.south |- 0, 1.45)
    -- node[pos=0.5, lbl] {contamination signal} (20.2, 1.45)
    -- (20.2, 8.9)
    -- (state.west);

% 3. Publish -> Provider  (depth y=1.05, deepest)
\draw[crossG, rounded corners=3pt]
    (publish.south) -- (publish.south |- 0, 1.05)
    -- node[pos=0.4, lbl] {serve online} (5.8, 1.05)
    -- (5.8, 7.7)
    -- ([yshift=-0.3cm]provider.west);

\end{tikzpicture}
