exp(06): focused investigation — visual hierarchy without layout changes
Emphasise the active investigation card through stronger elevation, clearer borders, and improved spacing. Quiet supporting panels by reducing border opacity, softening heading weight, and lowering text contrast — making them available without competing for attention. Facilitator card receives a warm surface tint to read as a briefing card rather than a generic panel. Documentation: close experiment 05 with findings, add experiment 06 to the design evolution log, add Attention Hierarchy to UX guidelines, defer dark mode to a future Investigation Mode experiment. Presentation changes only — no reasoning, prompts, graph, API, or backend modifications.
This commit is contained in:
@@ -82,15 +82,15 @@ export default function InvestigationMap({ turnCount = 0 }) {
|
||||
if (!hasActiveTopics && groups.established.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-gray-100 bg-gray-50/50 p-4" role="region" aria-label="Investigation map preview">
|
||||
<h2 className="mb-1 text-sm font-semibold tracking-tight text-gray-400">
|
||||
<div className="rounded-lg border border-gray-200/60 bg-gray-50/30 p-4" role="region" aria-label="Investigation map preview">
|
||||
<h2 className="mb-1 text-[11px] font-medium tracking-widest uppercase text-gray-300">
|
||||
Investigation Map
|
||||
</h2>
|
||||
<p className="mb-3 text-xs text-gray-400">
|
||||
<p className="mb-3 text-xs text-gray-400/70">
|
||||
Active investigation topics and their status.
|
||||
</p>
|
||||
|
||||
<div className="space-y-px border-t border-gray-100 pt-3" role="list" aria-label="Investigation topics">
|
||||
<div className="space-y-px border-t border-gray-200/60 pt-3" role="list" aria-label="Investigation topics">
|
||||
{topics.map((topic, i) => (
|
||||
<TopicRow key={`${topic.title}-${i}`} title={topic.title} status={topic.status} />
|
||||
))}
|
||||
|
||||
@@ -62,10 +62,10 @@ function InvestigationSummaryPanel({ graph, selectedQuestion, result, updateStat
|
||||
}
|
||||
|
||||
const statusColors = {
|
||||
idle: { border: "border-gray-200", bg: "bg-gray-50", text: "text-gray-600" },
|
||||
investigating: { border: "border-blue-200", bg: "bg-blue-50", text: "text-blue-700" },
|
||||
complete: { border: "border-green-200", bg: "bg-green-50", text: "text-green-700" },
|
||||
limit: { border: "border-gray-300", bg: "bg-gray-100", text: "text-gray-500" },
|
||||
idle: { border: "border-gray-200/60", bg: "bg-gray-50/40", text: "text-gray-400" },
|
||||
investigating: { border: "border-blue-200/60", bg: "bg-blue-50/30", text: "text-blue-600" },
|
||||
complete: { border: "border-green-200/60", bg: "bg-green-50/30", text: "text-green-600" },
|
||||
limit: { border: "border-gray-200", bg: "bg-gray-50/40", text: "text-gray-400" },
|
||||
};
|
||||
|
||||
const colors = statusColors[currentStatus.level] || statusColors.idle;
|
||||
@@ -125,10 +125,10 @@ function InvestigationSummaryPanel({ graph, selectedQuestion, result, updateStat
|
||||
{/* Current understanding */}
|
||||
{currentUnderstanding && (
|
||||
<div>
|
||||
<h3 className="mb-1 text-xs font-bold uppercase tracking-wider text-gray-400">
|
||||
<h3 className="mb-1 text-[11px] font-medium tracking-widest uppercase text-gray-400/70">
|
||||
What we understand so far
|
||||
</h3>
|
||||
<p className="text-sm leading-relaxed text-gray-700">{currentUnderstanding}</p>
|
||||
<p className="text-sm leading-relaxed text-gray-600">{currentUnderstanding}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -99,8 +99,8 @@ function UnexpectedStateCard({ stateName, onRetry, onRestart }) {
|
||||
|
||||
function ContinueLaterBanner({ onRestart }) {
|
||||
return (
|
||||
<div className="rounded-lg border border-blue-200 bg-blue-50 px-5 py-4 text-center">
|
||||
<p className="text-sm text-blue-800">
|
||||
<div className="rounded-lg border border-blue-200/60 bg-blue-50/40 px-5 py-4 text-center">
|
||||
<p className="text-sm text-blue-700/70">
|
||||
Your previous investigation state is still saved. You can continue where you left off or start fresh.
|
||||
</p>
|
||||
{onRestart && (
|
||||
@@ -248,13 +248,13 @@ function CurrentInvestigationCard({ selectedQuestion, graph }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="investigation-card rounded-lg border-2 border-green-300 bg-green-50 p-6">
|
||||
<h2 className="mb-2 text-sm font-bold tracking-wide text-green-700">
|
||||
<div className="investigation-card rounded-lg border-[2.5px] border-green-400 bg-gradient-to-b from-green-50 to-white p-8 shadow-sm">
|
||||
<h2 className="mb-3 text-xs font-bold tracking-widest uppercase text-green-600/70">
|
||||
Investigation
|
||||
</h2>
|
||||
<p className="text-xl font-semibold leading-snug text-gray-900">{q}</p>
|
||||
<p className="text-2xl font-semibold leading-tight text-gray-900">{q}</p>
|
||||
{whyMattersText && (
|
||||
<p className="mt-4 text-sm leading-relaxed text-green-800">
|
||||
<p className="mt-5 text-sm leading-relaxed text-green-800/80">
|
||||
{whyMattersText}
|
||||
</p>
|
||||
)}
|
||||
@@ -317,11 +317,11 @@ function CurrentUnderstandingCard({ currentSummary, plainLanguage }) {
|
||||
if (!summary) return null;
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-gray-100 bg-white px-6 pt-5 pb-6">
|
||||
<h2 className="mb-4 text-xl font-semibold tracking-tight text-gray-800">
|
||||
<div className="rounded-lg border border-gray-200/60 bg-white/80 px-6 pt-5 pb-6">
|
||||
<h2 className="mb-4 text-base font-semibold tracking-tight text-gray-400">
|
||||
Understanding
|
||||
</h2>
|
||||
<p className="text-sm leading-relaxed text-gray-700">{summary}</p>
|
||||
<p className="text-sm leading-relaxed text-gray-600">{summary}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -331,11 +331,11 @@ function PlainLanguageCard({ summary }) {
|
||||
if (!summary) return null;
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-gray-100 bg-white px-6 pt-5 pb-6">
|
||||
<h2 className="mb-4 text-xl font-semibold tracking-tight text-gray-800">
|
||||
<div className="rounded-lg border border-gray-200/60 bg-white/80 px-6 pt-5 pb-6">
|
||||
<h2 className="mb-4 text-base font-semibold tracking-tight text-gray-400">
|
||||
Understanding
|
||||
</h2>
|
||||
<p className="text-sm leading-relaxed text-gray-700">{summary}</p>
|
||||
<p className="text-sm leading-relaxed text-gray-600">{summary}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -352,7 +352,7 @@ function InvestigationHistoryCard({ turn }) {
|
||||
|
||||
return (
|
||||
<details
|
||||
className="rounded-lg border border-gray-100 bg-gray-50/60"
|
||||
className="rounded-lg border border-gray-200/60 bg-gray-50/30"
|
||||
key={turn.id}
|
||||
open={!isCollapsed}
|
||||
data-testid="investigation-turn"
|
||||
@@ -383,7 +383,7 @@ function InvestigationHistory({ turns }) {
|
||||
|
||||
return (
|
||||
<div className="space-y-3" data-testid="investigation-history">
|
||||
<h2 className="text-xs font-semibold tracking-wider text-gray-400">
|
||||
<h2 className="text-[11px] font-medium tracking-widest uppercase text-gray-300">
|
||||
History
|
||||
</h2>
|
||||
<div className="space-y-2">
|
||||
@@ -402,12 +402,12 @@ function OriginalSituation({ scenario, centralStatement }) {
|
||||
if (!text) return null;
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-gray-100 bg-gray-50/70 px-5 py-4">
|
||||
<h2 className="mb-2 text-xs font-semibold tracking-wider text-gray-400">
|
||||
<div className="rounded-lg border border-gray-200/60 bg-gray-50/40 px-5 py-4">
|
||||
<h2 className="mb-2 text-[11px] font-medium tracking-widest uppercase text-gray-300">
|
||||
Situation
|
||||
</h2>
|
||||
|
||||
<p className="whitespace-pre-wrap text-sm leading-relaxed text-gray-700">
|
||||
<p className="whitespace-pre-wrap text-sm leading-relaxed text-gray-600">
|
||||
{text}
|
||||
</p>
|
||||
</div>
|
||||
@@ -440,7 +440,7 @@ function UpdateAcknowledgement({ updateResult }) {
|
||||
className="transition-all duration-1500 ease-in"
|
||||
style={{ opacity: visible ? 0.7 : 0, maxHeight: visible ? "4rem" : "0", marginBottom: visible ? "1rem" : "0" }}
|
||||
>
|
||||
<div className="rounded-lg border border-blue-200 bg-blue-50/60 px-4 py-2 text-xs text-blue-800">
|
||||
<div className="rounded-md border border-blue-200/60 bg-blue-50/30 px-4 py-2 text-xs text-blue-700/60">
|
||||
{summary}
|
||||
</div>
|
||||
</div>
|
||||
@@ -450,11 +450,11 @@ function UpdateAcknowledgement({ updateResult }) {
|
||||
// ── Developer details disclosure ──────────────────────────────
|
||||
function DeveloperDetails({ graph, selectedQuestion, diagnostics, newlySurfacedNodeIds, updateResult }) {
|
||||
return (
|
||||
<details className="rounded-lg border border-gray-200 bg-gray-50">
|
||||
<summary className="cursor-pointer px-5 py-3 text-sm font-medium text-gray-600 hover:text-gray-800">
|
||||
<details className="rounded-lg border border-gray-200/60 bg-gray-50/30">
|
||||
<summary className="cursor-pointer px-5 py-3 text-sm font-medium text-gray-400 hover:text-gray-600">
|
||||
Developer details
|
||||
</summary>
|
||||
<div className="border-t border-gray-200 px-5 pb-4 pt-3 space-y-4">
|
||||
<div className="border-t border-gray-200/60 px-5 pb-4 pt-3 space-y-4">
|
||||
{graph && (
|
||||
<SituationGraphView
|
||||
situationGraph={graph}
|
||||
@@ -482,13 +482,13 @@ function LoadingOverlay({ isLoading, elapsed, currentMessage, variant }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-gray-200 bg-blue-50 px-5 py-6" role="status" aria-busy="true" data-testid="loading-overlay">
|
||||
<div className="rounded-lg border border-blue-200/60 bg-blue-50/40 px-6 py-7" role="status" aria-busy="true" data-testid="loading-overlay">
|
||||
<div className="flex items-center gap-3">
|
||||
<ActivitySpinner />
|
||||
<span className="text-base font-medium text-blue-900">Working through your situation</span>
|
||||
<span className="text-base font-medium text-blue-800/70">Working through your situation</span>
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-blue-700">{statusText}</p>
|
||||
<p className="mt-1 text-xs text-blue-500" aria-live="polite">
|
||||
<p className="mt-3 text-sm text-blue-600/60">{statusText}</p>
|
||||
<p className="mt-2 text-xs text-blue-400/50" aria-live="polite">
|
||||
This has been running for {elapsed}s.
|
||||
{variant === "initial" && elapsed >= 45 && (
|
||||
<span className="block mt-1">This can take around a minute with the current local model.</span>
|
||||
|
||||
@@ -395,18 +395,18 @@ export default function ScenarioForm() {
|
||||
{/* Left panel — Facilitator (1/3 on desktop) */}
|
||||
{!hideFacilitatorOnLanding && (
|
||||
<div className="md:col-span-1">
|
||||
<div className="rounded-lg border border-gray-100 bg-gray-50/60 px-6 pt-5 pb-6 sticky top-6">
|
||||
<h2 className="mb-3 text-sm font-semibold tracking-wide text-gray-500 uppercase">Before we begin</h2>
|
||||
<p className="text-sm leading-relaxed text-gray-700 mb-4">
|
||||
<div className="rounded-lg border border-amber-200/60 bg-gradient-to-b from-amber-50/60 to-white px-8 pt-7 pb-7 sticky top-6 shadow-sm">
|
||||
<h2 className="mb-4 text-xs font-bold tracking-widest uppercase text-amber-600/60">Before we begin</h2>
|
||||
<p className="text-sm leading-relaxed text-amber-900/80 mb-5">
|
||||
The Confidence Engine helps build confidence by understanding situations before deciding what to do.
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed text-gray-700 mb-3">
|
||||
<p className="text-sm leading-relaxed text-amber-900/70 mb-4">
|
||||
You do not need to know exactly what the problem is.
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed text-gray-700 mb-6">
|
||||
<p className="text-sm leading-relaxed text-amber-900/70 mb-7">
|
||||
Simply describe what you have observed. We will work through it together, one question at a time.
|
||||
</p>
|
||||
<div className="flex items-center gap-2 pt-4 border-t border-gray-200">
|
||||
<div className="flex items-center gap-2 pt-5 border-t border-amber-100/60">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="dismiss-facilitator"
|
||||
@@ -428,7 +428,7 @@ export default function ScenarioForm() {
|
||||
|
||||
{/* Right panel — Workspace (2/3 on desktop) */}
|
||||
<div className={hideFacilitatorOnLanding ? "md:col-span-3" : "md:col-span-2"}>
|
||||
<h2 className="mb-3 text-sm font-semibold tracking-wide text-gray-500 uppercase">Tell me what's happening</h2>
|
||||
<h2 className="mb-4 text-xs font-bold tracking-widest uppercase text-gray-400">Tell me what's happening</h2>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
value={scenario}
|
||||
@@ -458,8 +458,8 @@ export default function ScenarioForm() {
|
||||
)}
|
||||
|
||||
{status === "idle" && MOCK_ENABLED && (
|
||||
<details className="rounded-lg border border-gray-200 bg-gray-50">
|
||||
<summary className="cursor-pointer px-4 py-2 text-sm font-medium text-gray-600">Developer details</summary>
|
||||
<details className="rounded-lg border border-gray-200/60 bg-gray-50/30">
|
||||
<summary className="cursor-pointer px-4 py-2 text-sm font-medium text-gray-400 hover:text-gray-600">Developer details</summary>
|
||||
<div className="space-y-3 px-4 pb-4">
|
||||
<div>
|
||||
<label htmlFor="mock-scenario" className="block text-xs font-medium text-gray-500 mb-1">Mock scenario</label>
|
||||
@@ -483,7 +483,7 @@ export default function ScenarioForm() {
|
||||
type="button"
|
||||
onClick={() => handleScenarioFill(s.key)}
|
||||
disabled={!scenario.trim() && scenario !== s.centralStatement}
|
||||
className="rounded-md border border-gray-200 bg-white px-3 py-1.5 text-xs text-gray-600 hover:bg-gray-100 disabled:opacity-30"
|
||||
className="rounded-md border border-gray-200/60 bg-white/80 px-3 py-1.5 text-xs text-gray-500 hover:bg-gray-50 disabled:opacity-30"
|
||||
>
|
||||
{s.label} ({s.turnCount} turns)
|
||||
</button>
|
||||
@@ -558,7 +558,7 @@ export default function ScenarioForm() {
|
||||
setCurrentUnderstanding(null);
|
||||
setUpdateError(null);
|
||||
}}
|
||||
className="rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-600 transition hover:bg-gray-50"
|
||||
className="rounded-lg border border-gray-200/60 px-4 py-2 text-sm font-medium text-gray-500 transition hover:bg-gray-50/80"
|
||||
>
|
||||
Start new investigation
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user