// Project Stack. One dependency-free component; edit projects in demo.html. (() => { if(customElements.get('project-stack'))return; const arrow=''; class ProjectStack extends HTMLElement{ connectedCallback(){ if(this.shadowRoot)return; const r=this.attachShadow({mode:'open'}); r.innerHTML=`
PICK A PROJECT. TAKE A LOOK.
`; this.items=[{title:'Orbit',tag:'BRAND & WEB',description:'A home for your next big idea.',href:'https://example.com/#orbit'},{title:'Forma',tag:'DESIGN SYSTEM',description:'A small system for making things feel consistent.',href:'https://example.com/#forma'},{title:'Echo',tag:'DIGITAL PRODUCT',description:'A quieter place for your favourite sounds.',href:'https://example.com/#echo'}]; const supplied=this.querySelector('script[type="application/json"]'); if(supplied){try{const data=JSON.parse(supplied.textContent);if(Array.isArray(data)&&data.length===3)this.items=data;}catch(_) {}} this.items.forEach((item,i)=>{ const card=document.createElement('section');card.className='card';card.id='project-'+i;card.setAttribute('role','tabpanel');card.setAttribute('aria-labelledby','tab-'+i); card.innerHTML=`
SELECTED WORK

View project ${arrow}0${i+1} / 03
`; card.querySelector('.label').textContent=String(item.title||'Project');card.querySelector('.tag').textContent=String(item.tag||'PROJECT');card.querySelector('.description').textContent=String(item.description||''); // optional "accent": "#rrggbb" recolors this card; only plain hex gets into the style const ac=String(item.accent||'');if(/^#[0-9a-f]{6}$/i.test(ac)){card.style.setProperty('--accent',ac);card.style.setProperty('--deep',`color-mix(in srgb,${ac} 45%,#000)`);card.style.setProperty('--haze',ac+'24');} try{const u=new URL(item.href);card.querySelector('a').href=['https:','http:'].includes(u.protocol)?u.href:'https://example.com';}catch(_){card.querySelector('a').href='https://example.com';} r.querySelector('.deck').append(card); const tab=document.createElement('button');tab.type='button';tab.className='tab';tab.id='tab-'+i;tab.setAttribute('role','tab');tab.setAttribute('aria-controls',card.id);tab.innerHTML=`0${i+1}`;tab.querySelector('span').textContent=String(item.title||'Project');tab.addEventListener('click',()=>this.select(i));r.querySelector('.tabs').append(tab); }); this.select(0); r.querySelector('.tabs').addEventListener('keydown',e=>{let next=this.active;if(e.key==='ArrowRight')next=(next+1)%3;else if(e.key==='ArrowLeft')next=(next+2)%3;else if(e.key==='Home')next=0;else if(e.key==='End')next=2;else return;e.preventDefault();this.select(next);r.querySelectorAll('.tab')[next].focus();}); } select(index){ this.active=index;const cards=[...this.shadowRoot.querySelectorAll('.card')]; cards.forEach((card,i)=>{const depth=(i-index+3)%3;card.inert=depth!==0;card.setAttribute('aria-hidden',String(depth!==0));card.style.zIndex=String(3-depth);card.style.transform=depth===0?'translate(0,0) rotate(0deg) scale(1)':`translate(${depth===1?-17:19}px,${depth===1?-22:-39}px) rotate(${depth===1?-6:7}deg) scale(${depth===1?.95:.89})`;card.style.filter=depth?'brightness(.64)':'brightness(1)';}); this.shadowRoot.querySelectorAll('.tab').forEach((tab,i)=>{tab.setAttribute('aria-selected',String(i===index));tab.tabIndex=i===index?0:-1;}); } } customElements.define('project-stack',ProjectStack); })();