(() => { 'use strict'; const B = window.Brand; const $ = id => document.getElementById(id); const modes = ['light','dim','dark']; const modeName = mode => mode[0].toUpperCase() + mode.slice(1); const defaults = {palette:'harbor',font:'dm',mark:'mosaic',mode:matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'}; let state = {...defaults}; let storageAvailable = true; try { const saved = JSON.parse(localStorage.getItem('mosaic-brand-preview-v1') || '{}'); for (const [key, items] of Object.entries({palette:B.palettes.map(p=>p.id),font:B.fonts.map(f=>f.id),mark:B.logos.map(l=>l.id),mode:modes})) { if (items.includes(saved?.[key])) state[key]=saved[key]; } $('review-notes').value = localStorage.getItem('mosaic-brand-notes-v1') || ''; } catch { storageAvailable = false; } function announce(text) {$('announcement').textContent=text;} function persist() { try {localStorage.setItem('mosaic-brand-preview-v1',JSON.stringify(state));} catch {storageAvailable=false;announce('Browser storage is unavailable. Download your review notes before closing.');} } for(const [id, data] of [['palette',B.palettes],['font',B.fonts],['mark',B.logos]]) { $(id).innerHTML=data.map(x=>``).join(''); $(id).addEventListener('change',event=>{state[id]=event.target.value;update();}); } document.querySelectorAll('[name=mode]').forEach(input=>input.addEventListener('change',()=>{state.mode=input.value;update();})); $('logo-grid').innerHTML=B.logos.map((l,i)=>`
${String(i+1).padStart(2,'0')} / ${l.family}
${B.logo(l.id)}

${l.name}

${l.note}

${[16,24,32].map(size=>`${B.logo(l.id,size)}${size} px`).join('')}${B.logo(l.id,24).replace('class="logo"','class="logo reverse"')}Reverse

${l.tradeoff}

`).join(''); $('font-grid').innerHTML=B.fonts.map(f=>`
${f.detail}

${f.name}

A place for your next idea.

Plan a project, write a chapter, or ask an agent to help. Start with what you need.

0123456789
Il1 O0 · 12:48 · 1,024

${f.note}

`).join(''); $('palette-grid').innerHTML=B.palettes.map(p=>{ const t=B.tokens(p,'light'); return `

${p.name}

${p.theory} · ${p.hue}° / ${p.accent}°

${p.note}

${p.use}

${modes.map(mode=>{const t=B.tokens(p,mode);return ``;}).join('')}
`; }).join(''); $('icon-grid').innerHTML=[['home','Home'],['conversation','Conversations'],['projects','Projects'],['agents','Agents'],['people','People'],['board','Board'],['extensions','Extensions'],['search','Search']].map(([id,label])=>`
${B.icon(id)}${label}
`).join(''); document.querySelectorAll('[data-icon]').forEach(el=>el.innerHTML=B.icon(el.dataset.icon)); document.querySelectorAll('[data-choose-mark]').forEach(button=>button.addEventListener('click',()=>{state.mark=button.dataset.chooseMark;update();})); document.querySelectorAll('[data-choose-font]').forEach(button=>button.addEventListener('click',()=>{state.font=button.dataset.chooseFont;update();})); document.querySelectorAll('[data-palette][data-mode]').forEach(button=>button.addEventListener('click',()=>{state.palette=button.dataset.palette;state.mode=button.dataset.mode;update();})); function update(shouldAnnounce=true) { const p=B.palettes.find(p=>p.id===state.palette), f=B.fonts.find(f=>f.id===state.font), l=B.logos.find(l=>l.id===state.mark); const t=B.tokens(p,state.mode); for(const [role,value] of Object.entries(t)) document.documentElement.style.setProperty('--'+role,value); document.documentElement.style.setProperty('--font',f.family); document.documentElement.style.colorScheme=state.mode==='light'?'light':'dark'; document.documentElement.dataset.mode=state.mode; document.documentElement.dataset.palette=state.palette; for(const id of ['palette','font','mark']) $(id).value=state[id]; document.querySelectorAll('[name=mode]').forEach(input=>input.checked=input.value===state.mode); document.querySelectorAll('[data-logo]').forEach(el=>el.innerHTML=B.logo(state.mark)); $('current-palette').textContent=p.name; $('current-mode').textContent=modeName(state.mode); for(const [kind,data] of [['mark',B.logos],['font',B.fonts],['palette',B.palettes]]) { data.forEach(item=>{ const chosen=item.id===state[kind]; document.querySelector(`[data-${kind}-card="${item.id}"]`).classList.toggle('is-selected',chosen); document.querySelector(`[data-${kind}-label="${item.id}"]`).textContent=chosen?'Selected':''; document.querySelector(`[data-choose-${kind}="${item.id}"]`)?.setAttribute('aria-pressed',String(chosen)); }); } document.querySelectorAll('[data-palette][data-mode]').forEach(button=>button.setAttribute('aria-pressed',String(button.dataset.palette===state.palette && button.dataset.mode===state.mode))); $('token-title').textContent=`${p.name} / ${modeName(state.mode)} tokens`; $('tokens').innerHTML=Object.entries(t).map(([role,value])=>`
${role.replace('onAction','On action')}
${value.toUpperCase()}
`).join(''); const summary=`${l.name} · ${f.name} · ${p.name} · ${modeName(state.mode)}`; $('selection-summary').textContent=`Current preview: ${summary}.`; persist(); if(shouldAnnounce) announce(`Preview changed to ${summary}.`); } const messages={ready:['conversation','Ready. Try a local success preview.'],loading:['dim','Loading specimen. No request is running. Choose another state to continue.'],empty:['plus','No projects in this example. Your next step would be to create one.'],error:['error','Could not save this example. Your text is unchanged. Use Preview success to try again.'],success:['check','Success preview. No project was created or saved to a server.']}; function sample() { const state=$('sample-state').value, [icon,message]=messages[state]; $('sample-feedback').dataset.state=state; $('sample-feedback').innerHTML=B.icon(icon)+`${message}`; $('sample-action').disabled=state==='loading'; $('sample-title').setAttribute('aria-describedby','sample-help sample-feedback'); } $('sample-state').addEventListener('change',sample); $('sample-action').addEventListener('click',()=>{$('sample-state').value='success';sample();}); $('reset').addEventListener('click',()=>{state={...defaults};update();announce('Preview reset. Your review notes and example project name are unchanged.');}); function download(name,type,content) { const url=URL.createObjectURL(new Blob([content],{type})); const a=document.createElement('a');a.href=url;a.download=name;a.click(); // Browser downloads consume the URL before the next task; retain briefly for portability. setTimeout(()=>URL.revokeObjectURL(url),1000); announce(`${name} prepared for download. Check your browser downloads.`); } $('export').addEventListener('click',()=>{ const p=B.palettes.find(p=>p.id===state.palette); download(`mosaic-${p.id}-tokens.json`,'application/json',JSON.stringify({prototype:true,palette:p.name,theory:p.theory,rationale:p.note,modes:Object.fromEntries(modes.map(mode=>[mode,B.tokens(p,mode)]))},null,2)); }); $('review-notes').addEventListener('input',()=>{ try{localStorage.setItem('mosaic-brand-notes-v1',$('review-notes').value);}catch{storageAvailable=false;announce('Notes cannot be stored in this browser. Download them before closing.');} }); $('download-review').addEventListener('click',()=>{ download('mosaic-brand-review.txt','text/plain',`Mosaic Stack branding review\n${$('selection-summary').textContent}\n\n${$('review-notes').value||'No written notes yet.'}\n\nThese selections are review preferences, not an automatic approval.\n`); }); update(false);sample(); if(!storageAvailable) announce('Browser storage is unavailable. The preview still works. Download notes to keep them.'); })();