app.js
'use strict';
const W=window.WORKSPACE,F=window.FOUNDATION,S=window.STUDIES,app=document.getElementById('app');
const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const icon=n=>F.icons[n]||F.icons.Folder;
const num=(n,max=10)=>Math.max(1,Math.min(max,Math.floor(Number(n)||1)));
const cat=id=>S.categories.find(c=>c.id===id)||S.categories[0];
const defaults=()=>({version:'v1.9',hero:'forest',images:Object.fromEntries(S.categories.map(c=>[c.id,c.recommend])),layout:1,chart:1});
let prefs=defaults(),storageOK=true,motion=!matchMedia('(prefers-reduced-motion: reduce)').matches,gsapContext,route=[];
try{const saved=JSON.parse(localStorage.getItem('samurai-design-v19')||localStorage.getItem('samurai-design-v18')||'null');if(['v1.8','v1.9'].includes(saved?.version)){prefs={...defaults(),layout:num(saved.layout),chart:num(saved.chart),images:Object.fromEntries(S.categories.map(c=>[c.id,num(saved.images?.[c.id]||c.recommend)]))};}}catch{storageOK=false;}
function notify(t){const e=document.getElementById('toast');e.textContent=t;clearTimeout(notify.timer);notify.timer=setTimeout(()=>{e.textContent='';},3200);}
function save(){try{localStorage.setItem('samurai-design-v19',JSON.stringify(prefs));storageOK=true;}catch{storageOK=false;}return storageOK;}
function choose(type,id,value){if(type==='image')prefs.images[cat(id).id]=num(value);else prefs[type]=num(value);save();notify(storageOK?'このブラウザに仮選択を保存しました。':'仮選択はこの画面内だけに保持します。JSONで保存できます。');}
function tile(c,i,cls='',priority=false){const studies=['plan','aum','sales','gp','marketing'],single=['home-marketing','investment','treasury',...studies].includes(c);const file=studies.includes(c)?'study19-'+c+'-'+String(num(i)).padStart(2,'0'):single?(c==='home-marketing'?'home-marketing-r2':c)+'-'+String(num(i)).padStart(2,'0'):'contact-'+c;return `<span class="tile-media ${single?'single-photo':''} ${cls}" role="img" aria-label="${esc(cat(c).name+':'+cat(c).items[num(i)-1].name)}"><img src="assets/${file}.webp" alt="" loading="${priority?'eager':'lazy'}" decoding="async" ${single?'':`style="left:${-((num(i)-1)%2)*100}%;top:${-Math.floor((num(i)-1)/2)*100}%;"`}></span>`;}
function header(){return W.nav();}
function siteHeader(){return '';}
const footer=()=>`<footer class="site-footer"><p>SAMURAI Workspace · 画像はAI生成素材 · 業務データ未接続</p><div><a href="#monthly">月締め・PDF</a><a href="#choices">画像・詳細の選択を確認</a><a href="../../handoff/CLAUDE_HANDOFF_v1.10_GraphAdjacent_20261029.zip">Claude用の資料一式</a><a href="../marketing-v1.9/">前の画面</a></div></footer>`;
function intro(kicker,title,text,extra=''){return `<header class="intro"><p class="eyebrow">${kicker}</p><h1>${title}</h1><p class="intro-text">${text}</p>${extra}</header>`;}
function home(){document.body.className='workspace forest';return `${header('home')}${siteHeader('home')}<main id="content" tabindex="-1"><section class="forest-hero"><div class="forest-picture"><img class="hero-photo" src="assets/forest.webp" alt="朝の光が差し込む深い森。採用したトップの生成画像。"></div><div class="forest-copy"><p class="eyebrow">SAMURAI WORKSPACE</p><h1>必要な仕事へ、<br>ここから。</h1><p>必要な情報を、迷わず。<br class="mobile-only">資料とデータを、ひとつの入口に。</p></div><span class="image-credit">採用トップ · 生成画像</span></section><div class="work-main"><div class="update-line"><strong>UPDATE</strong><span>データ未接続 / 更新履歴は接続後に表示します</span><a href="#marketing">資料一覧へ ${icon('ArrowRight')}</a></div><div class="home-set-controls"><label for="home-set">3入口の画像セット</label><select id="home-set"><option value="0">個別の組み合わせ</option>${Array.from({length:10},(_,i)=>`<option value="${i+1}" ${['home-marketing','investment','treasury'].every(id=>prefs.images[id]===i+1)?'selected':''}>${String(i+1).padStart(2,'0')} の3枚を表示</option>`).join('')}</select><span>入口の写真を切り替えて比較できます</span></div><section class="home-cards principal-cards" aria-label="仕事の入口">${[['home-marketing','マーケティング','数値・資料・月締めPDF','#marketing'],['investment','投融資管理','貸付・為替・投融資の状況','#department/investment'],['treasury','資金管理','デポジット・入出金の状況','#department/treasury']].map(([id,name,desc,href])=>`<article class="work-card portal-card" data-portal="${id}">${tile(id,prefs.images[id],'card-picture',true)}<div class="work-copy"><p class="eyebrow">${cat(id).en}</p><h2>${href?`<a class="card-link" href="${href}">${name} ${icon('ArrowUpRight')}</a>`:`<button class="card-link" data-pending="${name}">${name} ${icon('ArrowUpRight')}</button>`}</h2><p>${desc}${href?'':' · 準備中'}</p><a class="picture-link" href="#images/${id}">画像10案を比べる</a></div></article>`).join('')}</section><nav class="portal-actions" aria-label="画像の選択"><a href="#images/plan">資料の新画像50案を見る ${icon('ArrowRight')}</a><a href="#choices">仮選択を確認する ${icon('ArrowRight')}</a></nav><section class="import-strip" aria-label="ファイルを取り込む"><div class="import-icon">${icon('Folder')}</div><div class="import-strip-copy"><p class="eyebrow">DATA IMPORT</p><h2>ファイルを取り込む</h2><p>CSV・JSONの数値を、このブラウザ内の資料へ。</p></div><button class="button primary" data-pending="ファイルの取り込み">数値を取り込む ${icon('ArrowRight')}</button></section></div></main>${footer()}`;}
const marketingIds=['plan','kpi','aum','sales','gp','comparison','marketing','excel'];
function marketing(){document.body.className='workspace forest';return `${header('home')}${siteHeader('marketing')}<main id="content" tabindex="-1" class="work-main marketing-main"><div class="marketing-title"><div><p class="eyebrow">MARKETING</p><h1>マーケティング資料</h1><p>親フォルダー5つと、独立シート。必要な資料へ、すぐに。</p></div><a class="button outline" href="#images/aum">画像を比較する</a></div><section class="folder-cards" aria-label="マーケティング資料の入口">${marketingIds.map(id=>{const c=cat(id),g=F.folders.groups.find(x=>x.id===id),href=id==='excel'?'#download':'#folder/'+id;return `<article class="folder-card">${tile(id,prefs.images[id],'folder-picture',true)}<div class="folder-copy"><p>${g?'親フォルダー · '+W.docs.filter(d=>d.parent===id).length+'項目':id==='excel'?'書式・PDF・数値一覧':'独立シート'}</p><h2><a class="card-link" href="${href}">${c.name}${icon('ArrowUpRight')}</a></h2><a class="picture-link" href="#images/${id}">画像10案</a></div></article>`;}).join('')}</section><p class="muted">実資料は未接続。AUMは基準値サンプルで確認できます。配下のシート構成は従来版を保持しています。</p></main>${footer()}`;}
function images(id){document.body.className='lab';const c=cat(id);return `${header('images')}<main id="content" tabindex="-1" class="lab-main">${intro('VISUAL LIBRARY','入口に、意味のある美しさを。','事業計画・AUM・販売償還・GP・マーケティングに、それぞれ新しい10案。写真・アート・建築・人物・立体造形を自由に見比べられます。')}<div class="library-shell"><aside class="choice-rail"><h2>入口を選ぶ</h2><nav aria-label="画像を比べる項目">${S.categories.map(v=>`<a href="#images/${v.id}" ${v.id===c.id?'aria-current="page"':''}><span>${v.name}</span><small>${v.group}</small></a>`).join('')}</nav></aside><section class="image-library" aria-labelledby="category-title"><div class="section-heading"><div><p class="eyebrow">${c.en}</p><h2 id="category-title">${c.name}の10案</h2><p>${c.why}</p></div><span class="selection-chip">仮選択 ${String(prefs.images[c.id]).padStart(2,'0')}</span></div><div class="image-grid">${c.items.map(v=>`<article class="image-option ${prefs.images[c.id]===v.id?'is-selected':''}"><button class="image-preview" data-preview="${c.id}" data-n="${v.id}" aria-label="${c.name} ${v.id} ${v.name}を拡大">${tile(c.id,v.id)}<span class="option-number">${String(v.id).padStart(2,'0')}</span>${v.id===c.recommend?'<span class="recommend-mark">GPT推薦</span>':''}</button><div class="option-copy"><h3>${v.name}</h3><button class="choose" data-image="${c.id}" data-n="${v.id}" aria-pressed="${prefs.images[c.id]===v.id}">${prefs.images[c.id]===v.id?'仮選択中':'この画像を選ぶ'} ${icon(prefs.images[c.id]===v.id?'CheckCircle':'ArrowRight')}</button></div></article>`).join('')}</div><p class="source-note">全110枚は今回の提案用に生成した画像です。実在企業・施設・機器を撮影した写真ではありません。画像選択はこのブラウザの仮設定です。</p><div class="section-actions"><a class="button primary" href="${c.group==='ホーム'?'#home':'#marketing'}">組み合わせた画面を見る ${icon('ArrowRight')}</a><a class="button" href="#details">次は詳細ページを比べる</a></div></section></div></main>${footer()}`;}
function details(){document.body.className='lab';return `${header('details')}<main id="content" tabindex="-1" class="lab-main">${intro('DETAIL STUDIES','数字を読む、10の居場所。','配置と情報の優先順位を変えた詳細ページ。すべて同じAUMの基準値サンプルで比較できます。')}<section class="detail-gallery" aria-label="詳細ページの10案">${S.layouts.map(l=>`<article class="layout-option"><a href="#detail/${l.id}" class="layout-preview"><img src="assets/previews/detail-${l.id}.webp" alt="${l.name}の実画面プレビュー" loading="lazy"><span>${String(l.id).padStart(2,'0')}</span></a><div class="layout-copy"><div class="title-row"><h2>${l.name}</h2>${[1,5,10].includes(l.id)?'<span class="small-note">推薦</span>':''}</div><p>${l.desc}</p><p class="muted">${l.forUse}</p><div class="option-actions"><a class="button" href="#detail/${l.id}">画面で試す</a><button class="button ${prefs.layout===l.id?'primary':''}" data-layout="${l.id}" aria-pressed="${prefs.layout===l.id}">${prefs.layout===l.id?'仮選択中':'仮選択'}</button></div></div></article>`).join('')}</section></main>${footer()}`;}
function chartGallery(){document.body.className='lab';return `${header('charts')}<main id="content" tabindex="-1" class="lab-main">${intro('CHART STUDIES','見るから、触れて分かるへ。','立体の厚み、内訳をつなぐ帯、拡大して読むキャンバス。数値を読み取れる10方式です。')}<section class="chart-gallery" aria-label="グラフの10案">${S.charts.map(c=>`<article class="chart-option"><a class="chart-preview" href="#chart/${c.id}" aria-label="${c.id} ${c.name}を操作する">${window.renderStudyChart(c.id,{mini:true,uid:'thumb'+c.id})}<span class="option-number">${String(c.id).padStart(2,'0')}</span></a><div class="layout-copy"><h2>${c.name}</h2><p>${c.desc}</p><p class="muted">${c.forUse} · ${c.recommend}</p><div class="option-actions"><a class="button" href="#chart/${c.id}">操作して比べる</a><button class="button ${prefs.chart===c.id?'primary':''}" data-chart="${c.id}" aria-pressed="${prefs.chart===c.id}">${prefs.chart===c.id?'仮選択中':'仮選択'}</button></div></div></article>`).join('')}</section><p class="source-note">過去推移・予測・相関・顧客別内訳は未接続です。存在しないデータを前提とするグラフは提案に含めていません。</p></main>${footer()}`;}
function table(){const m=F.baseline.summary;return `<section class="data-table data-panel"><h2>基準値の根拠</h2><table><caption>2026/09/30 · 検算用サンプル · 金額:億円</caption><thead><tr><th scope="col">項目</th><th scope="col">金額</th><th scope="col">構成比</th></tr></thead><tbody><tr><th scope="row">リコース</th><td>${m.recourse}</td><td>${m.recoursePercent}%</td></tr><tr><th scope="row">ノンリコース</th><td>${m.nonRecourse}</td><td>${m.nonRecoursePercent}%</td></tr><tr class="total"><th scope="row">AUM合計</th><td>${m.aum}</td><td>100.0%</td></tr></tbody></table><p class="chart-note">表示の丸めにより内訳合計に端数差が出ます。月次実績・予測は未接続です。</p></section>`;}
function metrics(){const m=F.baseline.summary;return `<section class="metrics" aria-label="主要数値">${[['aum','AUM',m.aum,'億円'],['ratio','ノンリコース比率',m.nonRecoursePercent,'%'],['contracts','ファンド本数',m.contracts,'本']].map(([key,label,v,u])=>`<article class="metric data-panel"><h2>${label}</h2><p class="metric-value" data-metric="${key}">${v}<span>${u}</span></p><p class="muted">${key==='contracts'?'基準値の本数':'2026/09/30 基準'}</p></article>`).join('')}</section>`;}
function chartDisplayPicker(id){const short=['立体棒','リング','フロー','マップ','オービット','ドット','バブル','マトリクス','リボン','拡大'];return `<div class="chart-display-controls"><span class="control-caption">グラフ</span><div class="chart-display-options" role="group" aria-label="グラフの表示方式">${S.charts.map((c,n)=>`<button data-inline-chart="${c.id}" aria-pressed="${c.id===Number(id)}" title="${esc(c.name)}">${short[n]}</button>`).join('')}</div></div>`;}
function dataViz(id,{picker=true}={}){const c=S.charts[num(id)-1],m=F.baseline.summary;return `<section class="viz data-panel" aria-label="AUM構成のグラフ">${picker?chartDisplayPicker(id):''}<div class="viz-heading"><div><p class="eyebrow">PORTFOLIO COMPOSITION</p><h2>${c.name}</h2></div><span>単位:億円</span></div><div class="series-controls" role="group" aria-label="強調する内訳"><button data-focus-series="all" aria-pressed="true">全体</button><button data-focus-series="0" aria-pressed="false">リコース</button><button data-focus-series="1" aria-pressed="false">ノンリコース</button></div><div class="svg-wrap">${window.renderStudyChart(id)}</div><div class="chart-legend"><button data-focus-series="0"><i class="gold"></i><span>リコース <strong>${m.recourse}</strong>億円 <small>${m.recoursePercent}%</small></span></button><button data-focus-series="1"><i class="cyan"></i><span>ノンリコース <strong>${m.nonRecourse}</strong>億円 <small>${m.nonRecoursePercent}%</small></span></button></div><p class="chart-readout" id="chart-readout" role="status">全体を表示 · AUM ${m.aum}億円</p>${Number(id)===10?'<label class="zoom-control">表示倍率 <input id="chart-zoom" type="range" min="100" max="150" value="100" step="5"><output id="zoom-output">100%</output></label>':''}<p class="chart-note">${c.note}</p></section>`;}
function detailMenu(){return `<aside class="detail-menu data-panel"><h2>AUMの中身</h2><nav aria-label="AUM内の資料">${W.docs.filter(d=>d.parent==='aum').map(d=>`<a href="#document/${d.id}">${esc(d.title)}</a>`).join('')}</nav></aside>`;}
function insight(){return `<aside class="insight data-panel"><h2>今回、読めること</h2><p>AUMの総額と、リコース/ノンリコースの構成。</p><div class="insight-divider"></div><h3>次につなぐ資料</h3><p>月別AUM・商品構成・リスクの項目は資料接続後に表示します。</p><a href="#charts">グラフ10案を比較 ${icon('ArrowRight')}</a></aside>`;}
function previewControls(l,c,isChart){return `<div class="preview-controls"><a href="${isChart?'#charts':'#details'}">${icon('ArrowLeft')} ${isChart?'グラフ':'詳細'}10案へ</a><label>詳細の配置<select id="layout-select" aria-label="詳細の配置">${S.layouts.map(v=>`<option value="${v.id}" ${v.id===l?'selected':''}>${String(v.id).padStart(2,'0')} ${v.name}</option>`).join('')}</select></label><button class="button primary" data-combine="true" data-layout-id="${l}" data-chart-id="${c}">この組み合わせを仮選択</button></div>`;}
function detail(layout,chart,isChart=false){const l=num(layout),c=num(chart),item=S.layouts[l-1];document.body.className=`detail-view layout-${l}`;return `${header(isChart?'charts':'details')}${siteHeader('detail')}${previewControls(l,c,isChart)}<main id="content" tabindex="-1" class="detail-main"><header class="detail-title"><div><p class="eyebrow">MARKETING / AUM</p><h1>AUM情報</h1><p>2026/09/30 基準 <span class="sample-chip">検算用サンプル · 実データ未接続</span></p></div><div class="detail-style-name"><strong>${String(l).padStart(2,'0')} ${item.name}</strong><span>${item.desc}</span></div></header><div class="dashboard">${detailMenu()}<div class="analysis">${metrics()}${dataViz(c)}${insight()}${table()}</div></div><div class="detail-bottom"><a class="button" href="#marketing">資料一覧へ</a><a class="button primary" href="#choices">仮選択を確認</a></div></main>${footer()}`;}
function choices(){document.body.className='lab';return `${header('choices')}<main id="content" tabindex="-1" class="lab-main">${intro('YOUR COMPOSITION','気に入ったものを、一つの画面へ。','トップは採用済みの森。画像・詳細・グラフは仮選択です。最初はGPTの推薦を組み合わせています。')}<section class="selection-summary"><div class="selected-hero"><img src="assets/forest-card.webp" alt="採用した森のトップ"><div><p class="eyebrow">採用済み</p><h2>深緑のトップ</h2><p>ご指定の写真と左右分割を維持。</p></div></div><div class="chosen-composition"><article><span>詳細の配置</span><h2>${String(prefs.layout).padStart(2,'0')} ${S.layouts[prefs.layout-1].name}</h2><a href="#details">変更する ${icon('ArrowRight')}</a></article><article><span>グラフ</span><h2>${String(prefs.chart).padStart(2,'0')} ${S.charts[prefs.chart-1].name}</h2><a href="#charts">変更する ${icon('ArrowRight')}</a></article></div></section><section class="chosen-images" aria-label="各入口の仮選択">${S.categories.map(c=>`<a href="#images/${c.id}">${tile(c.id,prefs.images[c.id])}<div><h2>${c.name}</h2><p>${String(prefs.images[c.id]).padStart(2,'0')} ${c.items[prefs.images[c.id]-1].name}</p></div>${icon('ArrowUpRight')}</a>`).join('')}</section><div class="selection-actions"><a class="button primary" href="#detail/${prefs.layout}/${prefs.chart}">組み合わせを見る</a><button class="button" data-export="true">選択内容をJSONで保存 ${icon('DownloadSimple')}</button><button class="button" data-reset="true">GPT推薦に戻す</button></div><p class="source-note">${storageOK?'仮選択はこのブラウザに保存されます。別の端末とは同期しません。':'ブラウザの保存を利用できません。ページを閉じる前にJSONで保存してください。'} JSONはGPT・Claudeへの受け渡し用です。本番の確定や業務データの保存ではありません。</p></main>${footer()}`;}
function folder(id){return W.folderPage(id);}
function download(){document.body.className='workspace forest';return `${header('home')}${siteHeader('marketing')}<main id="content" tabindex="-1" class="work-main"><div class="marketing-title"><div><p class="eyebrow">WORKBOOK</p><h1>Excelひな形</h1><p>38シートの書式ひな形。実績・顧客情報・数式は含みません。</p></div></div><section class="download-panel"><h2>マーケティング管理Pの書式</h2><p>シート名・順番・青い親タブ・セル書式を参考にしています。</p><a class="button primary" href="../../downloads/MarketingP_template_v1.4.xlsx" download>Excelひな形をダウンロード ${icon('DownloadSimple')}</a></section></main>${footer()}`;}
function openDialog(title,html){const d=document.getElementById('dialog');document.getElementById('dialog-title').textContent=title;document.getElementById('dialog-body').innerHTML=html;d.showModal();}
function rerender(){const y=scrollY,active=document.activeElement;const key=active?.dataset?.image?'[data-image="'+active.dataset.image+'"][data-n="'+active.dataset.n+'"]':active?.dataset?.layout?'[data-layout="'+active.dataset.layout+'"]':active?.dataset?.chart?'[data-chart="'+active.dataset.chart+'"]':null;render(false);scrollTo(0,y);if(key)document.querySelector(key)?.focus({preventScroll:true});}
function render(scroll=true){
if(W.ctx)W.ctx.prefs=prefs;
if(document.getElementById('dialog').open)document.getElementById('dialog').close();gsapContext?.revert();route=location.hash.slice(1).split('/');const [view,a,b]=route;
app.innerHTML=view==='aum-images'?W.originalScreenshotsPage():view==='document'?W.documentPage(a,b):view==='document-looks'?W.looksPage(a):view==='monthly'?W.monthlyPage():view==='department'?W.departmentPage(a):view==='images'?images(a):view==='details'?details():view==='charts'?chartGallery():view==='detail'?detail(a,b||prefs.chart):view==='chart'?detail(prefs.layout,a,true):view==='marketing'?marketing():view==='choices'?choices():view==='folder'?folder(a):view==='download'?download():home();
if(!['aum-images','document','document-looks','monthly','department','folder'].includes(view))document.title='SAMURAI | '+({images:'画像の候補',details:'詳細の候補',charts:'グラフの候補',detail:'AUMの配置',chart:'グラフ',choices:'仮選択',marketing:'マーケティング資料'}[view]||'TOPページ');
if(scroll)scrollTo(0,0);W.updateNavigation();W.applyOriginalScreenshots();
if(motion&&window.gsap){gsapContext=gsap.context(()=>{gsap.from('.forest-copy,.intro,.detail-title,.document-heading,.folder-heading,.monthly-heading',{opacity:0,y:10,duration:.4,clearProps:'all'});gsap.utils.toArray('.portal-card,.image-option,.layout-option,.chart-option,.subdocument-card,.document-look-option').forEach(e=>gsap.from(e,{opacity:0,y:12,duration:.35,scrollTrigger:{trigger:e,start:'top 96%',once:true}}));const mm=gsap.matchMedia();mm.add('(min-width:1100px)',()=>{const rail=document.querySelector('.choice-rail');if(rail)gsap.to(rail,{scrollTrigger:{trigger:'.library-shell',pin:rail,pinSpacing:false,start:'top 85px',end:'bottom bottom'}});});});}
}
function focusSeries(series){document.querySelectorAll('.viz [data-series]').forEach(e=>{e.style.opacity=series==='all'||e.dataset.series===series?'1':'.22';});document.querySelectorAll('.series-controls button').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.focusSeries===series)));const m=F.baseline.summary;document.getElementById('chart-readout').textContent=series==='all'?`全体を表示 · AUM ${m.aum}億円`:series==='0'?`リコース ${m.recourse}億円 · 構成比 ${m.recoursePercent}%`:`ノンリコース ${m.nonRecourse}億円 · 構成比 ${m.nonRecoursePercent}%`;}
app.addEventListener('click',e=>{const t=e.target.closest('button,[data-pick]');if(!t)return;
if(t.dataset.inlineChart){const id=num(t.dataset.inlineChart),y=scrollY;prefs.chart=id;save();if(route[0]!=='document'){const l=route[0]==='chart'?prefs.layout:num(route[1]);history.replaceState(null,'','#detail/'+l+'/'+id);route=['detail',String(l),String(id)];}document.querySelector('.viz').outerHTML=dataViz(id);const combine=document.querySelector('[data-combine]');if(combine)combine.dataset.chartId=id;document.querySelector('[data-inline-chart="'+id+'"]')?.focus({preventScroll:true});scrollTo(0,y);notify('グラフ表示を '+S.charts[id-1].name+' に切り替えました。');}
if(t.dataset.preview){const c=cat(t.dataset.preview),n=num(t.dataset.n);openDialog(c.name+' / '+String(n).padStart(2,'0')+' '+c.items[n-1].name,`${tile(c.id,n,'dialog-picture',true)}<p>${c.why}</p><p class="muted">AI生成の提案素材。カードに置いた状態も確認できます。</p><div class="dialog-card-example">${tile(c.id,n)}<div><p class="eyebrow">${c.en}</p><h3>${c.name}</h3><p>必要な資料・データへ。</p></div></div><button class="button primary" data-dialog-choose="${c.id}" data-n="${n}">この画像で仮選択</button>`);}
if(t.dataset.image){choose('image',t.dataset.image,t.dataset.n);rerender();}
if(t.dataset.layout){choose('layout',null,t.dataset.layout);rerender();}
if(t.dataset.chart){choose('chart',null,t.dataset.chart);rerender();}
if(t.dataset.combine){prefs.layout=num(t.dataset.layoutId);prefs.chart=num(t.dataset.chartId);save();notify('詳細とグラフの組み合わせを仮選択しました。');}
if(t.dataset.pending==='ファイルの取り込み'){W.importDialog();}else if(t.dataset.pending){const isImport=false;openDialog(t.dataset.pending,isImport?'<p>取り込み操作のデザイン案です。形式・内容の検査結果を確認してから反映する予定です。</p><p>この比較画面では実ファイルの読み込み・送信・保存は行いません。</p>':'<p>資料・データはまだ接続していません。今回はデザインとフォルダー構成の確認です。</p>');}
if(t.dataset.focusSeries!==undefined)focusSeries(t.dataset.focusSeries);
if(t.dataset.pick!==undefined)focusSeries(t.dataset.pick);
if(t.dataset.overview)notify('AUM現状を表示しています。');
if(t.dataset.export){const data={...prefs,interfaceVersion:'v1.10',documentLooks:W.state.looks,sheetPeriod:W.period(),sheetCharts:W.state.sheetCharts||{},interfaceRevision:'mist-period-20261029',heroAdoptedByUser:true,forestSceneMode:'static-photo',selectionStatus:'tentative',imageProvenance:'AI-generated',imageRevision:{'home-marketing':'r2',plan:'v1.9',aum:'v1.9',sales:'v1.9',gp:'v1.9',marketing:'v1.9'},businessDataConnected:false,categories:S.categories.map(c=>({id:c.id,title:c.name,image:prefs.images[c.id],name:c.items[prefs.images[c.id]-1].name})),layoutName:S.layouts[prefs.layout-1].name,chartName:S.charts[prefs.chart-1].name};const url=URL.createObjectURL(new Blob([JSON.stringify(data,null,2)],{type:'application/json'}));const a=document.createElement('a');a.href=url;a.download='SAMURAI_design_choices_v1.10.json';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);notify('選択内容をJSONファイルにしました。');}
if(t.dataset.reset)openDialog('GPT推薦の組み合わせに戻す','<p>現在の仮選択を、最初の推薦に戻します。森の採用は維持します。</p><button class="button primary" data-confirm-reset="true">推薦に戻す</button>');
});
app.addEventListener('change',e=>{if(e.target.id==='home-set'&&Number(e.target.value)){for(const id of ['home-marketing','investment','treasury'])prefs.images[id]=num(e.target.value);save();render(false);document.getElementById('home-set')?.focus({preventScroll:true});notify('3入口の画像セットを切り替えました。');}if(e.target.id==='layout-select'){const c=route[0]==='chart'?num(route[1]):num(route[2]||prefs.chart);location.hash='detail/'+num(e.target.value)+'/'+c;}});
app.addEventListener('input',e=>{if(e.target.id==='chart-zoom'){const z=num(e.target.value,150);document.getElementById('zoom-output').value=z+'%';document.querySelector('.zoom-geometry').setAttribute('transform',`translate(360 170) scale(${z/100}) translate(-360 -170)`);}});
app.addEventListener('focusin',e=>{const p=e.target.closest('[data-pick]');if(p)focusSeries(p.dataset.pick);});
app.addEventListener('pointerover',e=>{const p=e.target.closest('[data-pick]');if(p)focusSeries(p.dataset.pick);});
app.addEventListener('keydown',e=>{const p=e.target.closest('[data-pick]');if(p&&['Enter',' '].includes(e.key)){e.preventDefault();focusSeries(p.dataset.pick);}});
document.getElementById('dialog').addEventListener('click',e=>{const t=e.target.closest('button');if(!t)return;if(t.dataset.dialogChoose){choose('image',t.dataset.dialogChoose,t.dataset.n);document.getElementById('dialog').close();rerender();document.querySelector('[data-image="'+t.dataset.dialogChoose+'"][data-n="'+t.dataset.n+'"]')?.focus({preventScroll:true});}if(t.dataset.confirmReset){prefs=defaults();save();document.getElementById('dialog').close();rerender();notify('GPT推薦に戻しました。');}});
document.getElementById('dialog').addEventListener('keydown',e=>{if(e.key!=='Tab')return;const items=[...e.currentTarget.querySelectorAll('button,a[href],input,select,textarea,[tabindex]')].filter(x=>x.getClientRects().length&&!x.disabled&&x.tabIndex>=0);const first=items[0],last=items.at(-1);if(e.shiftKey&&document.activeElement===first){e.preventDefault();last?.focus();}else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first?.focus();}});
for(const id of ['close-dialog','dialog-ok'])document.getElementById(id).addEventListener('click',()=>document.getElementById('dialog').close());
document.querySelector('.skip').addEventListener('click',e=>{e.preventDefault();document.getElementById('content').focus();});
window.addEventListener('hashchange',()=>{render();document.getElementById('content')?.focus({preventScroll:true});});
matchMedia('(prefers-reduced-motion: reduce)').addEventListener('change',e=>{if(e.matches){motion=false;render(false);}});
W.ctx={esc,icon,tile,dataViz,prefs,notify,dialog:openDialog,refresh:()=>{const y=scrollY;render(false);scrollTo(0,y);},shell:(content,bodyClass,title)=>{document.body.className=bodyClass;document.title='SAMURAI | '+title;return `${header()}<main id="content" tabindex="-1" class="workspace-main">${content}</main>${footer()}`;}};
W.bind();W.bindOriginalScreenshots();W.bindSheetControls();
render();