aboutsummaryrefslogtreecommitdiff
path: root/docs/prototypes/panel-widget-gallery.html
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-12 22:40:22 -0500
committerCraig Jennings <c@cjennings.net>2026-07-12 22:40:22 -0500
commit066bdac1c195fa27d4b71f2ba53ee9265ee7c833 (patch)
tree451f613874801c291b9fb692c8ed6da3d731dce9 /docs/prototypes/panel-widget-gallery.html
parent4064de53a9abfa7620d9d7e3f95e867ba0bf54e3 (diff)
downloadarchsetup-066bdac1c195fa27d4b71f2ba53ee9265ee7c833.tar.gz
archsetup-066bdac1c195fa27d4b71f2ba53ee9265ee7c833.zip
refactor(gallery): extract indicators R10, R11, R25, R26, R30, R31 into GW builders
Diffstat (limited to 'docs/prototypes/panel-widget-gallery.html')
-rw-r--r--docs/prototypes/panel-widget-gallery.html226
1 files changed, 14 insertions, 212 deletions
diff --git a/docs/prototypes/panel-widget-gallery.html b/docs/prototypes/panel-widget-gallery.html
index 2d7bcb9..f81b60b 100644
--- a/docs/prototypes/panel-widget-gallery.html
+++ b/docs/prototypes/panel-widget-gallery.html
@@ -1850,22 +1850,22 @@ card(I,'N28','Patch-bay jack field',
(st,rd)=>GW.patchBay(st,{onChange:(v,t)=>rd(t)}),
'<b>routing shown as patches.</b> A grid of jacks with cables between connected pairs. Click one jack then another to add/remove a cable.');
card(I,'R10','Data matrix readout',
- `<svg id="dmx" class="rsvg press" viewBox="0 0 190 74" width="190" height="74"></svg>`,
+ (st,rd)=>GW.dataMatrix(st,{onChange:(v,t)=>rd(t)}),
'<b>a page of amber fields.</b> The console data block — several labeled values on one lit matrix. Click to cycle pages; readout names the page. After a cockpit data readout.');
card(I,'R11','Warning flag window',
- `<svg id="wflag" class="rsvg press" viewBox="0 0 120 64" width="120" height="64"></svg>`,
+ (st,rd)=>GW.warningFlag(st,{onChange:(v,t)=>rd(t)}),
'<b>the mechanical alarm flag.</b> A striped barber-pole slides into a window when the condition trips — not a lamp, a flag. Click to trip and clear it. After the VIB flag on an altimeter.');
card(I,'R25','Fourteen-segment display',
- `<svg id="seg14" class="rsvg press" viewBox="0 0 130 54" width="130" height="54"></svg>`,
+ (st,rd)=>GW.seg14(st,{onChange:(v,t)=>rd(t)}),
'<b>segments that spell.</b> The starburst alphanumeric — diagonals and split bars let it write words, not just digits. Click to cycle the word. After a pedal preset display.');
card(I,'R26','Response graph',
- `<svg id="rgraph" class="rsvg" viewBox="0 0 190 110" width="190" height="110"></svg>`,
+ (st,rd)=>GW.responseGraph(st,{onChange:(v,t)=>rd(t)}),
'<b>a curve on labeled axes.</b> Log frequency across, dB up the side, and the amber peak is yours to place — drag it anywhere on the plot. After the response display of a mastering processor.');
card(I,'R30','Telegraph indicator',
- `<svg id="telind" class="rsvg press" viewBox="0 0 110 110" width="110" height="110"></svg>`,
+ (st,rd)=>GW.telegraphIndicator(st,{onChange:(v,t)=>rd(t)}),
'<b>state on a pie of sectors.</b> The pointer and its little flag name the active state, engine-telegraph style. Click to step the state. After an electric indicator dial.');
card(I,'R31','Radar sweep',
- `<svg id="radar" class="rsvg press" viewBox="0 0 130 130" width="130" height="130"></svg>`,
+ (st,rd)=>GW.radarSweep(st,{onChange:(v,t)=>rd(t)}),
'<b>the rotating scan.</b> A sweep beam circles the bearing ring, contacts bloom as it passes and fade after. Click to mark the current bearing. After a radar PPI scope.');
card(I,'R35','Day-date disc calendar',
`<svg id="daydate" class="rsvg press" viewBox="0 0 130 130" width="130" height="130"></svg>`,
@@ -1916,215 +1916,17 @@ function tickMcVu(){const a=lvl();const target=Math.min(1.02,a*1.12);
/* R07-R09 extracted to widgets.js (GW.*) */
-/* R10 data matrix readout */
-const DMX_PAGES=[
- ['SYS',()=>['CPU 42 MEM 61','DSK 58 TMP 47C','ARCH 6.18 LTS']],
- ['NET',()=>['WIFI @HYATT 300M','VPN UP DNS OK','BT 2 PAIRED']],
- ['TIME',()=>{const d=new Date(),p=n=>String(n).padStart(2,'0');
- return [`${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}`,
- `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`,
- 'UP 14D 06:12'];}]];
-let dmxI=0,dmxLines=[];
-(function(){const s=$('dmx');
- const defs=svgEl(s,'defs',{});
- const fl=svgEl(defs,'filter',{id:'dmxGlow',x:'-30%',y:'-30%',width:'160%',height:'160%'});
- svgEl(fl,'feGaussianBlur',{in:'SourceGraphic',stdDeviation:1.1});
- const bg=svgEl(defs,'linearGradient',{id:'dmxBg',x1:0,y1:0,x2:0,y2:1});
- svgEl(bg,'stop',{offset:'0','stop-color':'var(--scr-bg1,#140d06)'});
- svgEl(bg,'stop',{offset:'1','stop-color':'var(--scr-bg2,#0a0705)'});
- svgEl(s,'rect',{x:1,y:1,width:188,height:72,rx:5,fill:'url(#dmxBg)',stroke:'#2c261d'});
- for(let r=0;r<3;r++){
- const glow=svgEl(s,'text',{x:12,y:24+r*18,'font-size':9,'letter-spacing':'.15em',
- 'font-family':'var(--mono)',fill:'var(--scr-hi,var(--gold-hi))',opacity:.6,filter:'url(#dmxGlow)'});
- const crisp=svgEl(s,'text',{x:12,y:24+r*18,'font-size':9,'letter-spacing':'.15em',
- 'font-family':'var(--mono)',fill:'var(--scr-hi,var(--gold-hi))'});
- dmxLines.push([glow,crisp]);}
- $('dmx').onclick=()=>setDmx((dmxI+1)%DMX_PAGES.length);})();
-function setDmx(i){dmxI=i;const [name,rowsFn]=DMX_PAGES[i];
- rowsFn().forEach((txt,r)=>{dmxLines[r][0].textContent=txt;dmxLines[r][1].textContent=txt;});
- setRd('R10','page '+name);}
-setDmx(0);
-if(!matchMedia('(prefers-reduced-motion: reduce)').matches)
- setInterval(()=>{if(DMX_PAGES[dmxI][0]==='TIME')setDmx(dmxI);},1000);
-
-/* R11 warning flag window */
-let flagOn=false,flagStripes=null;
-(function(){const s=$('wflag');
- const defs=svgEl(s,'defs',{});
- const pat=svgEl(defs,'pattern',{id:'barber',width:10,height:10,patternUnits:'userSpaceOnUse',
- patternTransform:'rotate(45)'});
- svgEl(pat,'rect',{x:0,y:0,width:10,height:10,fill:'#17130c'});
- svgEl(pat,'rect',{x:0,y:0,width:5,height:10,fill:'var(--gold)'});
- const clip=svgEl(defs,'clipPath',{id:'flagWin'});
- svgEl(clip,'rect',{x:14,y:20,width:56,height:24});
- svgEl(s,'rect',{x:11,y:17,width:62,height:30,rx:3,fill:'#0a0908',stroke:'#2c261d','stroke-width':2});
- const g=svgEl(s,'g',{'clip-path':'url(#flagWin)'});
- flagStripes=svgEl(g,'rect',{x:14,y:20,width:56,height:24,fill:'url(#barber)'});
- flagStripes.style.transition='transform .18s';flagStripes.style.transform='translateX(-58px)';
- svgEl(s,'text',{x:92,y:35,'text-anchor':'middle','font-size':7,'letter-spacing':'.14em',
- 'font-family':'var(--mono)',fill:'var(--steel)'}).textContent='VIB';
- $('wflag').onclick=()=>setFlag(!flagOn);})();
-function setFlag(on){flagOn=on;flagStripes.style.transform=on?'translateX(0)':'translateX(-58px)';
- setRd('R11',on?'FLAG':'clear');}
-setFlag(false);
+/* R10, R11 extracted to widgets.js (GW.*) */
+
/* R13, R17 extracted to widgets.js (GW.*) */
-/* R30 telegraph indicator: the pointer names the state on labeled sectors */
-const TELIND_STATES=['OK','TEST','BUSY','STOP','ATTN','FAULT'];
-let telI=0,telPtr=null;
-(function(){const s=$('telind'),cx=55,cy=55,N=TELIND_STATES.length;
- svgEl(s,'circle',{cx,cy,r:50,fill:'#171412',stroke:'#060505','stroke-width':2});
- svgEl(s,'circle',{cx,cy,r:43,fill:'var(--cream)',stroke:'#b7b29a','stroke-width':1});
- TELIND_STATES.forEach((st,i)=>{
- const a=i*360/N-90+180/N;
- const [lx1,ly1]=polar(cx,cy,9,i*360/N),[lx2,ly2]=polar(cx,cy,43,i*360/N);
- svgEl(s,'line',{x1:lx1,y1:ly1,x2:lx2,y2:ly2,stroke:'#2b2418','stroke-width':1});
- const g=svgEl(s,'g',{transform:`rotate(${a+90},${cx},${cy})`});
- svgEl(g,'text',{x:cx,y:cy-28,'text-anchor':'middle','font-size':7,'font-weight':700,
- 'font-family':'var(--mono)',fill:'#2b2418'}).textContent=st;});
- telPtr=svgEl(s,'g',{});telPtr.style.transition='transform .18s';telPtr.style.transformOrigin=`${cx}px ${cy}px`;
- svgEl(telPtr,'polygon',{points:`${cx-2.5},${cy} ${cx},${cy-38} ${cx+2.5},${cy}`,fill:'var(--fail)',stroke:'#7a2a1a','stroke-width':.5});
- svgEl(telPtr,'rect',{x:cx+1,y:cy-16,width:10,height:7,rx:1,fill:'var(--gold)',stroke:'#7d5c16','stroke-width':.6});
- svgEl(s,'circle',{cx,cy,r:8,fill:'#171412',stroke:'#000'});
- $('telind').onclick=()=>setTelind((telI+1)%TELIND_STATES.length);})();
-function setTelind(i){telI=i;const N=TELIND_STATES.length;
- telPtr.style.transform=`rotate(${i*360/N+180/N}deg)`;
- setRd('R30',TELIND_STATES[i]);}
-setTelind(0);
-
-/* R31 radar sweep: rotating beam, blooming contacts, click to mark */
-let radarA=210,radarSweep=null,radarBlips=[],radarMark=null;
-const RADAR_CONTACTS=[[60,.55],[205,.7],[318,.4]];
-(function(){const s=$('radar'),cx=65,cy=65;
- const defs=svgEl(s,'defs',{});
- const clip=svgEl(defs,'clipPath',{id:'radarClip'});
- svgEl(clip,'circle',{cx,cy,r:46});
- svgEl(s,'circle',{cx,cy,r:62,fill:'#1a1714',stroke:'#060505','stroke-width':2});
- svgEl(s,'circle',{cx,cy,r:52,fill:'var(--scr-bg1,#120b05)',stroke:'var(--scr-brd,#3a2a12)','stroke-width':1});
- for(let d=0;d<360;d+=30){const a=d;
- const [x1,y1]=polar(cx,cy,51,a),[x2,y2]=polar(cx,cy,47,a);
- svgEl(s,'line',{x1,y1,x2,y2,stroke:'var(--scr-ink,var(--gold))','stroke-width':1,opacity:.8});
- const g=svgEl(s,'g',{transform:`rotate(${a},${cx},${cy})`});
- svgEl(g,'text',{x:cx,y:cy-47.5,'text-anchor':'middle','font-size':4.6,'font-family':'var(--mono)',
- fill:'var(--scr-ink,var(--gold))',opacity:.9}).textContent=String(d).padStart(3,'0');}
- for(let d=0;d<360;d+=10){if(d%30===0)continue;
- const [x1,y1]=polar(cx,cy,51,d),[x2,y2]=polar(cx,cy,49,d);
- svgEl(s,'line',{x1,y1,x2,y2,stroke:'var(--scr-ink,var(--gold))','stroke-width':.6,opacity:.5});}
- const g=svgEl(s,'g',{'clip-path':'url(#radarClip)'});
- svgEl(g,'circle',{cx,cy,r:46,fill:'var(--scr-bg2,#0d0803)'});
- for(const r of [15,30,45])
- svgEl(g,'circle',{cx,cy,r,fill:'none',stroke:'var(--scr-ink,var(--gold))','stroke-opacity':.18,'stroke-width':.7});
- radarSweep=svgEl(g,'g',{});
- for(let t=0;t<5;t++){const a0=-14+t*2.8,a1=a0+2.8;
- const [x1,y1]=polar(cx,cy,46,a0),[x2,y2]=polar(cx,cy,46,a1);
- svgEl(radarSweep,'path',{d:`M ${cx} ${cy} L ${x1} ${y1} A 46 46 0 0 1 ${x2} ${y2} Z`,
- fill:'var(--scr-ink,var(--gold))','fill-opacity':(0.04+t*0.045).toFixed(3)});}
- svgEl(radarSweep,'line',{x1:cx,y1:cy,x2:cx,y2:cy-46,stroke:'var(--scr-hi,var(--gold-hi))','stroke-width':1.4,
- transform:`rotate(0,${cx},${cy})`,opacity:.95});
- RADAR_CONTACTS.forEach(([bd,rf])=>{const [x,y]=polar(cx,cy,46*rf,bd);
- radarBlips.push(svgEl(g,'circle',{cx:x,cy:y,r:2.2,fill:'var(--scr-hi,var(--gold-hi))',opacity:0}));});
- svgEl(s,'circle',{cx,cy,r:3,fill:'var(--scr-ink,var(--gold))',opacity:.8});
- radarMark=svgEl(s,'text',{x:cx,y:124,'text-anchor':'middle','font-size':6,'letter-spacing':'.12em',
- 'font-family':'var(--mono)',fill:'var(--steel)'});
- radarMark.textContent='BEARING —';
- $('radar').onclick=()=>{radarMark.textContent='MARK '+String(Math.round(radarA)%360).padStart(3,'0');
- setRd('R31','mark '+String(Math.round(radarA)%360).padStart(3,'0'));};})();
-function tickRadar(){radarA=(radarA+2.6)%360;
- radarSweep.setAttribute('transform',`rotate(${radarA},65,65)`);
- RADAR_CONTACTS.forEach(([bd],i)=>{
- let d=(radarA-bd+360)%360; // degrees since beam passed
- radarBlips[i].setAttribute('opacity',d<200?Math.max(0,.95-d/200):0);});
- radarMark.textContent=radarMark.textContent.startsWith('MARK')?radarMark.textContent:
- 'BEARING '+String(Math.round(radarA)%360).padStart(3,'0');}
-tickRadar();setRd('R31','sweeping');
-if(!matchMedia('(prefers-reduced-motion: reduce)').matches)setInterval(tickRadar,50);
-
-/* R25 fourteen-segment display: the starburst that spells */
-const SEG14_WORDS=['ZOOM','ECHO','TAPE','MOOD','HALL','COMP'];
-const SEG14_MAP={A:['a','b','c','e','f','g1','g2'],C:['a','d','e','f'],
- D:['a','b','c','d','i','l'],E:['a','d','e','f','g1','g2'],H:['b','c','e','f','g1','g2'],
- L:['d','e','f'],M:['b','c','e','f','h','j'],O:['a','b','c','d','e','f'],
- P:['a','b','e','f','g1','g2'],T:['a','i','l'],Z:['a','d','j','k']};
-let seg14I=0,seg14Cells=[];
-(function(){const s=$('seg14');
- svgEl(s,'rect',{x:1,y:1,width:128,height:52,rx:5,fill:'#0a0f08',stroke:'#1c2a16','stroke-width':2});
- // segment shapes in a 22x36 local box (x0,y0 top-left)
- const TH=2.6,W=22,H=36,mid=H/2;
- const segs={
- a:[[2,0],[W-2,0],[W-4,TH],[4,TH]],
- d:[[2,H],[W-2,H],[W-4,H-TH],[4,H-TH]],
- f:[[0,2],[TH,4],[TH,mid-2],[0,mid-1]],
- e:[[0,H-2],[TH,H-4],[TH,mid+2],[0,mid+1]],
- b:[[W,2],[W-TH,4],[W-TH,mid-2],[W,mid-1]],
- c:[[W,H-2],[W-TH,H-4],[W-TH,mid+2],[W,mid+1]],
- g1:[[2,mid],[4,mid-TH/2],[W/2-1,mid-TH/2],[W/2-1,mid+TH/2],[4,mid+TH/2]],
- g2:[[W-2,mid],[W-4,mid-TH/2],[W/2+1,mid-TH/2],[W/2+1,mid+TH/2],[W-4,mid+TH/2]],
- i:[[W/2-TH/2,3],[W/2+TH/2,3],[W/2+TH/2,mid-2],[W/2-TH/2,mid-2]],
- l:[[W/2-TH/2,H-3],[W/2+TH/2,H-3],[W/2+TH/2,mid+2],[W/2-TH/2,mid+2]],
- h:[[3,3],[5.5,3],[W/2-2,mid-3],[W/2-4.5,mid-3]],
- j:[[W-3,3],[W-5.5,3],[W/2+2,mid-3],[W/2+4.5,mid-3]],
- k:[[3,H-3],[5.5,H-3],[W/2-2,mid+3],[W/2-4.5,mid+3]],
- m:[[W-3,H-3],[W-5.5,H-3],[W/2+2,mid+3],[W/2+4.5,mid+3]]};
- for(let d=0;d<4;d++){const x0=13+d*28,y0=9,cell={};
- for(const k in segs){
- cell[k]=svgEl(s,'polygon',{points:segs[k].map(p=>`${x0+p[0]},${y0+p[1]}`).join(' '),
- fill:'var(--sevoff)'});}
- seg14Cells.push(cell);}
- $('seg14').onclick=()=>setSeg14((seg14I+1)%SEG14_WORDS.length);})();
-function setSeg14(i){seg14I=i;const word=SEG14_WORDS[i].padEnd(4,' ');
- seg14Cells.forEach((cell,d)=>{const lit=SEG14_MAP[word[d]]||[];
- for(const k in cell){const on=lit.includes(k);
- cell[k].setAttribute('fill',on?'var(--sevgrn)':'var(--sevoff)');
- cell[k].setAttribute('style',on?'filter:drop-shadow(0 0 2.5px rgba(87,211,87,.6))':'');}});
- setRd('R25',SEG14_WORDS[i]);}
-setSeg14(0);
-
-/* R26 response graph: log-frequency axes, a draggable amber peak */
-const RG={x0:30,x1:182,y0:8,y1:86,fLo:Math.log10(32),fHi:Math.log10(16000),db:12};
-let rgFc=1200,rgGain=6.5,rgCurve=null,rgGlowLine=null,rgHandle=null;
-(function(){const s=$('rgraph');
- svgEl(s,'rect',{x:1,y:1,width:188,height:108,rx:5,fill:'#0d1410',stroke:'#22301f','stroke-width':2});
- const FREQS=[32,64,128,250,500,1000,2000,4000,8000,16000];
- FREQS.forEach(f=>{const x=RG.x0+(Math.log10(f)-RG.fLo)/(RG.fHi-RG.fLo)*(RG.x1-RG.x0);
- svgEl(s,'line',{x1:x,y1:RG.y0,x2:x,y2:RG.y1,stroke:'#22301f','stroke-width':.6});
- svgEl(s,'text',{x,y:RG.y1+9,'text-anchor':'middle','font-size':4.8,'font-family':'var(--mono)',
- fill:'var(--steel)'}).textContent=f<1000?f:(f/1000)+'k';});
- for(const db of [12,6,0,-6,-12]){const y=RG.y0+(RG.db-db)/(2*RG.db)*(RG.y1-RG.y0);
- svgEl(s,'line',{x1:RG.x0,y1:y,x2:RG.x1,y2:y,stroke:'#22301f','stroke-width':db===0?1:.6});
- svgEl(s,'text',{x:RG.x0-4,y:y+2,'text-anchor':'end','font-size':4.8,'font-family':'var(--mono)',
- fill:'var(--steel)'}).textContent=(db>0?'+':'')+db;}
- rgGlowLine=svgEl(s,'polyline',{points:'',fill:'none',stroke:'var(--gold-hi)','stroke-width':3,
- opacity:.35,filter:'url(#avGlow)'});
- rgCurve=svgEl(s,'polyline',{points:'',fill:'none',stroke:'var(--gold)','stroke-width':1.6});
- rgHandle=svgEl(s,'circle',{r:4,fill:'var(--gold-hi)',stroke:'#7d5c16','stroke-width':1});
- rgHandle.style.cursor='move';
- // 2D drag: the handle (and the plot) place the peak in both axes at once
- s.style.touchAction='none';
- s.addEventListener('pointerdown',e=>{s.setPointerCapture(e.pointerId);
- const move=ev=>{const r=s.getBoundingClientRect(),sx=190/r.width,sy=110/r.height;
- const px=(ev.clientX-r.left)*sx,py=(ev.clientY-r.top)*sy;
- const lf=RG.fLo+Math.max(0,Math.min(1,(px-RG.x0)/(RG.x1-RG.x0)))*(RG.fHi-RG.fLo);
- const g=RG.db-Math.max(0,Math.min(1,(py-RG.y0)/(RG.y1-RG.y0)))*2*RG.db;
- setRgraph(Math.pow(10,lf),g);};
- move(e);
- const up=()=>{s.removeEventListener('pointermove',move);s.removeEventListener('pointerup',up);s.removeEventListener('pointercancel',up);};
- s.addEventListener('pointermove',move);s.addEventListener('pointerup',up);s.addEventListener('pointercancel',up);
- e.preventDefault();});})();
-function setRgraph(fc,gain){rgFc=Math.max(32,Math.min(16000,fc));rgGain=Math.max(-12,Math.min(12,gain));
- const xOf=f=>RG.x0+(Math.log10(f)-RG.fLo)/(RG.fHi-RG.fLo)*(RG.x1-RG.x0);
- const yOf=db=>RG.y0+(RG.db-db)/(2*RG.db)*(RG.y1-RG.y0);
- const sigma=0.28;let pts='';
- for(let i=0;i<=76;i++){const lf=RG.fLo+i/76*(RG.fHi-RG.fLo);
- const db=rgGain*Math.exp(-Math.pow(lf-Math.log10(rgFc),2)/(2*sigma*sigma));
- pts+=`${xOf(Math.pow(10,lf)).toFixed(1)},${yOf(db).toFixed(1)} `;}
- rgCurve.setAttribute('points',pts.trim());rgGlowLine.setAttribute('points',pts.trim());
- rgHandle.setAttribute('cx',xOf(rgFc));rgHandle.setAttribute('cy',yOf(rgGain));
- const fLbl=rgFc<1000?Math.round(rgFc)+' Hz':(rgFc/1000).toFixed(1)+' kHz';
- setRd('R26',`${fLbl} · ${rgGain>=0?'+':''}${rgGain.toFixed(1)} dB`);}
-setRgraph(1200,6.5);
+/* R25, R26, R30, R31 extracted to widgets.js (GW.*) */
+
+
+
/* ============ LIVE SIGNAL LOOPS ============ */
/* extracted meters: the page keeps the clock + demo signal, drives card handles */
@@ -2371,10 +2173,10 @@ let dkPos=0,dkCarry=0;
/* ---- screen-family chips: first round, the clearly-screen widgets ----
Defaults match each widget's shipped color (var fallbacks), so no family is applied until a chip is clicked.
The nixie stays chipless on purpose: neon is only ever orange, so alternates would be historically false. */
-screenChips('R10',$('dmx'),['amber','green','red','blue','vfd'],'amber');
+screenChips('R10',$('card-R10').gw.el,['amber','green','red','blue','vfd'],'amber');
screenChips('R17',$('card-R17').gw.el,['green','amber','red','blue','vfd'],'green');
screenChips('R19',$('card-R19').gw.el,['white','green','amber','blue','vfd'],'white');
-screenChips('R31',$('radar'),['amber','green','red','blue','vfd'],'amber');
+screenChips('R31',$('card-R31').gw.el,['amber','green','red','blue','vfd'],'amber');
screenChips('N11',$('card-N11').gw.el,['green','amber','red','blue','vfd'],'green');
BOOST.forEach(no=>{const rd=document.getElementById('rd-'+no);
if(rd)rd.closest('.card').querySelector('.stagew').classList.add('boost');});