diff --git a/CLAUDE.md b/CLAUDE.md
index 8345bd6..19c71d8 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1726,3 +1726,9 @@ design review, not desktop behavior. Source format/FPS, MV and ISO stay visible;
optional columns persist in browser storage and narrow views expose secondary
values through row Details. ISO arm is explicitly distinct from writer state.
Its feedback is isolated under `sources-table-v1`; preserve prior proposals.
+
+The table mockup exposes Grade selected in the toolbar and Grade in the
+inspector. Both open the source-bound expanded grade dialog, then return to
+the same table state. Picture/scopes are explicitly illustrative, not sampled
+video or native processing. ROI is scopes-only; the LUT/native stack remains
+a preservation requirement rather than a claim of complete web simulation.
diff --git a/site-assets/reviews/sources-table/grade.css b/site-assets/reviews/sources-table/grade.css
new file mode 100644
index 0000000..c6cc222
--- /dev/null
+++ b/site-assets/reviews/sources-table/grade.css
@@ -0,0 +1,3 @@
+#grade-dialog{width:min(1450px,96vw);max-width:96vw;max-height:94vh;padding:0;background:#101b22;color:#e7f0f3;border:1px solid #587582;border-radius:12px;overflow:auto}#grade-dialog::backdrop{background:#02080dd9}.grade-header{display:flex;justify-content:space-between;gap:20px;padding:18px 22px;border-bottom:1px solid #344d59}.grade-header h2{margin:5px 0}.grade-header p{font-size:12px;margin:5px 0}.grade-header button{align-self:start}.grade-warning{background:#20372f;padding:10px 22px;font-size:12px;color:#bad5cc;line-height:1.5}.grade-workspace{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:20px;padding:20px}.grade-monitor{min-width:0}.grade-monitor-tools{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:12px}.grade-monitor-tools label{font-size:12px;margin-left:auto}.grade-stage{aspect-ratio:16/9;background:#182b38;border:1px solid #52707d;position:relative;overflow:hidden;border-radius:7px;touch-action:none;cursor:crosshair}.grade-stage>svg,#grade-art{position:absolute;inset:0;width:100%;height:100%}#grade-art{background:linear-gradient(120deg,#275469,#63536d 60%,#31444b)}.grade-art-label{position:absolute;top:12px;left:14px;font-size:10px;color:#c0d0da}.grade-subject{position:absolute;inset:13% 20% 0}.grade-subject span{display:block;position:absolute;left:36%;top:0;width:28%;height:45%;border-radius:50%;background:#aac7bd}.grade-subject i{position:absolute;left:7%;top:48%;width:86%;height:90%;border-radius:50% 50% 0 0;background:#5d908a}.grade-stage>strong{position:absolute;bottom:16px;left:18px;font-size:14px}#grade-region-guide rect,#grade-region-guide ellipse{stroke:#8ae0ca;stroke-width:3;fill:#8ae0ca20;vector-effect:non-scaling-stroke}.scope-toolbar{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin:12px 0;font-size:12px;color:#bdd4dc}.scope-toolbar select,.grade-controls select,.grade-controls input[type=number]{background:#0c151b;color:#e7f0f3;border:1px solid #4f6b79;border-radius:5px;padding:8px;font:inherit}.scope-note{font-size:11px!important;margin:10px 0;color:#a2b9c5}.grade-scopes{display:grid;grid-template-columns:1fr 1fr .7fr;gap:10px}.grade-scopes section{background:#0b141b;border:1px solid #2e4a58;border-radius:5px;padding:10px;min-width:0}.grade-scopes h3{font-size:11px;margin:0 0 12px}.grade-scopes h3 span{font-weight:400;float:right;color:#93adbc;font-size:9px}.grade-scopes svg{width:100%;height:auto;min-height:120px;display:block}.scope-grid{fill:none;stroke:#3d5361;stroke-width:1}.scope-red,.scope-green,.scope-blue{fill:none;stroke-width:2;stroke-linejoin:round}.scope-red{stroke:#ff797f}.scope-green{stroke:#87dfba}.scope-blue{stroke:#86acff}.grade-scopes text{fill:#a8c4d2;font-size:8px}.grade-controls{background:#1a2b35;padding:16px;border-radius:7px;min-width:0}.grade-controls .check{font-size:12px;display:block}.grade-controls p,.grade-controls li{font-size:12px}.grade-controls label:not(.check){display:block;margin:16px 0;font-size:12px}.grade-controls label>select{display:block;width:100%;margin-top:6px}.grade-tabs{display:flex;gap:5px;margin:18px 0}.grade-tabs button{flex:1;padding:9px 5px}.grade-tabs [aria-selected=true],.grade-monitor-tools [aria-pressed=true]{background:#8ae0ca;color:#10251e}.grade-control-row{margin-bottom:18px}.grade-control-row label{margin-bottom:7px!important}.grade-control-row output{float:right}.grade-control-row>div{display:flex;align-items:center;gap:8px}.grade-controls input[type=range]{width:100%;min-width:50px;accent-color:#8ae0ca}.grade-controls input[type=number]{width:62px;padding:6px}.grade-control-row button{padding:6px}.grade-curve{width:100%;background:#0c151b;border:1px solid #3d5967;border-radius:6px}#grade-curve-path{fill:none;stroke:#a8e9d4;stroke-width:3}#grade-curve-point{fill:#e7f0f3}.grade-controls details{margin-top:20px;border-top:1px solid #47606e;padding-top:16px}.grade-controls summary{font-size:12px}.grade-footer{display:flex;align-items:center;justify-content:end;gap:12px;padding:14px 22px;background:#1b2e38;border-top:1px solid #45616e;position:sticky;bottom:0}.grade-footer span{margin-right:auto;font-size:11px;color:#abc4d1}#grade-edit-status{color:#c1d8d0}.grade-stage:focus-visible{outline:2px solid #8ae0ca;outline-offset:3px}@media(max-width:1000px){.grade-workspace{grid-template-columns:minmax(0,1fr)}.grade-controls{display:block}.grade-scopes svg{min-height:90px}.grade-footer{flex-wrap:wrap}.grade-footer span{width:100%}}@media(max-width:600px){#grade-dialog{width:98vw;max-width:98vw;max-height:96vh}.grade-header,.grade-warning{padding:12px}.grade-workspace{padding:10px;gap:12px}.grade-header h2{font-size:18px}.grade-monitor-tools{gap:5px}.grade-monitor-tools button{font-size:10px;padding:7px}.grade-monitor-tools label{margin-left:0}.grade-scopes{grid-template-columns:1fr}.grade-scopes svg{max-height:170px}.grade-scopes h3{font-size:12px}.grade-footer{padding:12px;gap:8px}.grade-footer button{font-size:11px}}
+
+.grade-header{padding:12px 18px}.grade-warning{padding:8px 18px;font-size:11px}.grade-stage{max-width:min(100%,calc(35vh * 16 / 9));margin:0 auto}.expanded-picture .grade-stage{max-width:none}.grade-scopes svg{height:115px;min-height:0}.grade-monitor-tools label{margin-left:0}.grade-monitor-tools{justify-content:flex-start}@media(max-width:1000px){.grade-stage{max-width:none}.grade-scopes svg{height:115px}}
diff --git a/site-assets/reviews/sources-table/grade.js b/site-assets/reviews/sources-table/grade.js
new file mode 100644
index 0000000..d8d1e39
--- /dev/null
+++ b/site-assets/reviews/sources-table/grade.js
@@ -0,0 +1,35 @@
+(()=>{
+const $=id=>document.getElementById(id),dialog=$('grade-dialog');
+const controls=[['brightness','Brightness',50,150,100],['contrast','Contrast',50,150,100],['saturation','Saturation',0,200,100],['intensity','Overall intensity',0,100,100]];
+const neutral=()=>({brightness:100,contrast:100,saturation:100,intensity:100,bypass:false,channel:'Master',midpoints:{Master:0,Red:0,Green:0,Blue:0},preset:'neutral'});
+const applied=new Map(),regions=new Map();let target=null,draft=neutral(),roi=null,mode='move',drag=null,live=false,opener=null;
+$('grade-sliders').innerHTML=controls.map(([key,label,min,max,value])=>`
`).join('');
+function saveApplied(){applied.set(target.id,structuredClone(draft));const active=!draft.bypass&&(controls.some(control=>draft[control[0]]!==control[4])||Object.values(draft.midpoints).some(v=>v!==0));window.dispatchEvent(new CustomEvent('corevideo-grade-applied',{detail:{id:target.id,active}}));}
+function scopeLabel(){const tap=$('grade-scope-tap').value==='graded'?'Graded draft':'Original source';$('grade-roi-summary').textContent=roi?`${roi.shape==='circle'?'Circle':'Rectangle'} ROI · x ${(roi.x*100).toFixed(1)}% / y ${(roi.y*100).toFixed(1)}% · ${(roi.w*100).toFixed(1)} × ${(roi.h*100).toFixed(1)}%`:'Full-frame scope analysis';$('grade-scope-label').textContent=`Illustrative scopes · ${tap} · ${roi?'ROI':'Full frame'} · not live measurements`;}
+function drawRegion(){const rect=$('grade-rect'),circle=$('grade-circle');rect.hidden=!roi||roi.shape!=='rectangle';circle.hidden=!roi||roi.shape!=='circle';rect.toggleAttribute('hidden',rect.hidden);circle.toggleAttribute('hidden',circle.hidden);rect.style.display=rect.hidden?'none':'block';circle.style.display=circle.hidden?'none':'block';if(roi){rect.setAttribute('x',roi.x*1000);rect.setAttribute('y',roi.y*562.5);rect.setAttribute('width',roi.w*1000);rect.setAttribute('height',roi.h*562.5);circle.setAttribute('cx',(roi.x+roi.w/2)*1000);circle.setAttribute('cy',(roi.y+roi.h/2)*562.5);circle.setAttribute('rx',roi.w*500);circle.setAttribute('ry',roi.h*281.25);}if(target)regions.set(target.id,roi?{...roi}:null);scopeLabel();}
+function render(){
+ for(const [key] of controls){$('grade-'+key).value=$('grade-'+key+'-exact').value=draft[key];$('grade-'+key+'-out').textContent=draft[key]+'%';}
+ $('grade-bypass').checked=draft.bypass;$('grade-live').checked=live;$('grade-channel').value=draft.channel;$('grade-preset').value=draft.preset;$('grade-curve-mid').value=draft.midpoints[draft.channel];
+ const curveY=120-draft.midpoints[draft.channel]*2;$('grade-curve-path').setAttribute('d',`M0 240Q120 ${curveY} 240 0`);$('grade-curve-point').setAttribute('cy',120-draft.midpoints[draft.channel]);$('grade-curve-path').style.stroke={Master:'#b0f2d9',Red:'#ff797f',Green:'#87dfba',Blue:'#86acff'}[draft.channel];
+ const strength=draft.intensity/100,identity=draft.bypass||$('grade-compare').checked;
+ $('grade-art').style.filter=identity?'none':`brightness(${1+(draft.brightness/100-1)*strength}) contrast(${1+(draft.contrast/100-1)*strength}) saturate(${1+(draft.saturation/100-1)*strength})`;
+ $('grade-edit-status').textContent=live?'Live-edit simulation ON. Changes update the simulated applied reference immediately.':'Draft only. Apply and return commits this simulated source grade.';drawRegion();
+}
+function change(){if(live)saveApplied();render();}
+window.addEventListener('corevideo-grade-open',event=>{target=event.detail;opener=document.activeElement;draft=structuredClone(applied.get(target.id)||neutral());roi=regions.get(target.id)||null;live=false;mode='move';$('grade-compare').checked=false;$('grade-expand').checked=false;dialog.classList.remove('expanded-picture');$('grade-scope-tap').value='graded';$('grade-title').textContent=`Grade · ${target.name}`;$('grade-preview-source').textContent=target.name;$('grade-source-info').textContent=`Input ${String(target.input).padStart(2,'0')} · ${target.format} / ${target.fps} fps${target.online?' · simulated source':' · source offline; layout demonstration only'}`;setMode('move');gradeTab('primaries');render();dialog.showModal();});
+function close(commit){if(commit)saveApplied();dialog.close();opener?.focus({preventScroll:true});}
+$('grade-expand').addEventListener('change',()=>dialog.classList.toggle('expanded-picture',$('grade-expand').checked));
+$('grade-apply').addEventListener('click',()=>close(true));$('grade-discard').addEventListener('click',()=>close(false));$('grade-close').addEventListener('click',()=>close(false));dialog.addEventListener('cancel',event=>{event.preventDefault();close(false);});
+for(const [key,,min,max,defaultValue] of controls){function update(value){if(!Number.isFinite(value)||valuemax){$('grade-edit-status').textContent=`Enter ${min}–${max}%. The current grade is unchanged.`;return;}draft[key]=value;change();}$('grade-'+key).addEventListener('input',()=>update(Number($('grade-'+key).value)));$('grade-'+key+'-exact').addEventListener('change',()=>update($('grade-'+key+'-exact').value.trim()===''?NaN:Number($('grade-'+key+'-exact').value)));document.querySelector(`[data-grade-reset="${key}"]`).addEventListener('click',()=>update(defaultValue));}
+$('grade-preset').addEventListener('change',()=>{const choice=$('grade-preset').value;draft={...neutral(),preset:choice,...(choice==='warm'?{brightness:108,contrast:112,saturation:120}:choice==='mono'?{saturation:0}:{})};change();});
+$('grade-live').addEventListener('change',()=>{live=$('grade-live').checked;if(live)saveApplied();render();});$('grade-bypass').addEventListener('change',()=>{draft.bypass=$('grade-bypass').checked;change();});$('grade-compare').addEventListener('change',render);$('grade-channel').addEventListener('change',()=>{draft.channel=$('grade-channel').value;render();});$('grade-curve-mid').addEventListener('input',()=>{draft.midpoints[draft.channel]=Number($('grade-curve-mid').value);change();});$('grade-scope-tap').addEventListener('change',scopeLabel);
+function gradeTab(name){for(const button of document.querySelectorAll('[data-grade-tab]')){const on=button.dataset.gradeTab===name;button.setAttribute('aria-selected',String(on));button.tabIndex=on?0:-1;$('grade-'+button.dataset.gradeTab).hidden=!on;}}
+document.querySelectorAll('[data-grade-tab]').forEach(button=>{button.addEventListener('click',()=>gradeTab(button.dataset.gradeTab));button.addEventListener('keydown',event=>{if(!['ArrowLeft','ArrowRight','Home','End'].includes(event.key))return;event.preventDefault();const tabs=[...document.querySelectorAll('[data-grade-tab]')],i=event.key==='Home'?0:event.key==='End'?2:(tabs.indexOf(button)+(event.key==='ArrowRight'?1:2))%3;gradeTab(tabs[i].dataset.gradeTab);tabs[i].focus();});});
+function setMode(value){mode=value;for(const button of document.querySelectorAll('[data-roi-mode]'))button.setAttribute('aria-pressed',String(button.dataset.roiMode===mode));}
+document.querySelectorAll('[data-roi-mode]').forEach(button=>button.addEventListener('click',()=>setMode(button.dataset.roiMode)));$('grade-full-frame').addEventListener('click',()=>{roi=null;drawRegion();});
+const clamp=(n,min,max)=>Math.max(min,Math.min(max,n));function point(event){const r=$('grade-stage').getBoundingClientRect();return {x:clamp((event.clientX-r.left)/r.width,0,1),y:clamp((event.clientY-r.top)/r.height,0,1)};}
+$('grade-stage').addEventListener('pointerdown',event=>{if(event.button!==0)return;const p=point(event);if(mode==='move'&&(!roi||p.xroi.x+roi.w||p.yroi.y+roi.h))return;event.preventDefault();$('grade-stage').focus();drag={start:p,prior:roi?{...roi}:null};$('grade-stage').setPointerCapture(event.pointerId);});
+$('grade-stage').addEventListener('pointermove',event=>{if(!drag)return;const p=point(event);if(mode==='move'){roi={...drag.prior,x:clamp(drag.prior.x+p.x-drag.start.x,0,1-drag.prior.w),y:clamp(drag.prior.y+p.y-drag.start.y,0,1-drag.prior.h)};}else if(mode==='circle'){const aspect=$('grade-stage').clientWidth/$('grade-stage').clientHeight,w=Math.min(Math.abs(p.x-drag.start.x),Math.abs(p.y-drag.start.y)/aspect),h=w*aspect;if(w>.005)roi={shape:mode,x:p.x{if(!roi||!['ArrowLeft','ArrowRight','ArrowUp','ArrowDown'].includes(event.key))return;event.preventDefault();const n=event.shiftKey ? .01 : .001;roi.x=clamp(roi.x+(event.key==='ArrowLeft'?-n:event.key==='ArrowRight'?n:0),0,1-roi.w);roi.y=clamp(roi.y+(event.key==='ArrowUp'?-n:event.key==='ArrowDown'?n:0),0,1-roi.h);drawRegion();});
+})();
diff --git a/site-assets/reviews/sources-table/index.html b/site-assets/reviews/sources-table/index.html
index 1d5e164..33d13cc 100644
--- a/site-assets/reviews/sources-table/index.html
+++ b/site-assets/reviews/sources-table/index.html
@@ -1,9 +1,10 @@
-Sources table + inspector · CoreVideo review Alternative proposal · Table first · Needs review Keep the overview. Open the detail when you need it. The table remains the main workspace. Check every source quickly, use MV and ISO controls in the row, and open an inspector for the selected input.
Interactive design mockup All people, pictures, signal values, meters and actions are simulated. Nothing controls your app. ISO arm is distinct from recording; no recording starts here.
Compare the earlier inspector-first proposal ↗
+Sources table + inspector · CoreVideo review Alternative proposal · Table first · Needs review Keep the overview. Open the detail when you need it. The table remains the main workspace. Check every source quickly, use MV and ISO controls in the row, and open an inspector for the selected input.
Interactive design mockup All people, pictures, signal values, meters and actions are simulated. Nothing controls your app. ISO arm is distinct from recording; no recording starts here.
Compare the earlier inspector-first proposal ↗
Compare the same populated show Full table Table + inspector Guest disconnects Guest returns
Try: scan format/FPS → toggle Input 2’s MV → inspect Alice → close inspector. Other assignments and scene routes stay put.
-1 · Assign inputs / Multiview 2 · Route outputs 3 · Compose scenes / Take
+1 · Assign inputs / Multiview 2 · Route outputs 3 · Compose scenes / Take
FORMAT / FPS = observed source signal · all demo values
# Source Type Format FPS Signal Audio MV ISO Details
No matching assigned sources. Choose All sources to see empty inputs.
MV inclusion does not change a scene or Take. ISO shows arm state separately from writer state. Empty and offline inputs keep their numbers and assignments.
-
SIMULATED SOURCE PREVIEW ●
Source Picture Audio
Paired audio, sync offset and live gain/mute remain source controls; processing and bus routing remain in Audio/Routing. This proposal does not add an Apply barrier to live audio.
This is a layout review. Audio processing is not simulated.
Table overview. Select Inspect to open details; no scene change is implied.
+SIMULATED SOURCE PREVIEW ●
Source Picture Audio
Paired audio, sync offset and live gain/mute remain source controls; processing and bus routing remain in Audio/Routing. This proposal does not add an Apply barrier to live audio.
This is a layout review. Audio processing is not simulated.
Table overview. Select Inspect to open details; no scene change is implied.
Source catalog · retained categories Use the inspector’s Assigned source picker to bind these simulated sources to an input. This catalog shows the intended entry point; it does not create or connect a real device.
Zoom Guests and meeting share, with identity, role and missing/offline state.
Cameras / capture Device connection, format, signal, paired audio and synchronization.
Screens / media Window/screen selection, media library and playback setup.
Network / browser SRT, RTMP and browser configuration remain reachable. Enabled adapter support is preserved.
Close catalog
What this changes—and what it keeps Kept in the overview Ten stable slots, category/type, observed format and FPS, signal, audio summary, quick MV and separate ISO state. Secondary columns are customizable; important data is not replaced by a big preview.
Opened on demand The selected input’s larger preview, assignment, lower-third text and detailed controls. Close the inspector to recover the full-width table. Selection and filtering never reassign a source.
Same production workflow Sources assigns inputs and MV inclusion. Routing owns output routes; Scenes owns composition and Take. Detailed grade, audio, recording and webcam-only overlays remain their existing workflows.
At narrower widths the inspector stacks below the table. Secondary columns move into row Details so Format/FPS, MV and ISO remain available without horizontal page scrolling.
Open the full preservation requirements →
Does the table + inspector strike the right balance? No login needed. Notes are saved privately for CoreVideo for 180 days. Unsent drafts stay in this browser. Pick an area and mention the control or screenshot you mean.
-Design alternative only · Not installed or approved · Compare collapsed and expanded views before choosing a direction.
+Design alternative only · Not installed or approved · Compare collapsed and expanded views before choosing a direction. Illustrative picture and scopes, not sampled video or native grading. Nothing is sent to your app. Returning keeps the table, selection, MV and ISO state.
Draw rectangle Draw circle Select / move Full frame Expand picture Compare original · preview only
ILLUSTRATIVE SOURCE PICTURE
Full-frame scope analysis AnalyzeGraded draft Original source
ROI affects scopes only. The grade still covers the whole source. No crop, local grade mask or outgoing ROI guide is created.
Waveform Luma · 0–100 IRE Vectorscope 75% targets R Mg B Cy G Yl Illustrative scopes · Graded draft · Full frame · not live measurements
+
diff --git a/site-assets/reviews/sources-table/table.js b/site-assets/reviews/sources-table/table.js
index 1f695e5..e4aeda7 100644
--- a/site-assets/reviews/sources-table/table.js
+++ b/site-assets/reviews/sources-table/table.js
@@ -27,12 +27,12 @@ document.querySelectorAll('[data-column]').forEach(c=>c.addEventListener('change
function renderRows(){
const query=$('search').value.toLowerCase(),category=$('category').value;
const visible=inputs.filter(i=>{const s=source(i);return (name(i)+' '+(s?.type||'')+' '+String(i.id).padStart(2,'0')).toLowerCase().includes(query)&&(category==='all'||(category==='network'?['SRT','RTMP'].includes(s?.type):s?.type===category));});
- $('rows').innerHTML=visible.map(i=>{const s=source(i),offline=s&&!s.online,state=!s?'Empty':offline?'Offline':'Advancing';return `${String(i.id).padStart(2,'0')} ${!s?'+':offline?'—':s.type==='Media'?'▶':'●'} ${escape(name(i))} ${escape(s?s.type+' · '+(offline?'Offline':i.mv?'In MV':'Not in MV'):'Choose a source')}
${s?.type||'—'} ${s&&s.online?s.format:'—'} ${s&&s.online?s.fps:'—'} ${state} ${s&&s.level!=null&&s.online?`
${s.level} dBFS `:`${offline?'Unavailable':s?'No paired audio':'—'} `} ${i.writing?'REC · demo':i.iso?'Armed':'Off'} Inspect ⋯ Source type${s?.type||'Unassigned'} Signal${state} Paired audio${escape(s?.audio||'None')} Audio level${s?.level!=null&&s.online?s.level+' dBFS · demo':'Unavailable'} ISO writer${i.writing?'Writing · simulated':i.iso?'Armed · writer idle':'Not armed'} ${offline?`Last known format, not advancing${s.format} · ${s.fps} fps `:''}
`;}).join('');
+ $('rows').innerHTML=visible.map(i=>{const s=source(i),offline=s&&!s.online,state=!s?'Empty':offline?'Offline':'Advancing';return `${String(i.id).padStart(2,'0')} ${!s?'+':offline?'—':s.type==='Media'?'▶':'●'} ${escape(name(i))} ${escape(s?s.type+' · '+(offline?'Offline':i.mv?'In MV':'Not in MV')+(looks[s.id].gradeApplied?' · Grade':''):'Choose a source')}
${s?.type||'—'} ${s&&s.online?s.format:'—'} ${s&&s.online?s.fps:'—'} ${state} ${s&&s.level!=null&&s.online?`
${s.level} dBFS `:`${offline?'Unavailable':s?'No paired audio':'—'} `} ${i.writing?'REC · demo':i.iso?'Armed':'Off'} Inspect ⋯ Source type${s?.type||'Unassigned'} Signal${state} Paired audio${escape(s?.audio||'None')} Audio level${s?.level!=null&&s.online?s.level+' dBFS · demo':'Unavailable'} ISO writer${i.writing?'Writing · simulated':i.iso?'Armed · writer idle':'Not armed'} ${offline?`Last known format, not advancing${s.format} · ${s.fps} fps `:''}
`;}).join('');
$('no-results').hidden=visible.length>0;$('counts').textContent=`${inputs.filter(i=>i.source).length}/10 assigned · ${inputs.filter(i=>i.mv&&i.source).length} in MV · ${inputs.filter(i=>i.iso).length} ISO armed`;
}
function tab(name){activeTab=name;for(const button of document.querySelectorAll('[data-tab]')){const on=button.dataset.tab===name;button.setAttribute('aria-selected',String(on));button.tabIndex=on?0:-1;$('pane-'+button.dataset.tab).hidden=!on;}}
function renderInspector(){
- const i=input(),s=source(),look=looks[i.source];pending=null;$('assignment-confirm').hidden=true;
+ const i=input(),s=source(),look=looks[i.source];$('grade-selected').disabled=$('grade-inspector').disabled=!s;$('grade-selected').title=s?'Grade '+look.name:'Assign a source to grade it';pending=null;$('assignment-confirm').hidden=true;
$('inspector').hidden=!opened;$('workspace').classList.toggle('inspector-open',opened);$('toggle-inspector').setAttribute('aria-expanded',String(opened));$('toggle-inspector').textContent=opened?'Close inspector':'Open inspector';
$('inspector-slot').textContent=`INPUT ${String(selected).padStart(2,'0')} · ${s?.type||'UNASSIGNED'}`;$('inspector-name').textContent=name(i);$('preview-name').textContent=!s?'Unassigned':!s.online?'Offline · binding retained':name(i);
$('detail-stats').textContent=!s?'No source selected.':s.online?`${s.format} · ${s.fps} fps · advancing (demo)`:`Offline · no advancing video. Last known ${s.format} / ${s.fps} fps.`;
@@ -66,6 +66,9 @@ $('opacity').addEventListener('input',()=>opacity(Number($('opacity').value)));$
$('catalog').addEventListener('click',()=>{$('catalog-panel').hidden=!$('catalog-panel').hidden;if(!$('catalog-panel').hidden)$('catalog-panel').scrollIntoView({behavior:'auto'});});$('close-catalog').addEventListener('click',()=>{$('catalog-panel').hidden=true;$('catalog').focus();});
for(const id of ['quality-resolution','quality-fps'])$(id).addEventListener('change',()=>status('Requested Zoom quality changed in the mockup. Observed source format/FPS cells remain unchanged.'));
document.addEventListener('click',event=>{for(const detail of document.querySelectorAll('.tools details[open]'))if(!detail.contains(event.target))detail.open=false;});
+function openGrade(){const s=source();if(!s)return;window.dispatchEvent(new CustomEvent('corevideo-grade-open',{detail:{id:s.id,name:looks[s.id].name,input:selected,format:s.format,fps:s.fps,online:s.online}}));}
+$('grade-selected').addEventListener('click',openGrade);$('grade-inspector').addEventListener('click',openGrade);
+window.addEventListener('corevideo-grade-applied',event=>{if(!looks[event.detail.id])return;looks[event.detail.id].gradeApplied=event.detail.active;renderRows();status('Simulated source grade applied. Source assignment, MV, ISO and scene routes unchanged.');});
render();
})();