@@ -165,6 +165,7 @@ export default function PathInvariance() {
165165 const currentSpace = selectedSpace in data . per_embedding_space && ! data . per_embedding_space [ selectedSpace ] . error
166166 ? selectedSpace : spaces [ 0 ]
167167
168+ const isJudge = space ?. dimensions === 'judgment'
168169 const points = space ?. pca_2d || [ ]
169170 const responses = data . responses || [ ]
170171 const clustering = space ?. clustering || { }
@@ -199,9 +200,9 @@ export default function PathInvariance() {
199200 </ div >
200201 ) }
201202
202- { /* Embedding space selector + color toggle */ }
203- < div className = "flex items-center gap-4 mb-6" >
204- < div className = "flex gap-2" >
203+ { /* Space selector + color toggle */ }
204+ < div className = "flex items-center gap-4 mb-6 flex-wrap " >
205+ < div className = "flex gap-2 flex-wrap " >
205206 { spaces . map ( s => (
206207 < button
207208 key = { s }
@@ -216,76 +217,94 @@ export default function PathInvariance() {
216217 </ button >
217218 ) ) }
218219 </ div >
219- < div className = "flex gap-2 ml-auto" >
220- < button
221- onClick = { ( ) => setColorBy ( 'phase' ) }
222- className = { `px-3 py-1 text-xs rounded border ${
223- colorBy === 'phase' ? 'border-accent text-accent' : 'border-border text-muted'
224- } `}
225- >
226- Color by Phase
227- </ button >
228- < button
229- onClick = { ( ) => setColorBy ( 'model' ) }
230- className = { `px-3 py-1 text-xs rounded border ${
231- colorBy === 'model' ? 'border-accent text-accent' : 'border-border text-muted'
232- } `}
233- >
234- Color by Model
235- </ button >
236- </ div >
220+ { ! isJudge && (
221+ < div className = "flex gap-2 ml-auto" >
222+ < button
223+ onClick = { ( ) => setColorBy ( 'phase' ) }
224+ className = { `px-3 py-1 text-xs rounded border ${
225+ colorBy === 'phase' ? 'border-accent text-accent' : 'border-border text-muted'
226+ } `}
227+ >
228+ Color by Phase
229+ </ button >
230+ < button
231+ onClick = { ( ) => setColorBy ( 'model' ) }
232+ className = { `px-3 py-1 text-xs rounded border ${
233+ colorBy === 'model' ? 'border-accent text-accent' : 'border-border text-muted'
234+ } `}
235+ >
236+ Color by Model
237+ </ button >
238+ </ div >
239+ ) }
237240 </ div >
238241
239- { /* Main layout: scatter + metrics */ }
240- < div className = "grid grid-cols-1 lg:grid-cols-5 gap-6 mb-10" >
241- { /* Scatter plot */ }
242- < div className = "lg:col-span-3" >
243- < ScatterPlot
244- points = { points }
245- responses = { responses }
246- colorBy = { colorBy }
247- space = { currentSpace }
248- hoveredPoint = { hoveredPoint }
249- setHoveredPoint = { setHoveredPoint }
250- />
251- { /* Legend */ }
252- < div className = "flex flex-wrap gap-3 mt-3" >
253- { colorBy === 'model'
254- ? Object . entries ( MODEL_COLORS )
255- . filter ( ( [ m ] ) => responses . some ( r => r . model === m ) )
256- . map ( ( [ m , c ] ) => (
257- < div key = { m } className = "flex items-center gap-1.5" >
258- < div className = "w-3 h-3 rounded-full" style = { { backgroundColor : c } } />
259- < span className = "text-xs text-muted" > { m } </ span >
260- </ div >
261- ) )
262- : Object . entries ( PHASE_COLORS )
263- . filter ( ( [ p ] ) => responses . some ( r => r . phase === p ) )
264- . map ( ( [ p , c ] ) => (
265- < div key = { p } className = "flex items-center gap-1.5" >
266- < div className = "w-3 h-3 rounded-full" style = { { backgroundColor : c } } />
267- < span className = "text-xs text-muted" > { p } </ span >
268- </ div >
269- ) )
270- }
242+ { /* Main layout */ }
243+ < div className = { `grid grid-cols-1 ${ isJudge ? 'lg:grid-cols-3' : 'lg:grid-cols-5' } gap-6 mb-10` } >
244+ { /* Scatter plot — embedding spaces only */ }
245+ { ! isJudge && (
246+ < div className = "lg:col-span-3" >
247+ < ScatterPlot
248+ points = { points }
249+ responses = { responses }
250+ colorBy = { colorBy }
251+ space = { currentSpace }
252+ hoveredPoint = { hoveredPoint }
253+ setHoveredPoint = { setHoveredPoint }
254+ />
255+ { /* Legend */ }
256+ < div className = "flex flex-wrap gap-3 mt-3" >
257+ { colorBy === 'model'
258+ ? Object . entries ( MODEL_COLORS )
259+ . filter ( ( [ m ] ) => responses . some ( r => r . model === m ) )
260+ . map ( ( [ m , c ] ) => (
261+ < div key = { m } className = "flex items-center gap-1.5" >
262+ < div className = "w-3 h-3 rounded-full" style = { { backgroundColor : c } } />
263+ < span className = "text-xs text-muted" > { m } </ span >
264+ </ div >
265+ ) )
266+ : Object . entries ( PHASE_COLORS )
267+ . filter ( ( [ p ] ) => responses . some ( r => r . phase === p ) )
268+ . map ( ( [ p , c ] ) => (
269+ < div key = { p } className = "flex items-center gap-1.5" >
270+ < div className = "w-3 h-3 rounded-full" style = { { backgroundColor : c } } />
271+ < span className = "text-xs text-muted" > { p } </ span >
272+ </ div >
273+ ) )
274+ }
275+ </ div >
276+ { /* Hover info */ }
277+ { hovered && (
278+ < div className = "mt-3 p-3 border border-border rounded bg-panel text-xs" >
279+ < span className = "text-gray-200 font-medium" > Q{ hovered . question_num } : { hovered . question_title } </ span >
280+ < span className = "text-muted" > — { hovered . model } — { hovered . phase } </ span >
281+ </ div >
282+ ) }
283+ < p className = "text-xs text-muted/60 mt-2" >
284+ PCA projection of { space ?. dimensions } d embeddings to 2D.
285+ { colorBy === 'phase'
286+ ? ' Tight clusters = same phase, same endpoint. Switch to "Color by Model" — if clusters break apart, it\'s training bias. If they hold, it\'s structural.'
287+ : ' If model colors are scattered (no model-specific clusters), model identity doesn\'t predict position in embedding space.' }
288+ </ p >
271289 </ div >
272- { /* Hover info */ }
273- { hovered && (
274- < div className = "mt-3 p-3 border border-border rounded bg-panel text-xs" >
275- < span className = "text-gray-200 font-medium" > Q{ hovered . question_num } : { hovered . question_title } </ span >
276- < span className = "text-muted" > — { hovered . model } — { hovered . phase } </ span >
290+ ) }
291+
292+ { /* Judge space description */ }
293+ { isJudge && (
294+ < div className = "lg:col-span-1" >
295+ < div className = "p-4 border border-border rounded-lg bg-panel" >
296+ < p className = "text-xs text-gray-200 font-medium mb-2" > Pairwise LLM Judgment</ p >
297+ < p className = "text-xs text-muted" >
298+ { space . judge_model } scored { space . dimensions === 'judgment' ? '663' : '' } response pairs for semantic similarity.
299+ No scatter plot — pairwise scores don't have spatial positions.
300+ The metrics are computed from the scored pairs directly.
301+ </ p >
277302 </ div >
278- ) }
279- < p className = "text-xs text-muted/60 mt-2" >
280- { space ?. dimensions === 'judgment' ? 'PCA projection of LLM similarity judgments to 2D.' : `PCA projection of ${ space ?. dimensions } d embeddings to 2D.` }
281- { colorBy === 'phase'
282- ? ' Tight clusters = same phase, same endpoint. Switch to "Color by Model" — if clusters break apart, it\'s training bias. If they hold, it\'s structural.'
283- : ' If model colors are scattered (no model-specific clusters), model identity doesn\'t predict position in embedding space.' }
284- </ p >
285- </ div >
303+ </ div >
304+ ) }
286305
287306 { /* Metrics */ }
288- < div className = " lg:col-span-2" >
307+ < div className = { isJudge ? ' lg:col-span-2' : 'lg:col-span-2' } >
289308 < div className = "p-4 border border-border rounded-lg bg-panel mb-4" >
290309 < h3 className = "text-xs font-semibold text-accent mb-3 tracking-wide" >
291310 KNN Purity (k=5)
0 commit comments