From 1a7311f30dd92657896b4917153a1b93ffa8e4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=EC=96=B8?= Date: Wed, 28 Oct 2026 10:15:04 +0900 Subject: [PATCH 1/6] feat: improve workbench layout and panel controls (#9) --- web/index.html | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/web/index.html b/web/index.html index a775d24..43a59ce 100644 --- a/web/index.html +++ b/web/index.html @@ -12,12 +12,13 @@ -
HWACHEONSTORMWATER NETWORK

화천군 맨홀 및 우수관로 3D 뷰어

입력 제원 기반 관망 탐색
LOCAL ONLY
+
HWACHEONSTORMWATER NETWORK

화천군 관망도

SWMM 입력 제원 · 3D 검토
입력 자료 기반
+
-
-- 2.54.0 From 84e420949c06ce2b36bf3dc1e2a8eb594450e926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=EC=96=B8?= Date: Wed, 28 Oct 2026 10:15:05 +0900 Subject: [PATCH 2/6] feat: improve workbench layout and panel controls (#9) --- web/app.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/web/app.js b/web/app.js index b8174e3..b9aaf0e 100644 --- a/web/app.js +++ b/web/app.js @@ -33,6 +33,7 @@ function drawProfile(f){ c.fillStyle='#587455';c.textAlign='left';c.fillText(`관저 ${fmt(z1,3)}`,13,142);c.textAlign='right';c.fillText(`${fmt(z2,3)} m`,227,142); } export function selectFacility(f){ + if(f&&window.innerWidth<=1000)setPanel('inspector',true); if(!f)return;app.selected=f;app.renderer.set({selected:f.pickId});$('#view-name').textContent=f.id; let html=`${labels[f.type]}

${esc(f.id)}

`; const reviewReasons=app.network.issues.filter(i=>['length_mismatch','adverse_slope','missing_coordinate','unplaced_link','coincident_endpoints'].includes(i.code)&&i.ids.includes(f.id)); @@ -87,6 +88,29 @@ function updateScales(){ const height=Number($('#height-scale').value),size=Number($('#size-scale').value);app.renderer.set({height,size,section:$('#section-mode').checked}); $('#height-output').value=`×${height}`;$('#size-output').value=`×${size}`;$('#height-badge').textContent=`높이 ×${height}`;$('#size-badge').textContent=`단면·맨홀 폭 ×${size}`;$('#section-badge').hidden=!$('#section-mode').checked; } +function setPanel(panel,open){ + const narrow=window.innerWidth<=1000; + if(narrow&&open)document.body.classList.add(panel==='controls'?'hide-inspector':'hide-controls'); + document.body.classList.toggle(`hide-${panel}`,!open); + for(const name of ['controls','inspector']){ + const visible=!document.body.classList.contains(`hide-${name}`); + $(`#toggle-${name}`).setAttribute('aria-expanded',String(visible)); + $(`#${name}-panel`).inert=!visible; + } + $('#panel-backdrop').hidden=!narrow||['controls','inspector'].every(n=>document.body.classList.contains(`hide-${n}`)); +} +function initPanels(){ + const narrow=matchMedia('(max-width: 1000px)'); + const layout=()=>{setPanel('controls',!narrow.matches);setPanel('inspector',!narrow.matches);}; + layout();narrow.addEventListener('change',layout); + for(const name of ['controls','inspector'])$(`#toggle-${name}`).onclick=()=>setPanel(name,document.body.classList.contains(`hide-${name}`)); + $('#panel-backdrop').onclick=()=>{setPanel('controls',false);setPanel('inspector',false);}; + document.addEventListener('keydown',e=>{if(e.key==='Escape'&&narrow.matches&&!$('dialog[open]')){ + const wasControls=!document.body.classList.contains('hide-controls'); + setPanel('controls',false);setPanel('inspector',false); + $(`#toggle-${wasControls?'controls':'inspector'}`).focus(); + }}); +} function syncAuditHighlight(){ const enabled=$('#audit-highlight').checked,reduced=matchMedia('(prefers-reduced-motion: reduce)').matches; const steady=$('#audit-steady').checked||reduced; @@ -114,6 +138,7 @@ function updateAudit(){ } function fatal(message){$('#loading').hidden=true;$('#fatal-error').hidden=false;$('#fatal-error').textContent=message;$('#render-status').textContent='실행 확인 필요';} async function main(){ + initPanels(); try{ const response=await fetch('data/network.json',{cache:'no-store'});if(!response.ok)throw Error('관망 데이터를 읽을 수 없습니다. web/data/network.json 파일이 포함되어 있는지 확인해 주세요.'); app.network=await response.json();const n=app.network,s=n.summary; -- 2.54.0 From fcb362e4ce95e2a80fee937ca06b9b07c4b0ed5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=EC=96=B8?= Date: Wed, 28 Oct 2026 10:15:05 +0900 Subject: [PATCH 3/6] feat: improve workbench layout and panel controls (#9) --- web/viewer-readability.css | 109 +++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/web/viewer-readability.css b/web/viewer-readability.css index 40ee845..29632df 100644 --- a/web/viewer-readability.css +++ b/web/viewer-readability.css @@ -58,3 +58,112 @@ button, input, select { font-family: inherit; } .network-page .legend-panel small { display: none; } .network-page .legend-panel > div { margin-top: 6px; } } + +/* Workbench layout: keep the model in view; panels scroll independently. */ +.network-page{--header-height:72px;--footer-height:32px;height:100dvh;overflow:hidden;background:#eef3f1;color:#203b35} +.network-page .topbar{height:var(--header-height);min-height:0;display:flex;padding:0 22px;gap:22px;position:relative;z-index:30;box-shadow:0 1px 0 #d9e4df} +.network-page .brand{min-width:0;font-size:13px;letter-spacing:1.3px;gap:10px} +.network-page .brand-icon{width:36px;height:36px;font-size:26px;border-radius:10px} +.network-page .brand small{font-size:8px;letter-spacing:1px;margin-top:2px} +.network-page .title-block{padding-left:22px;border-left:1px solid #dce6e0;gap:2px} +.network-page .topbar .title-block h1{font-size:18px;font-weight:700;letter-spacing:-.6px} +.network-page .title-block>span{font-size:11px;display:block} +.network-page .local-badge{font-size:10px;letter-spacing:0;padding:5px 9px} +.panel-actions{display:flex;gap:6px;margin-left:0} +.panel-actions button{border:1px solid #d9e3dd;background:#fff;border-radius:8px;padding:9px 13px;min-height:38px;font-size:12px;white-space:nowrap;font-weight:600} +.panel-actions button[aria-expanded=true]{background:#e9f4ee;color:#216a55;border-color:#bcd9ca} +.network-page .app-grid{height:calc(100dvh - var(--header-height) - var(--footer-height));min-height:0;grid-template-columns:280px minmax(0,1fr) 320px;gap:0;display:grid} +.network-page.hide-controls .app-grid{grid-template-columns:minmax(0,1fr) 320px} +.network-page.hide-inspector .app-grid{grid-template-columns:280px minmax(0,1fr)} +.network-page.hide-controls.hide-inspector .app-grid{grid-template-columns:minmax(0,1fr)} +.network-page.hide-controls .controls,.network-page.hide-inspector .inspector{display:none} +.network-page .controls,.network-page .inspector{min-height:0;min-width:0;max-height:none;overflow:auto;overscroll-behavior:contain;padding:22px 20px;background:#fbfdfb;grid-column:auto;grid-row:auto;scrollbar-gutter:stable} +.network-page .controls>.section-kicker{font-size:9px;letter-spacing:1.6px;color:#7d9387} +.network-page .controls h2{font-size:21px;line-height:1.3;margin:12px 0 7px;font-weight:700;letter-spacing:-.7px} +.network-page .controls>.subtle{font-size:12px;line-height:1.6;margin:0 0 16px} +.network-page .summary-cards{border-radius:10px;margin:0 0 16px;background:#f0f6f2} +.network-page .summary-cards div{padding:11px 14px} +.network-page .summary-cards strong{font-size:25px;line-height:1.2;font-weight:650} +.network-page .summary-cards span{font-size:11px;margin-bottom:4px} +.network-page .controls section{padding:16px 0} +.network-page .controls h3{font-size:13px} +.network-page .section-heading{margin-bottom:10px} +.network-page .search-wrap{background:#fff;min-height:42px;border-radius:9px} +.network-page .search-wrap input{font-size:13px} +.network-page .layer-list{display:flex;gap:3px} +.network-page .layer-list label{min-height:36px;padding:4px 0;font-size:13px} +.network-page .layer-list small{font-size:11px;color:#607d70} +.network-page input[type=checkbox]{width:17px;height:17px;flex-shrink:0} +.network-page .text-button{font-size:12px;min-height:28px} +.network-page .toggle-label{margin-top:0;font-size:13px} +.network-page .toggle-label small{font-size:11px;line-height:1.5} +.network-page .audit-highlight-controls{position:relative} +.network-page .audit-highlight-controls .audit-steady-label{font-size:12px;min-height:32px;margin:6px 0} +.network-page .audit-highlight-controls .fine-print{font-size:11px;line-height:1.65;margin:7px 0} +.network-page .range-heading{font-size:12px;margin-top:12px} +.network-page .range-heading output{font-size:11px} +.network-page .fine-print{font-size:11px;line-height:1.7} +.network-page .source-label{font-size:10px;line-height:1.7} +.network-page .viewer{min-height:0;height:auto;max-height:none;order:0;grid-column:auto;grid-row:auto;border:0} +.network-page .viewport-heading{top:20px;left:24px;right:20px} +.network-page .viewport-heading .section-kicker{font-size:9px;letter-spacing:1.6px} +.network-page .viewport-heading h2{font-size:25px;margin:5px 0;max-width:100%;overflow-wrap:anywhere;line-height:1.2} +.network-page .viewport-heading p{font-size:11px} +.network-page .view-badge{font-size:10px;padding:5px 9px} +.network-page .scale-badges{top:107px;left:24px;gap:5px} +.network-page .scale-badges span{font-size:10px;padding:4px 6px} +.network-page .coordinate-note{top:141px;left:24px;font-size:10px} +.network-page .legend-panel{left:20px;bottom:117px;padding:11px 13px;border-radius:10px;background:#15262be8;max-width:210px} +.network-page .legend-title{font-size:10px;margin-bottom:7px} +.network-page .legend-panel>div{font-size:11px;margin:6px 0} +.network-page .legend-panel small{font-size:10px;display:none} +.network-page .axes{bottom:105px;right:10px} +.network-page .datum-note{bottom:97px;font-size:9px} +.network-page .view-toolbar{bottom:43px;border-radius:12px;gap:2px;padding:5px;max-width:calc(100% - 24px)} +.network-page .view-toolbar button{min-height:36px;padding:8px 9px;font-size:12px} +.network-page .viewer-footer{bottom:12px;left:20px;right:20px} +.network-page .viewer-footer>span{font-size:10px;max-width:none} +.network-page .viewer-footer .text-button{font-size:11px;min-height:20px} +.network-page .inspector{border-top:0;background:#fff;padding:22px} +.network-page .inspector>.section-kicker{margin-bottom:18px;font-size:12px;color:#416b5c} +.network-page .inspector>.section-kicker span{font-size:9px;letter-spacing:1.2px} +.network-page .inspector h2{font-size:23px;line-height:1.35;margin:10px 0 15px;font-weight:650} +.network-page .overview-icon{width:42px;height:42px;border-radius:12px;font-size:22px;margin-bottom:16px} +.network-page .inspector-intro{font-size:13px;line-height:1.8} +.network-page .overview-list{gap:10px;margin-top:20px} +.network-page .overview-list>div{font-size:13px} +.network-page .detail-section{padding-top:16px;margin-top:16px} +.network-page .detail-section h3{font-size:13px;margin-bottom:10px} +.network-page .inspector .properties>div{font-size:12px;padding:7px 0;gap:12px;border-bottom:1px solid #edf2ef} +.network-page .properties dt{flex-basis:42%;overflow-wrap:anywhere} +.network-page .properties dd{font-weight:550;overflow-wrap:anywhere} +.network-page .notice{font-size:11px;padding:10px 12px;line-height:1.8} +.network-page .connection-link{font-size:12px;padding:10px;min-width:0} +.network-page .connection-link span{min-width:0;overflow-wrap:anywhere} +.network-page .connection-link small{font-size:10px} +.network-page .focus-button{font-size:12px;min-height:40px;margin:8px 0 12px} +.network-page .statusbar{height:var(--footer-height);min-height:0;font-size:10px;padding:0 18px;overflow:hidden;white-space:nowrap} +.network-page #toast{bottom:85px;font-size:12px;z-index:60} +.panel-backdrop{position:fixed;inset:var(--header-height) 0 var(--footer-height);background:#11282066;backdrop-filter:blur(2px);border:0;z-index:35;cursor:default} +@media(min-width:1700px){.network-page .app-grid{grid-template-columns:300px minmax(0,1fr) 350px}.network-page.hide-controls .app-grid{grid-template-columns:minmax(0,1fr) 350px}.network-page.hide-inspector .app-grid{grid-template-columns:300px minmax(0,1fr)}.network-page.hide-controls.hide-inspector .app-grid{grid-template-columns:minmax(0,1fr)}} +@media(max-width:1200px) and (min-width:1001px){.network-page .app-grid{grid-template-columns:250px minmax(0,1fr) 280px}.network-page.hide-controls .app-grid{grid-template-columns:minmax(0,1fr) 280px}.network-page.hide-inspector .app-grid{grid-template-columns:250px minmax(0,1fr)}.network-page.hide-controls.hide-inspector .app-grid{grid-template-columns:minmax(0,1fr)}.network-page .controls,.network-page .inspector{padding:18px 16px}.network-page .title-block{display:none}.network-page .viewer-footer>span{display:none}} +@media(max-width:1000px){ + .network-page .app-grid,.network-page.hide-controls .app-grid,.network-page.hide-inspector .app-grid,.network-page.hide-controls.hide-inspector .app-grid{display:grid;grid-template-columns:minmax(0,1fr);height:calc(100dvh - var(--header-height) - var(--footer-height));min-height:0} + .network-page .viewer{height:100%;min-height:0;grid-column:1;grid-row:1} + .network-page .controls,.network-page .inspector{position:fixed;top:var(--header-height);bottom:var(--footer-height);width:min(360px,calc(100vw - 32px));z-index:40;height:auto;box-shadow:0 10px 40px #0e252b33;border:0;border-radius:0} + .network-page .controls{left:0}.network-page .inspector{right:0} + .network-page .title-block{padding-left:15px}.network-page .statusbar>span:nth-child(2){display:none} +} +@media(max-width:650px){ + .network-page{--header-height:64px;--footer-height:28px} + .network-page .topbar{padding:0 12px;gap:8px;display:flex;height:64px;min-height:0} + .network-page .brand{gap:7px;font-size:10px;letter-spacing:.5px}.network-page .brand-icon{width:29px;height:29px;font-size:21px;border-radius:8px}.network-page .brand small{font-size:6px;letter-spacing:.4px} + .network-page .title-block,.network-page .local-badge{display:none}.panel-actions{margin-left:auto;gap:5px}.panel-actions button{font-size:11px;padding:8px;min-height:36px} + .network-page .viewport-heading{left:16px;right:16px;top:20px}.network-page .viewport-heading h2{font-size:23px;max-width:245px}.network-page .viewport-heading p{font-size:10px}.network-page .viewport-heading .section-kicker{font-size:8px;letter-spacing:1px} + .network-page .scale-badges{left:16px;top:108px}.network-page .coordinate-note{left:16px;top:142px;font-size:9px}.network-page .legend-panel{left:14px;bottom:123px;max-width:185px;padding:9px 11px}.network-page .legend-panel>div{font-size:10px} + .network-page .axes{right:0;bottom:110px}.network-page .datum-note{right:15px;bottom:100px;font-size:8px} + .network-page .view-toolbar{bottom:45px;gap:0;padding:4px;max-width:calc(100% - 20px)}.network-page .view-toolbar button{font-size:11px;padding:8px 7px;min-height:36px}.network-page .view-toolbar i{margin:0 1px} + .network-page .viewer-footer{left:15px;right:15px;bottom:12px}.network-page .viewer-footer>span{display:none}.network-page .viewer-footer .text-button{margin-left:auto} + .network-page .statusbar{font-size:9px;padding:0 12px;flex-wrap:nowrap}.network-page .statusbar>span:last-child{display:none} + .network-page dialog{padding:18px;max-height:85dvh;overflow:auto}.network-page .audit-filter-label{align-items:flex-start;gap:10px;flex-direction:column}.network-page .audit-filter-label select{width:100%;max-width:100%}.network-page .audit-summary-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.network-page .audit-summary-grid div{min-width:0;overflow-wrap:anywhere}.network-page .audit-row{gap:8px;font-size:12px}.network-page .audit-row>span{min-width:0;overflow-wrap:anywhere} +} -- 2.54.0 From 535de1e38d5005b8ae1375546de2b6b12c0bdc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=EC=96=B8?= Date: Wed, 28 Oct 2026 10:15:06 +0900 Subject: [PATCH 4/6] feat: improve workbench layout and panel controls (#9) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 29e164f..e9b2393 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hwacheon-network-viewer-validation", "private": true, - "scripts": { "test:ui": "node tests/viewer.cjs", "test:runner": "node tests/launch-failure.cjs", "test:audit": "node tests/audit-highlight.cjs" }, + "scripts": { "test:ui": "node tests/viewer.cjs", "test:runner": "node tests/launch-failure.cjs", "test:audit": "node tests/audit-highlight.cjs", "test:layout": "node tests/responsive-ui.cjs" }, "devDependencies": { "playwright": "1.62.1" } } -- 2.54.0 From a6ce439f3727e27f7957806e5a52f4fb7acd8122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=EC=96=B8?= Date: Wed, 28 Oct 2026 10:15:06 +0900 Subject: [PATCH 5/6] test: verify responsive workbench layout (#9) --- tests/responsive-ui.cjs | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/responsive-ui.cjs diff --git a/tests/responsive-ui.cjs b/tests/responsive-ui.cjs new file mode 100644 index 0000000..1583ae2 --- /dev/null +++ b/tests/responsive-ui.cjs @@ -0,0 +1,52 @@ +const {chromium}=require('playwright'); +const assert=require('node:assert/strict'); +const fs=require('node:fs/promises'); +const path=require('node:path'); +const out=path.resolve(__dirname,'../test-results'); +(async()=>{ + const browser=await chromium.launch({headless:true,...(process.env.PLAYWRIGHT_CHANNEL?{channel:process.env.PLAYWRIGHT_CHANNEL}:{})}); + const page=await browser.newPage();const errors=[];page.on('pageerror',e=>errors.push(e.message)); + const results=[];let failure; + await fs.mkdir(out,{recursive:true}); + try{ + for(const [width,height] of [[1920,1080],[1366,768],[390,844]]){ + await page.setViewportSize({width,height});await page.goto(process.env.VIEWER_URL||'http://127.0.0.1:8081/',{waitUntil:'networkidle'}); + await page.waitForFunction(()=>document.querySelector('#loading').hidden); + const bounds=await page.evaluate(()=>({width:innerWidth,scroll:document.documentElement.scrollWidth, + mainBottom:document.querySelector('.app-grid').getBoundingClientRect().bottom, + footerTop:document.querySelector('.statusbar').getBoundingClientRect().top, + canvasWidth:document.querySelector('#scene').getBoundingClientRect().width})); + assert.ok(bounds.scroll<=width+1,JSON.stringify(bounds));assert.ok(bounds.mainBottom<=bounds.footerTop+1); + assert.ok(bounds.canvasWidth>width*(width<=1000?.9:.4)); + await page.screenshot({path:path.join(out,`ui9-${width}.png`),fullPage:true}); + if(width>1000){ + const original=bounds.canvasWidth;await page.locator('#toggle-controls').click(); + assert.equal(await page.locator('#controls-panel').isVisible(),false); + assert.ok(await page.locator('#scene').evaluate(e=>e.getBoundingClientRect().width)>original); + await page.locator('#toggle-controls').click(); + await page.locator('#toggle-inspector').click();assert.equal(await page.locator('#inspector-panel').isVisible(),false); + await page.locator('#toggle-inspector').click(); + }else{ + assert.equal(await page.locator('#controls-panel').isVisible(),false); + assert.equal(await page.locator('#inspector-panel').isVisible(),false); + await page.locator('#toggle-controls').click();assert.equal(await page.locator('#controls-panel').isVisible(),true); + await page.locator('#search').fill('UHR1-02901');await page.locator('#search-results button').first().click(); + assert.equal(await page.locator('#controls-panel').isVisible(),false); + assert.equal(await page.locator('#inspector-panel').isVisible(),true); + assert.equal(await page.locator('#selected-id').textContent(),'UHR1-02901'); + await page.screenshot({path:path.join(out,'ui9-mobile-details.png'),fullPage:true}); + await page.keyboard.press('Escape');assert.equal(await page.locator('#inspector-panel').isVisible(),false); + assert.equal(await page.locator('#toggle-inspector').evaluate(e=>e===document.activeElement),true); + await page.locator('#toggle-controls').click();await page.locator('#search').fill('__no_result__'); + assert.equal(await page.locator('#search-results button').count(),0); + await page.screenshot({path:path.join(out,'ui9-mobile-controls.png'),fullPage:true}); + await page.locator('#panel-backdrop').click({position:{x:375,y:100}}); + assert.equal(await page.locator('#controls-panel').isVisible(),false); + } + const overflow=await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth+1);assert.equal(overflow,false); + results.push({width,height,status:'passed'}); + } + assert.deepEqual(errors,[]); + }catch(e){failure=e.stack;process.exitCode=1;await page.screenshot({path:path.join(out,'ui9-failure.png'),fullPage:true}).catch(()=>{});} + finally{const report={status:failure?'failed':'passed',viewports:results,...(failure?{error:failure}:{})};await fs.writeFile(path.join(out,'ui9-validation.json'),JSON.stringify(report,null,2));console.log(JSON.stringify(report,null,2));await browser.close();} +})().catch(e=>{console.error(e);process.exitCode=1;}); -- 2.54.0 From b71674657944acc5ea1ec66e1322c4a564639437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=EC=96=B8?= Date: Wed, 28 Oct 2026 10:15:07 +0900 Subject: [PATCH 6/6] test: verify responsive workbench layout (#9) --- validation/issue-9-result.json | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 validation/issue-9-result.json diff --git a/validation/issue-9-result.json b/validation/issue-9-result.json new file mode 100644 index 0000000..e90c883 --- /dev/null +++ b/validation/issue-9-result.json @@ -0,0 +1,75 @@ +{ + "status": "passed", + "viewports": [ + { + "width": 1920, + "height": 1080, + "status": "passed" + }, + { + "width": 1366, + "height": 768, + "status": "passed" + }, + { + "width": 390, + "height": 844, + "status": "passed" + } + ], + "existing_facility_panel_validation": { + "status": "passed", + "facilities_checked": 1662, + "search_click_samples": [ + "MH3", + "UHR1-0058", + "UHR1-02901", + "OUT_PUMP", + "UHR1-01411", + "C_OUT_PUMP" + ], + "checks": [ + "all facility information panels", + "summary counts", + "real search/selection clicks", + "missing-facility explanations", + "unknown ID", + "scale reset", + "plan view", + "layer toggles", + "audit dialog", + "reset", + "no page errors" + ], + "browser": "156.0.4314.42", + "limitations": [ + "Information consistency; screenshot review is separate from dimensional geometry verification." + ] + }, + "audit_highlight_validation": { + "status": "passed", + "target_conduits": 207, + "red_pixels_on": 4127, + "red_pixels_off": 0, + "original_colors_restored": true, + "checks": [ + "target IDs", + "red pixels", + "1 second phases", + "exact original pixel restoration", + "timer cancellation", + "steady mode", + "layer visibility", + "selection reasons", + "3 unplaced facilities", + "reset", + "reduced motion", + "no page errors" + ] + }, + "visual_review": [ + "ui9-1366.png", + "ui9-390.png", + "ui9-mobile-details.png" + ] +} -- 2.54.0