\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.82cm}, 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}, bidir/.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); \draw[bidir] (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 (straight south) \draw[arr] (kBehav.south) -- (worker.north); \draw[arr] (kQuotes.south) -- (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.4 y: 2.2–10.0 %% ============================================================ \draw[panel] (11.6,2.2) rectangle (19.8,10.0); \node[anchor=west, font=\small\bfseries] at (11.85,9.72) {(b) Distinguishability layer}; \node[bG, minimum width=2.4cm] (session) at (14.0, 8.9) {Session prefix\\$\tau'$}; \node[bB, minimum width=2.4cm] (empKern) at (13.65,7.45) {Empirical kernel\\$\hat T'$}; \node[bY, minimum width=2.4cm] (weakLab) at (17.55,8.9) {Weak labels\\$\mathcal{D}_H,\mathcal{D}_A$}; \node[bY, minimum width=2.2cm] (protoH) at (12.8, 5.9) {Prototype\\$\bar T_H$}; \node[bA, minimum width=2.4cm] (kldist) at (15.55,5.9) {KL distances\\$\Delta_H,\Delta_A$}; \node[bY, minimum width=2.2cm] (protoA) at (18.3, 5.9) {Prototype\\$\bar T_A$}; \node[bB, minimum width=2.9cm] (calHead) at (13.55,4.25) {Contrastive\\calibration head}; \node[bG, minimum width=2.55cm] (score) at (17.75,4.25) {Session score\\$f(\tau'),\hat\alpha(\tau')$}; \node[lbl] at (15.55, 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.65, 6.8) -| (protoH.north); \draw[arr, rounded corners=4pt] (weakLab.south) -- (17.55, 6.8) -| (protoA.north); % weak labels -> protoH: go south then hard-left below weakLab \draw[arr, rounded corners=4pt] (weakLab.south) -- (17.55,6.8) -| (protoH.north east); \draw[arr] (protoH.east) -- (kldist.west); \draw[arr] (protoA.west) -- (kldist.east); \draw[arr] (kldist.south) -- (calHead.north east); \draw[arr] (calHead.east) -- (score.west); %% ============================================================ %% Panel C x: 20.8–31.0 y: 2.2–10.0 %% ============================================================ \draw[panel] (20.8,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); \draw[arr, rounded corners=6pt] (reward.south) -- (25.9, 3.7) -| (policy.north); \draw[arr] (policy.east) -- (publish.west); % market response: up the right edge of panel C, entirely inside, rounded \draw[arrG, rounded corners=6pt] (publish.east) -- (30.6, 3.05) -- (30.6, 9.8) -- node[midway, lbl] {market response} (state.north |- 0, 9.8) -- (state.north); %% ============================================================ %% 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.6, 1.45) -- (20.6, 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}