+ {/* Header */}
+
+
+
+ setSymbol(e.target.value.toUpperCase().trim())}
+ className="w-24 rounded bg-bg-secondary border border-fg-muted/20 px-2 py-1.5 text-sm text-fg"
+ maxLength={12}
+ />
+
+
+
+
+
+
+
+ {/* Picture quality + evidence summary */}
+ {quality ? (
+
+
+ {QUALITY_LABEL[quality]}
+
+
+ Bull {bullEvidence.toFixed(1)}
+ {' '}·{' '}
+ Bear {bearEvidence.toFixed(1)}
+ {' '}·{' '}
+ Net = 0 ? 'text-green-400' : 'text-red-400'}>
+ {(bullEvidence - bearEvidence).toFixed(1)}
+
+
+ {change?.changed && (
+
+ {change.changeType === 'improved' ? 'Improved' : 'Deteriorated'} from {change.previous ?? '?'}
+
+ )}
+
+ ) : evaluation !== null ? (
+
No evaluations yet for {symbol}. Data will appear once the confluence evaluator runs.
+ ) : null}
+
+ {/* Slot evidence grid */}
+ {assessments.length > 0 && (
+
+
+ {assessments.map((a) => {
+ const meta = slotMeta.get(a.id);
+ const fired = a.state === 'fired';
+ return (
+
+
+
+ {meta?.name ?? a.id}
+
+ {meta?.body ?? '?'}
+
+
+ {a.note &&
{a.note}
}
+
+ );
+ })}
+
+
+ )}
+
+ {/* Scorecard */}
+ {scorecard && scorecard.slots.length > 0 && (
+
+ {scorecard.note}
+
+
+
+ | Slot |
+ Fires |
+ Hit Rate |
+ Reliability |
+
+
+
+ {scorecard.slots
+ .sort((a, b) => ((b.hitRate as number) ?? 0) - ((a.hitRate as number) ?? 0))
+ .map((s) => (
+
+ | {slotMeta.get(s.slotId as string)?.name ?? (s.slotId as string)} |
+ {s.fires as number} |
+ {s.hitRate != null ? `${((s.hitRate as number) * 100).toFixed(0)}%` : '-'} |
+ {(s.reliabilityWeight as number).toFixed(2)} |
+
+ ))}
+
+
+
+ )}
+
+ );
+}
\ No newline at end of file