diff --git a/project-management/PJ2026-01/showcase/hwlab-first-principles/app.js b/project-management/PJ2026-01/showcase/hwlab-first-principles/app.js
new file mode 100644
index 00000000..f595f5cb
--- /dev/null
+++ b/project-management/PJ2026-01/showcase/hwlab-first-principles/app.js
@@ -0,0 +1,404 @@
+const STAGES = [
+ {
+ title: "分析工程",
+ summary: "识别 TIM2 输入捕获路径与 APB 时钟倍频条件。",
+ log: "scan TIM2 capture path · APB1 prescaler = 2",
+ },
+ {
+ title: "修改代码",
+ summary: "修正定时器时钟计算,并注入实板标定偏移。",
+ log: "patch Core/Src/frequency.c · +5 -3",
+ },
+ {
+ title: "Keil 编译",
+ summary: "使用受控工程配置生成可追溯固件产物。",
+ log: "build 0 error(s), 0 warning(s) · freq.axf",
+ },
+ {
+ title: "下载固件",
+ summary: "通过 CMSIS-DAP 将本次构建产物写入目标板。",
+ log: "flash 32.18 KiB · verify OK · reset target",
+ },
+ {
+ title: "UART 采集",
+ summary: "读取固件运行状态,确认输入捕获链路稳定。",
+ log: "uart capture stable · sample_count = 128",
+ },
+ {
+ title: "物理测量",
+ summary: "由 ioProbe 独立测量 FREQ_OUT,形成物理事实。",
+ log: "probe CH1 frequency · 999.8 Hz · error -0.02%",
+ },
+];
+
+const EVIDENCE = [
+ {
+ idle: "任务尚未进入受控执行。",
+ passed: "任务 freq-1khz-accuracy 已绑定 Workspace、Board、Probe 与目标指标。",
+ },
+ {
+ idle: "尚无 Agent 执行 trace。",
+ active: "Agent 正在受控工程上下文中分析、修改与执行。",
+ passed: "Agent trace 已封存:工程分析与代码变更均可回放。",
+ },
+ {
+ idle: "尚无本次构建产物。",
+ passed: "Keil 构建成功:0 error(s),0 warning(s),产物摘要已记录。",
+ },
+ {
+ idle: "目标板尚未写入本次固件。",
+ passed: "CMSIS-DAP 下载与校验通过,固件摘要与构建产物一致。",
+ },
+ {
+ idle: "UART 运行事实尚未采集。",
+ passed: "UART 连续采样 128 次,输入捕获状态稳定,无溢出。",
+ },
+ {
+ idle: "ioProbe 物理测量尚未开始。",
+ active: "ioProbe 正在独立采集 FREQ_OUT 波形。",
+ passed: "ioProbe 实测 999.8 Hz,误差 −0.02%,满足 ±0.10% 阈值。",
+ failed: "ioProbe 实测 972.4 Hz,误差 −2.76%,超出 ±0.10% 阈值。",
+ },
+ {
+ idle: "Aggregate 尚未汇总,不能保存为回归 Case。",
+ passed: "代码、构建、下载、运行与物理测量证据完整:Aggregate PASS。",
+ blocked: "物理测量未通过:Aggregate BLOCKED,阻止错误结果进入回归集。",
+ },
+];
+
+const stageButtons = [...document.querySelectorAll("[data-stage]")];
+const evidenceButtons = [...document.querySelectorAll("[data-evidence]")];
+const app = document.querySelector("#app");
+const runButton = document.querySelector("#runButton");
+const resetButton = document.querySelector("#resetButton");
+const failureMode = document.querySelector("#failureMode");
+const saveCaseButton = document.querySelector("#saveCaseButton");
+const runState = document.querySelector("#runState");
+const stageDetail = document.querySelector("#stageDetail");
+const runLog = document.querySelector("#runLog");
+const traceCounter = document.querySelector("#traceCounter");
+const evidenceDetail = document.querySelector("#evidenceDetail");
+const actualFrequency = document.querySelector("#actualFrequency");
+const frequencyError = document.querySelector("#frequencyError");
+const scopeStatus = document.querySelector("#scopeStatus");
+const benchState = document.querySelector("#benchState");
+const uartOutput = document.querySelector("#uartOutput");
+const uartIndicator = document.querySelector("#uartIndicator");
+const toast = document.querySelector("#toast");
+const scopeCanvas = document.querySelector("#scopeCanvas");
+const scopeContext = scopeCanvas.getContext("2d");
+
+const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
+let runToken = 0;
+let currentState = "idle";
+let selectedEvidence = -1;
+let selectedStage = -1;
+let scopeAnimationFrame = 0;
+let toastTimer = 0;
+
+function delay(milliseconds, token) {
+ const duration = reducedMotion.matches ? Math.min(milliseconds, 120) : milliseconds;
+ return new Promise((resolve) => {
+ window.setTimeout(() => resolve(token === runToken), duration);
+ });
+}
+
+function setRunState(state, label) {
+ currentState = state;
+ app.dataset.runState = state;
+ runState.textContent = label;
+ runButton.disabled = state === "running";
+ failureMode.disabled = state === "running";
+ saveCaseButton.disabled = state !== "passed";
+
+ if (state === "running" && !scopeAnimationFrame) {
+ animateScope();
+ }
+}
+
+function setStageStatus(index, status) {
+ const button = stageButtons[index];
+ if (!button) return;
+ if (status === "idle") {
+ button.removeAttribute("data-status");
+ return;
+ }
+ button.dataset.status = status;
+}
+
+function setEvidenceStatus(index, status, label) {
+ const button = evidenceButtons[index];
+ if (!button) return;
+ if (status === "idle") {
+ button.removeAttribute("data-status");
+ } else {
+ button.dataset.status = status;
+ }
+ if (label) {
+ button.querySelector("span").textContent = label;
+ }
+ if (selectedEvidence === index) {
+ showEvidence(index);
+ }
+}
+
+function selectStage(index) {
+ selectedStage = index;
+ stageButtons.forEach((button, buttonIndex) => {
+ button.classList.toggle("is-selected", buttonIndex === index);
+ });
+
+ const stage = STAGES[index];
+ if (!stage) {
+ stageDetail.innerHTML = `
+ READY
+
等待开始工程闭环
+ 执行过程将绑定代码、构建、下载、串口与物理测量事实。
+ `;
+ return;
+ }
+
+ const status = stageButtons[index].dataset.status || "idle";
+ const statusLabel = {
+ idle: "PENDING",
+ active: "RUNNING",
+ passed: "VERIFIED",
+ failed: "FAILED",
+ }[status];
+
+ stageDetail.innerHTML = `
+ ${String(index + 1).padStart(2, "0")} · ${statusLabel}
+ ${stage.title}
+ ${stage.summary}
+ `;
+}
+
+function showEvidence(index) {
+ selectedEvidence = index;
+ evidenceButtons.forEach((button, buttonIndex) => {
+ button.classList.toggle("is-selected", buttonIndex === index);
+ });
+
+ const status = evidenceButtons[index]?.dataset.status || "idle";
+ const detail = EVIDENCE[index]?.[status] || EVIDENCE[index]?.idle;
+ evidenceDetail.textContent = detail || "暂无证据详情。";
+}
+
+function appendLog(message, tone = "") {
+ const line = document.createElement("span");
+ line.className = tone ? `log-${tone}` : "";
+ line.textContent = `${runLog.textContent.trim() ? "\n" : ""}${message}`;
+ runLog.append(line);
+ runLog.scrollTop = runLog.scrollHeight;
+}
+
+function resetDemo({ keepFailure = true } = {}) {
+ runToken += 1;
+ window.cancelAnimationFrame(scopeAnimationFrame);
+ scopeAnimationFrame = 0;
+ if (!keepFailure) failureMode.checked = false;
+
+ stageButtons.forEach((button) => {
+ button.removeAttribute("data-status");
+ button.classList.remove("is-selected");
+ });
+ evidenceButtons.forEach((button, index) => {
+ button.removeAttribute("data-status");
+ button.classList.remove("is-selected");
+ if (index === 5) button.querySelector("span").textContent = "ioProbe 待测";
+ if (index === 6) button.querySelector("span").textContent = "Aggregate 待定";
+ });
+
+ selectedStage = -1;
+ selectedEvidence = -1;
+ traceCounter.textContent = "0 / 6";
+ runLog.innerHTML = '$ hwlab case run freq-1khz-accuracy\n 尚未执行';
+ evidenceDetail.textContent = "选择证据节点查看边界事实。";
+ actualFrequency.innerHTML = "— Hz";
+ frequencyError.textContent = "—";
+ scopeStatus.textContent = "等待采集";
+ benchState.textContent = "设备待命";
+ uartOutput.textContent = "[idle] waiting for firmware\n[idle] measurement channel ready";
+ uartIndicator.classList.remove("is-active");
+ setRunState("idle", "等待指令");
+ selectStage(-1);
+ drawScope(0);
+}
+
+async function runDemo() {
+ if (currentState === "running") return;
+
+ resetDemo();
+ const token = runToken;
+ const shouldFail = failureMode.checked;
+ setRunState("running", "闭环执行中");
+ benchState.textContent = "HWPOD 已锁定";
+ scopeStatus.textContent = "同步设备";
+ runLog.innerHTML = "";
+ appendLog("$ hwlab case run freq-1khz-accuracy", "muted");
+ setEvidenceStatus(0, "passed");
+ setEvidenceStatus(1, "active");
+
+ for (let index = 0; index < STAGES.length; index += 1) {
+ if (token !== runToken) return;
+
+ setStageStatus(index, "active");
+ selectStage(index);
+ traceCounter.textContent = `${index} / 6`;
+ appendLog(`→ ${STAGES[index].title}`, "active");
+
+ if (index === 3) benchState.textContent = "正在写入固件";
+ if (index === 4) {
+ benchState.textContent = "固件运行中";
+ uartIndicator.classList.add("is-active");
+ uartOutput.textContent = "[boot] 71-FREQ rev.C\n[capture] timer input ready";
+ }
+ if (index === 5) {
+ benchState.textContent = "ioProbe 采集中";
+ scopeStatus.textContent = "采集中";
+ setEvidenceStatus(5, "active", "ioProbe 采集中");
+ }
+
+ const shouldContinue = await delay(index === 5 ? 850 : 650, token);
+ if (!shouldContinue) return;
+
+ if (index === 5 && shouldFail) {
+ setStageStatus(index, "failed");
+ appendLog("probe CH1 frequency · 972.4 Hz · outside tolerance", "fail");
+ traceCounter.textContent = "5 / 6";
+ setEvidenceStatus(5, "failed", "ioProbe 972.4 Hz");
+ setEvidenceStatus(6, "blocked", "Aggregate BLOCKED");
+ actualFrequency.innerHTML = "972.4 Hz";
+ frequencyError.textContent = "−2.76%";
+ scopeStatus.textContent = "阈值超限";
+ benchState.textContent = "物理验证未通过";
+ uartOutput.textContent = "[capture] stable, samples=128\n[result] freq=972.4Hz, tolerance=FAIL";
+ setRunState("failed", "证据阻断");
+ selectStage(index);
+ showEvidence(6);
+ drawScope(0);
+ return;
+ }
+
+ setStageStatus(index, "passed");
+ appendLog(STAGES[index].log, "pass");
+ traceCounter.textContent = `${index + 1} / 6`;
+ selectStage(index);
+
+ if (index === 1) setEvidenceStatus(1, "passed");
+ if (index === 2) setEvidenceStatus(2, "passed");
+ if (index === 3) setEvidenceStatus(3, "passed");
+ if (index === 4) {
+ setEvidenceStatus(4, "passed");
+ uartOutput.textContent = "[capture] stable, samples=128\n[timer] clock=72000000Hz, overflow=0";
+ }
+ }
+
+ setEvidenceStatus(5, "passed", "ioProbe 999.8 Hz");
+ setEvidenceStatus(6, "passed", "Aggregate PASS");
+ actualFrequency.innerHTML = "999.8 Hz";
+ frequencyError.textContent = "−0.02%";
+ scopeStatus.textContent = "测量通过";
+ benchState.textContent = "验证完成 · PASS";
+ uartOutput.textContent = "[capture] stable, samples=128\n[result] freq=999.8Hz, tolerance=PASS";
+ appendLog("verdict Aggregate PASS · evidence sealed", "pass");
+ setRunState("passed", "Aggregate PASS");
+ showEvidence(6);
+ drawScope(0);
+}
+
+function showToast(message) {
+ window.clearTimeout(toastTimer);
+ toast.textContent = message;
+ toast.classList.add("is-visible");
+ toastTimer = window.setTimeout(() => toast.classList.remove("is-visible"), 2600);
+}
+
+function drawScope(phase = 0) {
+ const bounds = scopeCanvas.getBoundingClientRect();
+ const ratio = Math.min(window.devicePixelRatio || 1, 2);
+ const width = Math.max(1, Math.round(bounds.width * ratio));
+ const height = Math.max(1, Math.round(bounds.height * ratio));
+
+ if (scopeCanvas.width !== width || scopeCanvas.height !== height) {
+ scopeCanvas.width = width;
+ scopeCanvas.height = height;
+ }
+
+ scopeContext.setTransform(ratio, 0, 0, ratio, 0, 0);
+ const cssWidth = width / ratio;
+ const cssHeight = height / ratio;
+ scopeContext.clearRect(0, 0, cssWidth, cssHeight);
+ scopeContext.fillStyle = "#0c1418";
+ scopeContext.fillRect(0, 0, cssWidth, cssHeight);
+
+ scopeContext.strokeStyle = "rgba(94, 132, 143, 0.18)";
+ scopeContext.lineWidth = 1;
+ for (let x = 0; x <= cssWidth; x += cssWidth / 10) {
+ scopeContext.beginPath();
+ scopeContext.moveTo(x, 0);
+ scopeContext.lineTo(x, cssHeight);
+ scopeContext.stroke();
+ }
+ for (let y = 0; y <= cssHeight; y += cssHeight / 4) {
+ scopeContext.beginPath();
+ scopeContext.moveTo(0, y);
+ scopeContext.lineTo(cssWidth, y);
+ scopeContext.stroke();
+ }
+
+ const amplitude = cssHeight * 0.27;
+ const center = cssHeight * 0.5;
+ const period = currentState === "failed" ? 51 : 43;
+ const waveColor = currentState === "failed" ? "#ff766e" : "#53dce1";
+ const muted = currentState === "idle";
+
+ scopeContext.beginPath();
+ for (let x = 0; x <= cssWidth; x += 2) {
+ const signal = muted
+ ? Math.sin(x / 25) * 1.2
+ : Math.sin((x + phase) * ((Math.PI * 2) / period)) * amplitude;
+ const noise = currentState === "running" ? Math.sin((x + phase * 1.9) / 5) * 0.8 : 0;
+ const y = center + signal + noise;
+ if (x === 0) scopeContext.moveTo(x, y);
+ else scopeContext.lineTo(x, y);
+ }
+ scopeContext.strokeStyle = muted ? "#496169" : waveColor;
+ scopeContext.lineWidth = 1.4;
+ scopeContext.shadowColor = muted ? "transparent" : waveColor;
+ scopeContext.shadowBlur = muted ? 0 : 5;
+ scopeContext.stroke();
+ scopeContext.shadowBlur = 0;
+}
+
+function animateScope(timestamp = 0) {
+ if (currentState !== "running") {
+ scopeAnimationFrame = 0;
+ return;
+ }
+ drawScope(timestamp * 0.08);
+ scopeAnimationFrame = window.requestAnimationFrame(animateScope);
+}
+
+stageButtons.forEach((button, index) => {
+ button.addEventListener("click", () => selectStage(index));
+});
+
+evidenceButtons.forEach((button, index) => {
+ button.addEventListener("click", () => showEvidence(index));
+});
+
+runButton.addEventListener("click", runDemo);
+resetButton.addEventListener("click", () => resetDemo({ keepFailure: true }));
+failureMode.addEventListener("change", () => {
+ if (currentState !== "idle") resetDemo({ keepFailure: true });
+ showToast(failureMode.checked ? "失败演示已开启:物理测量将阻断聚合结果" : "已恢复通过路径");
+});
+saveCaseButton.addEventListener("click", () => {
+ if (currentState !== "passed") return;
+ showToast("回归 Case 已保存:freq-1khz-accuracy / evidence sealed");
+});
+
+window.addEventListener("resize", () => drawScope(0));
+window.addEventListener("load", () => drawScope(0));
+resetDemo({ keepFailure: false });
diff --git a/project-management/PJ2026-01/showcase/hwlab-first-principles/assets/hwpod-bench.png b/project-management/PJ2026-01/showcase/hwlab-first-principles/assets/hwpod-bench.png
new file mode 100644
index 00000000..bdfe773c
Binary files /dev/null and b/project-management/PJ2026-01/showcase/hwlab-first-principles/assets/hwpod-bench.png differ
diff --git a/project-management/PJ2026-01/showcase/hwlab-first-principles/index.html b/project-management/PJ2026-01/showcase/hwlab-first-principles/index.html
new file mode 100644
index 00000000..5dba0a94
--- /dev/null
+++ b/project-management/PJ2026-01/showcase/hwlab-first-principles/index.html
@@ -0,0 +1,251 @@
+
+
+
+
+
+
+ HWLAB · 真实硬件研发闭环
+
+
+
+
+
+
+ HW
+ HWLAB
+
+ 云端真实硬件研发闭环
+
+
+
+ TARGET
+ 71-FREQ / STM32F103
+
+
+
+ NC01 · v03
+ HWPOD 已占用
+ Session 就绪
+
+
+
+
+
+
+
+
+
+ ENGINEERING AGENT
+
受控工程工作台
+
+
等待指令
+
+
+
+
›_
+
修复 1 kHz 输入下的频率误差,并在真实板上完成验证
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
118static float capture_to_hz(uint32_t ticks)
+
119{
+
120- return TIMER_CLOCK_HZ / ticks;
+
121- // APB prescaler assumed to be 1
+
120+ const uint32_t timer_clock = get_timer_clock_hz();
+
121+ const float calibrated_ticks =
+
122+ (float)ticks - capture_offset_ticks;
+
123+ return timer_clock / calibrated_ticks;
+
124}
+
125
+
126float frequency_read(void)
+
127{
+
128+ capture_offset_ticks = calibration.capture_offset;
+
129 return capture_to_hz(last_capture_ticks);
+
130}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/project-management/PJ2026-01/showcase/hwlab-first-principles/styles.css b/project-management/PJ2026-01/showcase/hwlab-first-principles/styles.css
new file mode 100644
index 00000000..51dc3b82
--- /dev/null
+++ b/project-management/PJ2026-01/showcase/hwlab-first-principles/styles.css
@@ -0,0 +1,1525 @@
+:root {
+ color-scheme: light;
+ --ink: #172126;
+ --ink-soft: #4d5d64;
+ --line: #d7dfe2;
+ --line-strong: #b8c5ca;
+ --paper: #f4f7f7;
+ --surface: #ffffff;
+ --surface-muted: #edf2f3;
+ --cyan: #0798a4;
+ --cyan-dark: #087680;
+ --cyan-pale: #e3f5f5;
+ --green: #16845b;
+ --green-pale: #e8f5ef;
+ --amber: #b36a00;
+ --amber-pale: #fff1d8;
+ --red: #bd3e35;
+ --red-pale: #fae9e7;
+ --night: #10181c;
+ --night-2: #182328;
+ --night-line: #35444a;
+ --mono: "JetBrains Mono", "Cascadia Code", "SFMono-Regular", Consolas, monospace;
+ --sans: Bahnschrift, "DIN Alternate", "Noto Sans CJK SC", "Microsoft YaHei", sans-serif;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body {
+ margin: 0;
+ min-width: 320px;
+ min-height: 100%;
+ background: var(--paper);
+ color: var(--ink);
+ font-family: var(--sans);
+}
+
+body {
+ overflow: hidden;
+}
+
+button,
+input {
+ font: inherit;
+}
+
+button {
+ color: inherit;
+}
+
+button:focus-visible,
+input:focus-visible,
+[tabindex]:focus-visible {
+ outline: 2px solid #0b9da8;
+ outline-offset: 2px;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+.app-shell {
+ display: grid;
+ grid-template-rows: 52px minmax(0, 1fr) 138px;
+ width: 100vw;
+ height: 100dvh;
+ min-height: 560px;
+ overflow: hidden;
+}
+
+.topbar {
+ display: grid;
+ grid-template-columns: minmax(310px, auto) minmax(250px, 1fr) auto;
+ align-items: center;
+ gap: 24px;
+ padding: 0 18px;
+ border-bottom: 1px solid var(--line-strong);
+ background: #f9fbfb;
+}
+
+.brand-block,
+.system-status,
+.target-context,
+.panel-title-row,
+.hwpod-heading,
+.evidence-heading,
+.scope-header,
+.uart-panel header,
+.surface-header {
+ display: flex;
+ align-items: center;
+}
+
+.brand-block {
+ min-width: 0;
+}
+
+.brand-mark {
+ display: grid;
+ place-items: center;
+ width: 30px;
+ height: 30px;
+ border: 1px solid var(--ink);
+ background: var(--ink);
+ color: white;
+ font-family: var(--mono);
+ font-size: 10px;
+ font-weight: 800;
+ letter-spacing: -0.06em;
+}
+
+.brand-name {
+ margin-left: 9px;
+ font-family: var(--mono);
+ font-size: 15px;
+ font-weight: 800;
+ letter-spacing: 0.08em;
+}
+
+.brand-divider {
+ width: 1px;
+ height: 17px;
+ margin: 0 12px;
+ background: var(--line-strong);
+}
+
+.brand-tagline {
+ overflow: hidden;
+ color: var(--ink-soft);
+ font-size: 12px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.target-context {
+ min-width: 0;
+ gap: 10px;
+ justify-self: start;
+ font-size: 12px;
+}
+
+.target-context strong {
+ overflow: hidden;
+ font-family: var(--mono);
+ font-size: 12px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.context-label,
+.eyebrow {
+ color: #728187;
+ font-family: var(--mono);
+ font-size: 9px;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+}
+
+.system-status {
+ gap: 8px;
+ justify-content: flex-end;
+}
+
+.status-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 5px 7px;
+ border: 1px solid var(--line);
+ color: #415157;
+ font-family: var(--mono);
+ font-size: 9px;
+ white-space: nowrap;
+}
+
+.status-dot,
+.state-dot,
+.live-indicator i {
+ display: inline-block;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #8d999e;
+}
+
+.status-pill .status-dot,
+.state-dot--done {
+ background: var(--green);
+}
+
+.status-pill--hardware .status-dot,
+.state-dot--active {
+ background: var(--amber);
+}
+
+.workspace {
+ display: grid;
+ grid-template-columns: 250px minmax(540px, 1fr) minmax(370px, 420px);
+ min-width: 0;
+ min-height: 0;
+ overflow: hidden;
+}
+
+.task-panel,
+.agent-panel,
+.hwpod-panel {
+ min-width: 0;
+ min-height: 0;
+}
+
+.task-panel {
+ display: flex;
+ flex-direction: column;
+ padding: 20px 14px 14px;
+ border-right: 1px solid var(--line);
+ background: #f7f9f9;
+}
+
+.task-panel-heading {
+ position: relative;
+ min-height: 66px;
+ padding: 0 8px 14px;
+ border-bottom: 1px solid var(--line);
+}
+
+.project-copy h1 {
+ margin: 6px 0 0;
+ font-family: var(--mono);
+ font-size: 22px;
+ line-height: 1;
+ letter-spacing: -0.04em;
+}
+
+.project-copy p {
+ margin: 4px 0 0;
+ color: var(--ink-soft);
+ font-size: 12px;
+}
+
+.project-code {
+ display: none;
+}
+
+.task-list {
+ display: grid;
+ gap: 4px;
+ padding: 12px 0;
+}
+
+.task-item {
+ display: flex;
+ width: 100%;
+ min-width: 0;
+ min-height: 58px;
+ gap: 10px;
+ align-items: flex-start;
+ padding: 9px 8px;
+ border: 1px solid transparent;
+ background: transparent;
+ text-align: left;
+ cursor: pointer;
+}
+
+.task-item:hover {
+ border-color: var(--line);
+ background: var(--surface);
+}
+
+.task-item--active {
+ border-color: #a6ced1;
+ background: var(--cyan-pale);
+ box-shadow: inset 3px 0 0 var(--cyan);
+}
+
+.task-marker {
+ display: grid;
+ flex: 0 0 27px;
+ height: 27px;
+ place-items: center;
+ border: 1px solid var(--line-strong);
+ color: #647278;
+ font-family: var(--mono);
+ font-size: 9px;
+}
+
+.task-item--active .task-marker {
+ border-color: var(--cyan);
+ color: var(--cyan-dark);
+}
+
+.task-text {
+ display: grid;
+ min-width: 0;
+ gap: 5px;
+}
+
+.task-text strong {
+ overflow: hidden;
+ font-size: 12px;
+ font-weight: 650;
+ line-height: 1.35;
+ text-overflow: ellipsis;
+}
+
+.task-text small {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ color: #748187;
+ font-size: 10px;
+}
+
+.context-facts {
+ margin-top: auto;
+ padding: 14px 8px 0;
+ border-top: 1px solid var(--line);
+}
+
+.context-facts dl {
+ display: grid;
+ gap: 9px;
+ margin: 11px 0 0;
+}
+
+.context-facts dl div {
+ min-width: 0;
+}
+
+.context-facts dt {
+ color: #7b898e;
+ font-family: var(--mono);
+ font-size: 9px;
+}
+
+.context-facts dd {
+ overflow: hidden;
+ margin: 2px 0 0;
+ font-family: var(--mono);
+ font-size: 10px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.agent-panel {
+ display: grid;
+ grid-template-rows: 50px 62px 58px minmax(0, 1fr);
+ padding: 0 16px 14px;
+ background: var(--paper);
+}
+
+.panel-title-row,
+.hwpod-heading {
+ justify-content: space-between;
+}
+
+.panel-title-row h2,
+.hwpod-heading h2,
+.evidence-heading h2 {
+ margin: 3px 0 0;
+ font-size: 15px;
+ line-height: 1;
+}
+
+.run-state {
+ padding: 4px 7px;
+ border: 1px solid var(--line);
+ color: var(--ink-soft);
+ font-family: var(--mono);
+ font-size: 9px;
+}
+
+.app-shell[data-run-state="running"] .run-state {
+ border-color: #e1b660;
+ background: var(--amber-pale);
+ color: #895000;
+}
+
+.app-shell[data-run-state="passed"] .run-state {
+ border-color: #8bc7ad;
+ background: var(--green-pale);
+ color: #0d704b;
+}
+
+.app-shell[data-run-state="failed"] .run-state {
+ border-color: #dda19c;
+ background: var(--red-pale);
+ color: #9f3029;
+}
+
+.command-bar {
+ display: grid;
+ grid-template-columns: auto minmax(180px, 1fr) auto 34px auto;
+ align-items: center;
+ gap: 10px;
+ padding: 8px 9px;
+ border: 1px solid var(--line-strong);
+ background: var(--surface);
+ box-shadow: 0 2px 8px rgb(31 53 61 / 6%);
+}
+
+.command-symbol {
+ display: grid;
+ width: 33px;
+ height: 33px;
+ place-items: center;
+ background: var(--ink);
+ color: #86edf0;
+ font-family: var(--mono);
+ font-size: 13px;
+ font-weight: 700;
+}
+
+.command-bar p {
+ overflow: hidden;
+ margin: 0;
+ font-size: 12px;
+ font-weight: 650;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.failure-toggle {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ color: #66757a;
+ font-size: 10px;
+ cursor: pointer;
+ white-space: nowrap;
+}
+
+.failure-toggle input {
+ position: absolute;
+ opacity: 0;
+ pointer-events: none;
+}
+
+.toggle-track {
+ display: inline-flex;
+ width: 26px;
+ height: 14px;
+ align-items: center;
+ padding: 2px;
+ border: 1px solid var(--line-strong);
+ border-radius: 8px;
+ background: #e8edef;
+ transition: background 140ms ease, border-color 140ms ease;
+}
+
+.toggle-track i {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #7c8a8f;
+ transition: transform 140ms ease, background 140ms ease;
+}
+
+.failure-toggle input:checked + .toggle-track {
+ border-color: var(--red);
+ background: var(--red-pale);
+}
+
+.failure-toggle input:checked + .toggle-track i {
+ transform: translateX(12px);
+ background: var(--red);
+}
+
+.icon-button,
+.run-button,
+#saveCaseButton {
+ height: 34px;
+ border: 1px solid var(--line-strong);
+ background: var(--surface);
+ cursor: pointer;
+}
+
+.icon-button {
+ width: 34px;
+ color: #536369;
+ font-size: 17px;
+}
+
+.run-button {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ padding: 0 14px;
+ border-color: #087b84;
+ background: var(--cyan-dark);
+ color: white;
+ font-size: 11px;
+ font-weight: 700;
+}
+
+.run-button:hover:not(:disabled) {
+ background: #05636b;
+}
+
+.run-button:disabled,
+#saveCaseButton:disabled {
+ cursor: not-allowed;
+ opacity: 0.48;
+}
+
+.stage-strip {
+ display: grid;
+ grid-template-columns: repeat(6, minmax(0, 1fr));
+ align-items: end;
+ gap: 4px;
+ margin: 0;
+ padding: 8px 0 7px;
+ list-style: none;
+}
+
+.stage-strip button {
+ position: relative;
+ display: grid;
+ width: 100%;
+ height: 42px;
+ grid-template-columns: auto minmax(0, 1fr);
+ align-items: center;
+ gap: 6px;
+ padding: 0 7px;
+ overflow: hidden;
+ border: 1px solid var(--line);
+ background: #f9fbfb;
+ cursor: pointer;
+ text-align: left;
+}
+
+.stage-strip button:hover,
+.stage-strip button.is-selected {
+ border-color: #8fbcc0;
+ background: var(--cyan-pale);
+}
+
+.stage-strip button span {
+ color: #8a979b;
+ font-family: var(--mono);
+ font-size: 8px;
+}
+
+.stage-strip button strong {
+ overflow: hidden;
+ font-size: 10px;
+ font-weight: 650;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.stage-strip button i {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ height: 2px;
+ background: #cbd4d7;
+}
+
+.stage-strip button[data-status="active"] i {
+ background: var(--amber);
+ animation: stage-pulse 850ms ease-in-out infinite alternate;
+}
+
+.stage-strip button[data-status="passed"] i {
+ background: var(--green);
+}
+
+.stage-strip button[data-status="failed"] i {
+ background: var(--red);
+}
+
+@keyframes stage-pulse {
+ from { opacity: 0.35; }
+ to { opacity: 1; }
+}
+
+.work-surface {
+ display: grid;
+ grid-template-columns: minmax(0, 1.5fr) minmax(230px, 0.72fr);
+ min-height: 0;
+ overflow: hidden;
+ border: 1px solid var(--line-strong);
+ background: var(--surface);
+}
+
+.editor-pane,
+.trace-pane {
+ display: grid;
+ min-width: 0;
+ min-height: 0;
+ grid-template-rows: 36px minmax(0, 1fr);
+}
+
+.editor-pane {
+ border-right: 1px solid var(--line);
+}
+
+.trace-pane {
+ grid-template-rows: 36px auto minmax(0, 1fr);
+ background: #fafcfc;
+}
+
+.surface-header {
+ min-width: 0;
+ justify-content: space-between;
+ border-bottom: 1px solid var(--line);
+ background: #f2f5f5;
+}
+
+.surface-header > strong {
+ padding-left: 10px;
+ font-size: 10px;
+}
+
+.surface-header > span {
+ padding-right: 10px;
+ color: #718086;
+ font-family: var(--mono);
+ font-size: 9px;
+}
+
+.file-tabs {
+ display: flex;
+ height: 100%;
+ min-width: 0;
+}
+
+.file-tab {
+ position: relative;
+ min-width: 0;
+ padding: 0 12px;
+ overflow: hidden;
+ border: 0;
+ border-right: 1px solid var(--line);
+ background: transparent;
+ color: #68777d;
+ font-family: var(--mono);
+ font-size: 9px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ cursor: pointer;
+}
+
+.file-tab--active {
+ background: white;
+ color: var(--ink);
+}
+
+.file-tab i {
+ position: absolute;
+ top: 8px;
+ right: 7px;
+ width: 5px;
+ height: 5px;
+ border-radius: 50%;
+ background: var(--amber);
+}
+
+.change-summary b,
+.change-summary em {
+ font-style: normal;
+ font-weight: 700;
+}
+
+.change-summary b { color: var(--green); }
+.change-summary em { color: var(--red); }
+
+.code-view {
+ min-width: 0;
+ overflow: auto;
+ padding: 8px 0 20px;
+ background: #fff;
+ font-family: var(--mono);
+ font-size: 10px;
+ line-height: 1.7;
+}
+
+.code-line {
+ display: grid;
+ min-width: max-content;
+ grid-template-columns: 43px auto;
+ padding-right: 14px;
+}
+
+.code-line > span {
+ padding-right: 10px;
+ border-right: 1px solid #edf0f1;
+ color: #a0aaae;
+ text-align: right;
+ user-select: none;
+}
+
+.code-line code {
+ padding-left: 11px;
+ white-space: pre;
+}
+
+.code-line--removed {
+ background: var(--red-pale);
+ color: #8f312b;
+}
+
+.code-line--added {
+ background: var(--green-pale);
+ color: #116844;
+}
+
+.code-line--context {
+ color: #526167;
+}
+
+.stage-detail {
+ padding: 12px 11px 10px;
+ border-bottom: 1px solid var(--line);
+}
+
+.detail-index {
+ color: var(--cyan-dark);
+ font-family: var(--mono);
+ font-size: 8px;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+}
+
+.stage-detail h3 {
+ margin: 5px 0;
+ font-size: 12px;
+ line-height: 1.25;
+}
+
+.stage-detail p {
+ margin: 0;
+ color: #68777c;
+ font-size: 10px;
+ line-height: 1.5;
+}
+
+.run-log {
+ margin: 0;
+ overflow: auto;
+ padding: 10px 11px 16px;
+ background: #11191d;
+ color: #d4e2e5;
+ font-family: var(--mono);
+ font-size: 9px;
+ line-height: 1.65;
+ white-space: pre-wrap;
+}
+
+.log-muted { color: #72858d; }
+.log-active { color: #ffc86e; }
+.log-pass { color: #6ce0ad; }
+.log-fail { color: #ff8c84; }
+
+.hwpod-panel {
+ display: grid;
+ grid-template-rows: 50px minmax(180px, 1fr) auto auto;
+ padding: 0 14px 12px;
+ overflow: hidden;
+ border-left: 1px solid #27343a;
+ background: var(--night);
+ color: #eef5f6;
+}
+
+.hwpod-heading .eyebrow {
+ color: #6f858d;
+}
+
+.hwpod-heading h2 {
+ color: #f3f7f7;
+}
+
+.live-indicator {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ color: #6ed2ae;
+ font-family: var(--mono);
+ font-size: 9px;
+}
+
+.live-indicator i {
+ background: #40c58e;
+ box-shadow: 0 0 0 3px rgb(64 197 142 / 12%);
+}
+
+.bench-view {
+ position: relative;
+ min-height: 0;
+ margin: 0;
+ overflow: hidden;
+ border: 1px solid var(--night-line);
+ background: #111a1f;
+}
+
+.bench-view::after {
+ position: absolute;
+ inset: 0;
+ border: 1px solid rgb(255 255 255 / 4%);
+ content: "";
+ pointer-events: none;
+}
+
+.bench-view img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ object-position: center;
+ filter: saturate(0.88) contrast(1.04) brightness(0.78);
+}
+
+.hardware-label,
+.bench-state {
+ position: absolute;
+ z-index: 1;
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 4px 6px;
+ border: 1px solid rgb(153 210 215 / 40%);
+ background: rgb(11 20 24 / 78%);
+ color: #d3e9eb;
+ font-family: var(--mono);
+ font-size: 8px;
+ backdrop-filter: blur(3px);
+}
+
+.hardware-label i {
+ width: 4px;
+ height: 4px;
+ border-radius: 50%;
+ background: #5bd6d9;
+}
+
+.hardware-label--board { top: 16%; left: 4%; }
+.hardware-label--probe { top: 40%; left: 43%; }
+.hardware-label--ioprobe { right: 4%; bottom: 18%; }
+
+.bench-state {
+ right: 8px;
+ bottom: 8px;
+ border-color: rgb(255 198 103 / 48%);
+ color: #ffd089;
+}
+
+.app-shell[data-run-state="passed"] .bench-state {
+ border-color: rgb(91 214 163 / 50%);
+ color: #74dfb3;
+}
+
+.app-shell[data-run-state="failed"] .bench-state {
+ border-color: rgb(255 122 112 / 54%);
+ color: #ff8c84;
+}
+
+.measurement-panel,
+.uart-panel {
+ border-right: 1px solid var(--night-line);
+ border-bottom: 1px solid var(--night-line);
+ border-left: 1px solid var(--night-line);
+ background: var(--night-2);
+}
+
+.measurement-panel {
+ padding: 9px 10px 8px;
+}
+
+.scope-header {
+ justify-content: space-between;
+ font-family: var(--mono);
+ font-size: 8px;
+}
+
+.scope-header span { color: #7d929a; }
+.scope-header strong { color: #85e3e6; font-weight: 600; }
+
+#scopeCanvas {
+ display: block;
+ width: 100%;
+ height: 65px;
+ margin: 7px 0 8px;
+ border: 1px solid #304148;
+ background: #0c1418;
+}
+
+.measurement-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 8px;
+ margin: 0;
+}
+
+.measurement-grid div {
+ min-width: 0;
+ padding-left: 8px;
+ border-left: 1px solid #34454c;
+}
+
+.measurement-grid div:first-child {
+ padding-left: 0;
+ border-left: 0;
+}
+
+.measurement-grid dt {
+ color: #71878f;
+ font-size: 8px;
+}
+
+.measurement-grid dd {
+ margin: 3px 0 0;
+ color: #eef6f7;
+ font-family: var(--mono);
+ font-size: 14px;
+ font-weight: 700;
+ white-space: nowrap;
+}
+
+.measurement-grid dd small {
+ color: #748a92;
+ font-size: 8px;
+ font-weight: 400;
+}
+
+.app-shell[data-run-state="passed"] #actualFrequency,
+.app-shell[data-run-state="passed"] #frequencyError {
+ color: #62dda8;
+}
+
+.app-shell[data-run-state="failed"] #actualFrequency,
+.app-shell[data-run-state="failed"] #frequencyError {
+ color: #ff8178;
+}
+
+.uart-panel {
+ min-height: 69px;
+ padding: 8px 10px;
+}
+
+.uart-panel header {
+ justify-content: space-between;
+ color: #72868e;
+ font-family: var(--mono);
+ font-size: 8px;
+}
+
+.uart-panel header i {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #596970;
+}
+
+.uart-panel header i.is-active {
+ background: #58d2a1;
+ box-shadow: 0 0 0 3px rgb(88 210 161 / 10%);
+}
+
+.uart-panel pre {
+ margin: 6px 0 0;
+ overflow: hidden;
+ color: #a9b9be;
+ font-family: var(--mono);
+ font-size: 8px;
+ line-height: 1.45;
+ white-space: pre-wrap;
+}
+
+.evidence-band {
+ display: grid;
+ grid-template-rows: 48px minmax(0, 1fr);
+ min-width: 0;
+ padding: 0 18px 10px;
+ overflow: hidden;
+ border-top: 1px solid var(--line-strong);
+ background: #f8fafa;
+}
+
+.evidence-heading {
+ min-width: 0;
+ gap: 18px;
+}
+
+.evidence-heading > div {
+ flex: 0 0 180px;
+}
+
+.evidence-heading p {
+ min-width: 0;
+ flex: 1 1 auto;
+ overflow: hidden;
+ margin: 0;
+ color: #67767b;
+ font-size: 10px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+#saveCaseButton {
+ flex: 0 0 auto;
+ height: 30px;
+ padding: 0 12px;
+ border-color: var(--cyan-dark);
+ color: var(--cyan-dark);
+ font-size: 10px;
+ font-weight: 700;
+}
+
+#saveCaseButton:not(:disabled):hover {
+ background: var(--cyan-dark);
+ color: white;
+}
+
+.evidence-chain {
+ display: grid;
+ grid-template-columns: repeat(7, minmax(90px, 1fr));
+ min-width: 0;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.evidence-chain li {
+ position: relative;
+ min-width: 0;
+}
+
+.evidence-chain li:not(:last-child)::after {
+ position: absolute;
+ z-index: 0;
+ top: 16px;
+ right: -50%;
+ width: 100%;
+ height: 1px;
+ background: #c7d1d4;
+ content: "";
+}
+
+.evidence-chain button {
+ position: relative;
+ z-index: 1;
+ display: grid;
+ width: 100%;
+ min-width: 0;
+ grid-template-columns: 12px minmax(0, auto);
+ grid-template-rows: 17px 14px;
+ align-items: center;
+ justify-content: center;
+ column-gap: 7px;
+ padding: 4px 5px;
+ border: 1px solid transparent;
+ background: transparent;
+ cursor: pointer;
+}
+
+.evidence-chain button:hover,
+.evidence-chain button.is-selected {
+ border-color: var(--line);
+ background: white;
+}
+
+.evidence-chain button i {
+ grid-row: 1 / 3;
+ width: 10px;
+ height: 10px;
+ border: 2px solid #aab6ba;
+ border-radius: 50%;
+ background: #f8fafa;
+}
+
+.evidence-chain button span,
+.evidence-chain button small {
+ min-width: 0;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.evidence-chain button span {
+ font-size: 10px;
+ font-weight: 700;
+}
+
+.evidence-chain button small {
+ color: #879399;
+ font-family: var(--mono);
+ font-size: 8px;
+}
+
+.evidence-chain button[data-status="active"] i {
+ border-color: var(--amber);
+ background: var(--amber-pale);
+ box-shadow: 0 0 0 3px rgb(179 106 0 / 10%);
+}
+
+.evidence-chain button[data-status="passed"] i {
+ border-color: var(--green);
+ background: var(--green);
+ box-shadow: inset 0 0 0 2px white;
+}
+
+.evidence-chain button[data-status="failed"] i,
+.evidence-chain button[data-status="blocked"] i {
+ border-color: var(--red);
+ background: var(--red);
+ box-shadow: inset 0 0 0 2px white;
+}
+
+.evidence-final button {
+ border-left: 1px solid var(--line-strong);
+}
+
+.toast {
+ position: fixed;
+ z-index: 20;
+ right: 18px;
+ bottom: 154px;
+ max-width: min(360px, calc(100vw - 36px));
+ padding: 10px 12px;
+ transform: translateY(8px);
+ border: 1px solid #65b894;
+ background: #10231c;
+ color: #dff8ed;
+ font-size: 11px;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 160ms ease, transform 160ms ease;
+}
+
+.toast.is-visible {
+ transform: translateY(0);
+ opacity: 1;
+}
+
+@media (max-width: 1180px) and (min-width: 721px) {
+ .topbar {
+ grid-template-columns: auto minmax(180px, 1fr) auto;
+ gap: 12px;
+ padding: 0 10px;
+ }
+
+ .brand-divider,
+ .brand-tagline,
+ .system-status .status-pill:first-child {
+ display: none;
+ }
+
+ .workspace {
+ grid-template-columns: 72px minmax(0, 1fr) 320px;
+ }
+
+ .task-panel {
+ align-items: center;
+ padding: 14px 7px 10px;
+ }
+
+ .task-panel-heading {
+ display: grid;
+ width: 100%;
+ min-height: 48px;
+ place-items: center;
+ padding: 0 0 10px;
+ }
+
+ .task-panel-heading .eyebrow,
+ .project-copy,
+ .task-text,
+ .context-facts {
+ display: none;
+ }
+
+ .project-code {
+ display: grid;
+ width: 34px;
+ height: 34px;
+ place-items: center;
+ border: 1px solid var(--ink);
+ font-family: var(--mono);
+ font-size: 11px;
+ font-weight: 700;
+ }
+
+ .task-list {
+ width: 100%;
+ }
+
+ .task-item {
+ min-height: 46px;
+ justify-content: center;
+ padding: 8px 5px;
+ }
+
+ .task-marker {
+ flex-basis: 29px;
+ height: 29px;
+ }
+
+ .agent-panel {
+ padding-right: 10px;
+ padding-left: 10px;
+ }
+
+ .failure-toggle > span:last-child {
+ display: none;
+ }
+
+ .command-bar {
+ grid-template-columns: auto minmax(100px, 1fr) auto 32px auto;
+ gap: 6px;
+ padding-right: 6px;
+ padding-left: 6px;
+ }
+
+ .command-symbol,
+ .icon-button {
+ width: 32px;
+ }
+
+ .run-button {
+ padding: 0 9px;
+ }
+
+ .work-surface {
+ grid-template-columns: minmax(0, 1.35fr) minmax(190px, 0.65fr);
+ }
+
+ .hwpod-panel {
+ padding-right: 9px;
+ padding-left: 9px;
+ }
+
+ .hardware-label--probe {
+ left: 36%;
+ }
+
+ .measurement-grid {
+ gap: 4px;
+ }
+
+ .measurement-grid dd {
+ font-size: 12px;
+ }
+
+ .evidence-band {
+ padding-right: 10px;
+ padding-left: 10px;
+ }
+
+ .evidence-heading > div {
+ flex-basis: 155px;
+ }
+}
+
+@media (max-width: 900px) and (min-width: 721px) {
+ .system-status .status-pill:nth-child(2),
+ .target-context .context-label,
+ .file-tabs .file-tab:nth-child(2) {
+ display: none;
+ }
+
+ .trace-pane {
+ grid-template-rows: 36px auto minmax(0, 1fr);
+ }
+
+ .stage-detail p {
+ display: none;
+ }
+}
+
+@media (max-width: 720px) {
+ body {
+ overflow: auto;
+ }
+
+ .app-shell {
+ display: block;
+ width: 100%;
+ height: auto;
+ min-height: 100dvh;
+ overflow: visible;
+ }
+
+ .topbar {
+ position: sticky;
+ z-index: 10;
+ top: 0;
+ display: flex;
+ min-height: 52px;
+ justify-content: space-between;
+ gap: 8px;
+ padding: 0 10px;
+ }
+
+ .brand-divider,
+ .brand-tagline,
+ .target-context,
+ .system-status .status-pill:not(:last-child) {
+ display: none;
+ }
+
+ .workspace {
+ display: flex;
+ flex-direction: column;
+ overflow: visible;
+ }
+
+ .task-panel {
+ display: grid;
+ grid-template-columns: 122px minmax(0, 1fr);
+ gap: 10px;
+ padding: 14px 10px 10px;
+ border-right: 0;
+ border-bottom: 1px solid var(--line);
+ }
+
+ .task-panel-heading {
+ min-height: 0;
+ padding: 0 8px;
+ border-bottom: 0;
+ border-right: 1px solid var(--line);
+ }
+
+ .project-copy h1 {
+ font-size: 18px;
+ }
+
+ .task-list {
+ padding: 0;
+ }
+
+ .task-item {
+ display: none;
+ min-height: 50px;
+ }
+
+ .task-item--active {
+ display: flex;
+ }
+
+ .context-facts {
+ display: none;
+ }
+
+ .agent-panel {
+ display: block;
+ padding: 0 10px 12px;
+ }
+
+ .panel-title-row {
+ min-height: 50px;
+ }
+
+ .command-bar {
+ grid-template-columns: auto minmax(0, 1fr) 34px;
+ gap: 7px;
+ padding: 7px;
+ }
+
+ .command-bar p {
+ white-space: normal;
+ line-height: 1.35;
+ }
+
+ .failure-toggle {
+ grid-column: 1 / 3;
+ grid-row: 2;
+ padding-left: 2px;
+ }
+
+ .icon-button {
+ grid-column: 3;
+ grid-row: 2;
+ }
+
+ .run-button {
+ grid-column: 3;
+ grid-row: 1;
+ width: 34px;
+ justify-content: center;
+ padding: 0;
+ font-size: 0;
+ }
+
+ .run-button span {
+ font-size: 11px;
+ }
+
+ .stage-strip {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 5px;
+ }
+
+ .work-surface {
+ display: block;
+ overflow: visible;
+ }
+
+ .editor-pane,
+ .trace-pane {
+ min-height: 260px;
+ }
+
+ .editor-pane {
+ border-right: 0;
+ border-bottom: 1px solid var(--line);
+ }
+
+ .trace-pane {
+ min-height: 240px;
+ }
+
+ .hwpod-panel {
+ display: block;
+ padding: 0 10px 12px;
+ border-left: 0;
+ }
+
+ .hwpod-heading {
+ min-height: 52px;
+ }
+
+ .bench-view {
+ height: 250px;
+ }
+
+ #scopeCanvas {
+ height: 90px;
+ }
+
+ .uart-panel {
+ min-height: 76px;
+ }
+
+ .evidence-band {
+ display: block;
+ padding: 12px 10px 18px;
+ overflow: visible;
+ }
+
+ .evidence-heading {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ gap: 8px 12px;
+ }
+
+ .evidence-heading > div {
+ flex-basis: auto;
+ }
+
+ .evidence-heading p {
+ grid-column: 1 / 3;
+ grid-row: 2;
+ white-space: normal;
+ }
+
+ #saveCaseButton {
+ grid-column: 2;
+ grid-row: 1;
+ }
+
+ .evidence-chain {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 0;
+ margin-top: 10px;
+ }
+
+ .evidence-chain li:not(:last-child)::after {
+ top: 32px;
+ right: auto;
+ bottom: -8px;
+ left: 18px;
+ width: 1px;
+ height: 18px;
+ }
+
+ .evidence-chain button {
+ min-height: 48px;
+ grid-template-columns: 18px 1fr auto;
+ grid-template-rows: 1fr;
+ justify-content: stretch;
+ padding: 7px 12px;
+ text-align: left;
+ }
+
+ .evidence-chain button i {
+ grid-row: auto;
+ }
+
+ .evidence-chain button small {
+ text-align: right;
+ }
+
+ .evidence-final button {
+ border-top: 1px solid var(--line-strong);
+ border-left: 0;
+ }
+
+ .toast {
+ right: 10px;
+ bottom: 10px;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ scroll-behavior: auto !important;
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}