…
@media (max-width:899px){.fo-split,.fo-split[data-show]{grid-template-columns:1fr}.fo-split>.fo-lp,.fo-split>.fo-rp{display:block}.fo-split .fold{display:none}.fo-lp{position:static}.fo-lp #fo-map canvas{height:min(62vh,560px)}}
.fo-lp svg#fo-map.fo-tall{max-height:none;height:max(420px,calc(100vh - 210px))}
+.fo-orient{position:absolute;top:2px;right:34px;z-index:1;display:none;font-size:16px;line-height:1}
+.fo-orient[aria-pressed=true]{color:var(--th-focus)}
+@media (min-width:900px){.fo-split[data-show=both] .fo-brain:not(.fo-bfolded) .fo-orient{display:inline-flex}}
+.fo-brain .fo-btools{padding-right:64px}
+.wrap{padding-top:0}.fo-head{margin:0 0 6px}.fo-head h1{line-height:1.15}
+.fo-capx{display:block;flex:1 1 100%;margin:6px 0 0;padding:0;border:0;background:none;color:var(--soft);overflow-wrap:anywhere}
+.fo-split:has(#fo-capx) .fo-brain:not(.fo-bfolded) #fo-bcap{display:none}
</style></head>
<body>
-<div class="wrap"><main class="fo" id="fo-main"><p class="small fo-back"><a href="#" data-go="/thoughts">‹ Thoughts</a></p><p class="small" id="fo-load">Loading the Focus…</p></main></div>
+<div class="wrap"><main class="fo" id="fo-main"><p class="small" id="fo-load">Loading the Focus…</p></main></div>
<dialog id="fo-ask"><p id="fo-askt"></p><div id="fo-asksw" class="fo-sw" hidden><span class="on" data-v="one">one task</span><span data-v="split">split by feature</span></div><div class="fo-askb"><button type="button" class="btn sm" id="fo-askno">Cancel</button><button type="button" class="btn sm primary" id="fo-askgo">OK</button></div></dialog>
<script>
…
/* Mockup-focus-split: the map's own shape decides (a portrait pane stacks the sides), not the window's width */
let TALL = typeof window !== "undefined" && window.innerWidth < 700;
+const oriSide = () => typeof window !== "undefined" && window.FOORI === "side"; // ☯: how the tall column is used (the page sets it)
+const sideTall3 = n => ({ rx: Math.min(MAXR.ry, 110 + 30 * Math.sqrt(n)), ry: Math.min(MAXR.rx * 1.3, 190 + 70 * Math.sqrt(n)) });
const splitTall = () => { if (typeof document === "undefined") return false; const s = document.getElementById("fo-split"); return !!s && s.dataset.show === "both" && window.innerWidth >= 900 };
const tallOf = cv => splitTall() || (typeof window !== "undefined" && window.innerWidth < 700); // side by side: the brain's column stands up
const sideTall = n => ({ rx: Math.min(MAXR.ry, 120 + 36 * Math.sqrt(n)), ry: Math.min(MAXR.rx, 150 + 52 * Math.sqrt(n)) }); // grows down, not across
function lobes(M, T) {
- const ms = M.N, ss = TALL ? sideTall : sideSize, sl = ss(ms.length), sr = ss(T.length);
+ const ms = M.N, ss = TALL ? (oriSide() && splitTall() ? sideTall3 : sideTall) : sideSize, sl = ss(ms.length), sr = ss(T.length);
const tall = TALL; // the two sides stacked, model on top: a phone, or a map taller than wide
- if (tall) { LOBE.L = { x: 0, y: -(sl.ry + GAP + 40), ...sl }; LOBE.R = { x: 0, y: sr.ry + GAP + 40, ...sr } } /* room for the root */
+ if (tall && oriSide() && splitTall()) { LOBE.L = { x: -(sl.rx + GAP + 40), y: 0, ...sl }; LOBE.R = { x: sr.rx + GAP + 40, y: 0, ...sr } } else if (tall) { LOBE.L = { x: 0, y: -(sl.ry + GAP + 40), ...sl }; LOBE.R = { x: 0, y: sr.ry + GAP + 40, ...sr } } /* room for the root */
else { LOBE.L = { x: -(sl.rx + GAP + 40), y: 0, ...sl }; LOBE.R = { x: sr.rx + GAP + 40, y: 0, ...sr } } if (ms.length) { let cx = 0, cy = 0; for (const n of ms) { cx += n.x; cy += n.y } cx /= ms.length; cy /= ms.length;
const q = a => { const v = a.slice().sort((x, y) => x - y); return v[Math.floor(v.length * .85)] || 1 };
…
function size() { if (!cv) return; dpr = Math.min(2, W0.devicePixelRatio || 1); W = cv.clientWidth || 0; H = cv.clientHeight || 0; cv.width = W * dpr; cv.height = H * dpr }
function fit() { if (!M) return; let x0 = 1e9, x1 = -1e9, y0 = 1e9, y1 = -1e9; for (const n of M.N) { x0 = Math.min(x0, n.x); x1 = Math.max(x1, n.x); y0 = Math.min(y0, n.y); y1 = Math.max(y1, n.y) }
- const tall = TALL; to = tall ? (() => { const z = Math.min(1.2, W / (2 * Math.max(LOBE.L.rx, LOBE.R.rx) + 60), (H - 44) / (LOBE.R.y + LOBE.R.ry - LOBE.L.y + LOBE.L.ry + 90)) || 1; return { x: 0, y: (LOBE.L.y - LOBE.L.ry + LOBE.R.y + LOBE.R.ry) / 2 - 22 / z, z } })() : { x: (LOBE.L.x - LOBE.L.rx + LOBE.R.x + LOBE.R.rx) / 2, y: 0, z: Math.min(W / (4 * MAXR.rx + 2 * GAP + 40), H / (2 * MAXR.ry + 40)) || 1 } }
+ const tall = TALL; to = tall && LOBE.L.y === 0 ? (() => { const z = Math.min(1.2, (W - 20) / (LOBE.R.x + LOBE.R.rx - LOBE.L.x + LOBE.L.rx + 120), (H - 44) / (2 * Math.max(LOBE.L.ry, LOBE.R.ry) + 90)) || 1; return { x: (LOBE.L.x - LOBE.L.rx + LOBE.R.x + LOBE.R.rx) / 2, y: -22 / z, z } })() : tall ? (() => { const z = Math.min(1.2, W / (2 * Math.max(LOBE.L.rx, LOBE.R.rx) + 60), (H - 44) / (LOBE.R.y + LOBE.R.ry - LOBE.L.y + LOBE.L.ry + 90)) || 1; return { x: 0, y: (LOBE.L.y - LOBE.L.ry + LOBE.R.y + LOBE.R.ry) / 2 - 22 / z, z } })() : { x: (LOBE.L.x - LOBE.L.rx + LOBE.R.x + LOBE.R.rx) / 2, y: 0, z: Math.min(W / (4 * MAXR.rx + 2 * GAP + 40), H / (2 * MAXR.ry + 40)) || 1 } }
function rest() { const C = M.C, F = M.F;
return (C.length ? "<b>" + C.length + " classes</b> and their links" + (F.length ? ", with the " + F.length + " largest features around them" : "") + ". Name one in your thought, or tap a dot."
…
if (n !== was || n) go(n);
}
- /* stand up or lie down when the layout says so (a fold, a resize): a fresh layout of the same data */ const flip = () => { if (!cv || !cv.isConnected || !D || !W || !H || tallOf(cv) === TALL) return false; TALL = tallOf(cv); M = lobes(graph(D.C || [], D.F || []), D.T || []); M.partial = !!D.partial; focus = null; trail = []; go(null); cam = { ...to }; follow(text); return true };
+ /* stand up or lie down when the layout says so (a fold, a resize): a fresh layout of the same data */ const redo = () => { if (!D) return; M = lobes(graph(D.C || [], D.F || []), D.T || []); M.partial = !!D.partial; focus = null; trail = []; go(null); cam = { ...to }; follow(text) };
+ const flip = () => { if (!cv || !cv.isConnected || !D || !W || !H || tallOf(cv) === TALL) return false; TALL = tallOf(cv); redo(); return true };
const ready = (async () => {
try { const d = await (o.load ? o.load() : { C: [], F: [] }); D = d; TALL = tallOf(cv); M = lobes(graph(d.C || [], d.F || []), d.T || []); M.partial = !!d.partial } catch (e) { M = graph([], []); M.partial = true }
…
})();
return {
- relayout: () => { size(); flip() }, ready, follow,
+ relayout: () => { size(); flip() }, restack: () => { size(); TALL = tallOf(cv); redo() }, ready, follow,
/** while listening: the edge glows */
listening(v) { on = !!v; el.classList && el.classList[on ? "add" : "remove"]("th-listening"); if (!on) this.level(0) },
…
${model.map(mnode).join("")}${tp.map(node).join("")}<g class="fo-relgs">${rel.map(relNode).join("")}</g>
${V.sent?"":`<g class="fo-lplus" data-plus="model" role="button" tabindex="0" aria-label="Add a class to the model"><circle cx="${LOBES.model.x-60}" cy="${LOBES.model.y+110}" r="15"/><text x="${LOBES.model.x-60}" y="${LOBES.model.y+116}">+</text></g><g class="fo-lplus" data-plus="topic" role="button" tabindex="0" aria-label="Add a topic"><circle cx="${LOBES.topic.x+LOBE_R.rx-26}" cy="${LOBES.topic.y}" r="15"/><text x="${LOBES.topic.x+LOBE_R.rx-26}" y="${LOBES.topic.y+6}">+</text></g>`}${ring}</svg>`}
-function mapSvgA(){ATALL=aTallNow();if(ATALL)return mapSvgAT();
+function mapSvgA3(){const K=1.75,CORE={x:360,y:350},LOBES={model:{x:195,y:350},topic:{x:525,y:350}},LOBE_R={rx:165,ry:Math.round(150*K)};
+ const tp=topics().slice(0,MAX_DRAWN),model=(V.model||[]).slice(0,MAX_MODEL),rel=(V.related||[]).slice(0,Math.max(0,MAX_DRAWN-tp.length)).slice(0,4);
+ const L0=layout(model.length,tp.length),tr=p=>({...p,y:Math.round(350+(p.y-200)*K)}),L={model:L0.model.map(tr),topic:L0.topic.map(tr)},pos=new Map();tp.forEach((m,i)=>pos.set(m.n,L.topic[i]));
+ const selN=view.k==="topic"?view.n:view.k==="notes"?0:-1,many=tp.length>6;
+ const spokes=tp.map(m=>{const p=pos.get(m.n);return `<path class="fo-spoke${m.n===selN?" on":""}" data-n="${m.n}" d="M${CORE.x} ${CORE.y} L${p.x} ${p.y}"/>`}).join("");
+ const links=(V.links||[]).filter(([a,b])=>pos.has(a)&&pos.has(b)).map(([a,b])=>{const p=pos.get(a),q=pos.get(b),mx=Math.round((p.x+q.x)/2+(p.y-q.y)*.18),my=Math.round((p.y+q.y)/2+(q.x-p.x)*.18);
+ return `<path class="fo-link" data-a="${a}" data-b="${b}" d="M${p.x} ${p.y} Q${mx} ${my} ${q.x} ${q.y}"/>`}).join("");
+ const node=m=>{const p=pos.get(m.n),sel=m.n===selN,name=cut(label(m),many?13:22)+(m.project!==V.project?" · "+m.project:""),s=sel?34:30;
+ const ty=p.side?p.y+4:p.above?p.y-26:p.y+38,tx=p.side?p.x+24:p.x;
+ return `<g class="fo-node${sel?" sel":""}${m.missing?" gone":""}" data-n="${m.n}" data-kind="topic" role="button" tabindex="0" aria-label="Topic ${topics().indexOf(m)+1}: ${esc(label(m))}"><rect class="fo-shape" x="${p.x-s/2}" y="${p.y-s/2}" width="${s}" height="${s}" rx="7"/><text class="fo-num" x="${p.x}" y="${p.y+4}">${topics().indexOf(m)+1}</text><text class="fo-name${many?" sm":""}" x="${tx}" y="${ty}"${p.side?' style="text-anchor:start"':""}>${esc(name)}</text></g>`};
+ const RS=[[430,200],[560,330],[430,470],[560,600]],relNode=(r,i)=>{const p={x:RS[i][0],y:RS[i][1]};return `<g class="fo-relg${view.k==="rel"&&view.i===i?" on":""}" data-rel="${i}" role="button" tabindex="0" aria-label="Related topic: ${esc(r.name)}"><circle cx="${p.x}" cy="${p.y}" r="9"/><text x="${p.x+14}" y="${p.y+4}">${esc(cut(r.name,18))} · + Add</text></g>`};
+ const mi=model.length>6;
+ const mSpokes=model.map((x,i)=>`<path class="fo-mspoke" data-m="${i}" d="M${CORE.x} ${CORE.y} L${L.model[i].x} ${L.model[i].y}"/>`).join("");
+ const mLinks=model.flatMap((x,i)=>x.kind==="concept"?[]:(x.topics||[]).filter(n=>pos.has(n)).map(n=>{const p=L.model[i],q=pos.get(n);return `<path class="fo-mlink" data-m="${i}" data-b="${n}" d="M${p.x} ${p.y} Q${CORE.x} ${Math.round((p.y+q.y)/2+(p.y<CORE.y?-40:40))} ${q.x} ${q.y}"/>`})).join("");
+ const mnode=(x,i)=>{const p=L.model[i],ty=p.side?p.y+4:p.above?p.y-18:p.y+26,tx=p.side?p.x-16:p.x;
+ return `<g class="fo-mnode${view.k==="model"&&view.i===i?" sel":""}" data-m="${i}" data-mk="${esc(x.kind)}" role="button" tabindex="0" aria-label="${esc(x.kind)}: ${esc(x.label)}"><circle class="fo-mshape" cx="${p.x}" cy="${p.y}" r="10"/><text class="fo-name${mi?" sm":""}" x="${tx}" y="${ty}"${p.side?' style="text-anchor:end"':""}>${esc(cut(x.label,mi?13:22))}</text></g>`};
+ const capY=16,cur=selN===0?CORE:pos.get(selN);
+ const ring=cur?`<circle id="fo-ring" cx="${cur.x}" cy="${cur.y}" r="${selN===0?46:27}"/>`:"";
+ const lines=coreLines(V.title);
+ return `<svg id="fo-map" class="fo-tall" viewBox="0 0 720 700" role="group" aria-label="The Focus and its members">
+<ellipse class="fo-lobe" cx="${LOBES.model.x}" cy="${LOBES.model.y}" rx="${LOBE_R.rx}" ry="${LOBE_R.ry}"/><ellipse class="fo-lobe" cx="${LOBES.topic.x}" cy="${LOBES.topic.y}" rx="${LOBE_R.rx}" ry="${LOBE_R.ry}"/>
+<text class="fo-cap" x="${LOBES.model.x}" y="${capY}">MODEL</text><text class="fo-cap" x="${LOBES.topic.x}" y="${capY}">TOPICS</text>
+${mSpokes}${spokes}${links}${mLinks}
+<g class="fo-core${selN===0?" sel":""}" data-n="0" role="button" tabindex="0" aria-label="Focus notes"><circle cx="${CORE.x}" cy="${CORE.y}" r="38"/>${lines.map((l,i)=>`<text x="${CORE.x}" y="${CORE.y+4+(i-(lines.length-1)/2)*16}">${esc(l)}</text>`).join("")}</g>
+${model.map(mnode).join("")}${tp.map(node).join("")}<g class="fo-relgs">${rel.map(relNode).join("")}</g>
+${V.sent?"":`<g class="fo-lplus" data-plus="model" role="button" tabindex="0" aria-label="Add a class to the model"><circle cx="${LOBES.model.x-LOBE_R.rx+26}" cy="${LOBES.model.y}" r="15"/><text x="${LOBES.model.x-LOBE_R.rx+26}" y="${LOBES.model.y+6}">+</text></g><g class="fo-lplus" data-plus="topic" role="button" tabindex="0" aria-label="Add a topic"><circle cx="${LOBES.topic.x+LOBE_R.rx-26}" cy="${LOBES.topic.y}" r="15"/><text x="${LOBES.topic.x+LOBE_R.rx-26}" y="${LOBES.topic.y+6}">+</text></g>`}${ring}</svg>`}
+let ORI=(()=>{try{return localStorage.getItem("fo-orient")==="side"?"side":"stack"}catch(_){return "stack"}})();window.FOORI=ORI;
+function mapSvgA(){ATALL=aTallNow();if(ATALL)return ORI==="side"?mapSvgA3():mapSvgAT();
const tp=topics().slice(0,MAX_DRAWN),model=(V.model||[]).slice(0,MAX_MODEL),rel=(V.related||[]).slice(0,Math.max(0,MAX_DRAWN-tp.length)).slice(0,4);
const L=layout(model.length,tp.length),pos=new Map();tp.forEach((m,i)=>pos.set(m.n,L.topic[i]));
…
const capModel=i=>{const x=V.model[i],ts=(x.topics||[]).map(n=>V.members.find(m=>m.n===n)).filter(Boolean);return `<a href="#" class="th-bn" data-m="${i}"><b>${esc(x.label)}</b></a> · ${x.kind==="concept"?"class":esc(x.kind)}`+(ts.length?" · in "+ts.map(m=>`<a href="#" class="th-bn th-brel" data-n="${m.n}">${esc(label(m))}</a>`).join(" · "):"")};
const capTopic=n=>{const m=V.members.find(x=>x.n===n),ks=(V.model||[]).map((x,i)=>[x,i]).filter(([x])=>(x.topics||[]).includes(n));return `<a href="#" class="th-bn" data-n="${n}"><b>${esc(label(m))}</b></a>`+(ks.length?" · names "+ks.map(([x,i])=>`<a href="#" class="th-bn th-brel" data-m="${i}">${esc(cut(x.label,30))}</a>`).join(" · "):"")};
-const cap=h=>{const c=$("fo-bcap");if(c)c.innerHTML=h};
+const cap=h=>{for(const id of ["fo-bcap","fo-capx"]){const c=$(id);if(c)c.innerHTML=h}};
function follow(t){if(!V)return;t=String(t).slice(-160);let best=null,at=-1;
(V.model||[]).forEach((x,i)=>{const p=t.toLowerCase().lastIndexOf(x.label.toLowerCase());if(p>at){at=p;best=()=>cap(capModel(i))}});
…
return `<section id="fo-ed" class="${ed.k==="notes"?"fo-edfoc":""}" data-kind="${ed.k==="notes"?"focus":"thought"}"><div class="fo-edhead"><span class="fo-kind">${ek}</span><span class="fo-saved" id="fo-saved" aria-live="polite"></span>${ro||ed.k==="new"?"":`<span class="fo-bar"><button type="button" class="btn sm primary" data-act="new">+ New thought</button> <button type="button" class="btn sm" data-plus="thought">Find a thought</button> <a href="#" class="small" data-go="/search?focus=${encodeURIComponent(KEY)}">Search everything to add →</a></span>`}</div>
-${ro?"":tools("fo-text")}<div id="fo-text" data-ph="${ed.k==="new"?"A thought on this subject…":ed.k==="thought"?"This thought":"What this Focus is about"}" aria-label="${ed.k==="notes"?"Focus notes":"Thought"}"></div><div class="fo-acts">${ed.k==="thought"?`<button type="button" class="btn sm primary" id="fo-edsave">Save</button><button type="button" class="btn sm" id="fo-edsend">Send ▾</button>${V.sent?"":`<button type="button" class="btn sm" id="fo-edout">Take out of focus</button><button type="button" class="btn sm" id="fo-edarch">Archive</button><button type="button" class="btn sm fo-danger" id="fo-eddel">Delete…</button>`}`:ed.k==="new"?`<button type="button" class="btn sm primary" id="fo-newsave">Save</button><button type="button" class="btn sm" id="fo-edback">Cancel</button>`:ro?"":`<button type="button" class="btn sm primary" id="fo-notesave">Save</button>${V.sent?`<span class="fo-sentb">✓ Sent</span>`:`<button type="button" class="btn sm" id="fo-send"${V.members.length?"":" disabled"} title="Send this Focus to your designer">Send → @designer</button>`}<button type="button" class="btn sm" id="fo-archfoc" title="Archive this Focus and all its thoughts">Archive focus</button><button type="button" class="btn sm" id="fo-dissolve" title="Every thought goes back to Thoughts; your notes go into the first one">${thoughts().length===1&&!topics().length?"Back to a thought":"Dissolve focus"}</button><span class="small fo-privl">This thought defines the Focus: it can't be archived or deleted. Dissolve focus ends it.</span>`}${pnav()}</div></section>`}
+${ro?"":tools("fo-text")}<div id="fo-text" data-ph="${ed.k==="new"?"A thought on this subject…":ed.k==="thought"?"This thought":"What this Focus is about"}" aria-label="${ed.k==="notes"?"Focus notes":"Thought"}"></div><div class="fo-acts">${ed.k==="thought"?`<button type="button" class="btn sm primary" id="fo-edsave">Save</button><button type="button" class="btn sm" id="fo-edsend">Send ▾</button>${V.sent?"":`<button type="button" class="btn sm" id="fo-edout">Take out of focus</button><button type="button" class="btn sm" id="fo-edarch">Archive</button><button type="button" class="btn sm fo-danger" id="fo-eddel">Delete…</button>`}`:ed.k==="new"?`<button type="button" class="btn sm primary" id="fo-newsave">Save</button><button type="button" class="btn sm" id="fo-edback">Cancel</button>`:ro?"":`<button type="button" class="btn sm primary" id="fo-notesave">Save</button>${V.sent?`<span class="fo-sentb">✓ Sent</span>`:`<button type="button" class="btn sm" id="fo-send"${V.members.length?"":" disabled"} title="Send this Focus to your designer">Send → @designer</button>`}<button type="button" class="btn sm" id="fo-archfoc" title="Archive this Focus and all its thoughts">Archive focus</button><button type="button" class="btn sm" id="fo-dissolve" title="Every thought goes back to Thoughts; your notes go into the first one">${thoughts().length===1&&!topics().length?"Back to a thought":"Dissolve focus"}</button><span class="small fo-privl fo-capx th-bcap" id="fo-capx" aria-live="polite"></span>`}${pnav()}</div></section>`}
// ---- the cards: its thoughts; tick to take out; open one to edit it, Save or Thread now ----
…
let SPLIT=(()=>{try{return localStorage.getItem("fo-split")||"both"}catch(_){return "both"}})();
function setSplit(s){SPLIT=s;try{localStorage.setItem("fo-split",s)}catch(_){}const el=$("fo-split");if(el)el.dataset.show=s;requestAnimationFrame(()=>{if(typeof reA==="function")reA();if(BR&&BR.relayout)BR.relayout()})}
+document.addEventListener("click",e=>{const o=e.target.closest&&e.target.closest("#fo-orient");if(!o)return;e.preventDefault();ORI=ORI==="side"?"stack":"side";window.FOORI=ORI;try{localStorage.setItem("fo-orient",ORI)}catch(_){}
+ const lab=ORI==="side"?"Stack the lobes, one above the other":"Lobes side by side, stretched tall";o.title=lab;o.setAttribute("aria-label",lab);o.setAttribute("aria-pressed",String(ORI==="side"));
+ if(MODE==="A"){const m=$("fo-map");if(m)m.outerHTML=mapSvgA()}else if(BR&&BR.restack)BR.restack()});
document.addEventListener("click",e=>{const b=e.target.closest&&e.target.closest(".fo-split .fold [data-fold]");if(!b)return;
setSplit(b.dataset.fold==="l"?(SPLIT==="left"?"both":"right"):(SPLIT==="right"?"both":"left"))});
…
let first=false;try{first=!localStorage.getItem("fo-seen");if(first)localStorage.setItem("fo-seen","1")}catch(_){}
/* the map is drawn from the first open, even with no members: the core, the model lobe from the notes, the grey related topics (razie, chat 6acae83d, 2026-10-11) */ const small=false,empty=V.members.length<1&&!String(V.notes||"").trim();
- main.innerHTML=`<p class="small fo-back"><a href="#" data-go="/thoughts">‹ Thoughts</a></p>${head()}
+ main.innerHTML=`${head()}
${first?`<div class="fo-first" id="fo-first">New here: tap a node on the map to read it on the right, ⌄ folds the map to one line, and ? by the title says more. <button type="button" class="btn sm" id="fo-firstok">Got it</button></div>`:""}
<div class="fo-split" id="fo-split" data-show="${SPLIT}"><div class="fo-lp">
-${small?`<div id="fo-empty"><p>Nothing here yet. Add thoughts and topics on this subject, then work them down.</p></div>`:`<div class="fo-brain${folded?" fo-bfolded":""}" id="fo-brain"><button type="button" class="ibtn th-bfold fo-bfold" id="fo-bfold" aria-expanded="${!folded}" title="${folded?"Open the map":"Fold the map"}">${folded?"⌃":"⌄"}</button>${mapSvg()}<p class="th-bcap fo-bcap" id="fo-bcap" aria-live="polite"></p></div>
+${small?`<div id="fo-empty"><p>Nothing here yet. Add thoughts and topics on this subject, then work them down.</p></div>`:`<div class="fo-brain${folded?" fo-bfolded":""}" id="fo-brain"><button type="button" class="ibtn fo-orient" id="fo-orient" title="${ORI==="side"?"Stack the lobes, one above the other":"Lobes side by side, stretched tall"}" aria-label="${ORI==="side"?"Stack the lobes, one above the other":"Lobes side by side, stretched tall"}" aria-pressed="${ORI==="side"}">☯</button><button type="button" class="ibtn th-bfold fo-bfold" id="fo-bfold" aria-expanded="${!folded}" title="${folded?"Open the map":"Fold the map"}">${folded?"⌃":"⌄"}</button>${mapSvg()}<p class="th-bcap fo-bcap" id="fo-bcap" aria-live="polite"></p></div>
${(V.related||[]).length?`<p class="small" style="text-align:right;margin:4px 0 8px">Related topics in grey; Add makes them members</p>`:""}`}
${empty?`<div id="fo-empty"><p class="small">Nothing here yet. Add thoughts and topics on this subject, then work them down.</p></div>`:""}</div>
…
await Promise.all([...j.members.filter(m=>m.kind==="thought"&&!m.missing).map(m=>api("GET","/api/v2/thoughts/"+enc(m.name)+pq(m.project||PROJ)).then(r=>{m.tags=r.tags||[];m.created=r.created;if(r.text!=null)m.text=r.text;if(r.ver!=null)m.ver=r.ver}).catch(()=>{})),
api("GET","/api/v2/thoughts/"+enc(NAME)+pq(PROJ)).then(r=>{j.selfTags=r.tags||[];j.created=r.created}).catch(()=>{})]);V=j;document.title="Focus - "+(j.title||"");if(!keep||!dirty)render()}
-catch(e){$("fo-main").innerHTML=`<p class="small fo-back"><a href="#" data-go="/thoughts">‹ Thoughts</a></p><h1>Focus</h1><p class="bad">Can't open this Focus: ${esc(why(e))}</p>`}}
+catch(e){$("fo-main").innerHTML=`<h1>Focus</h1><p class="bad">Can't open this Focus: ${esc(why(e))}</p>`}}
// ---- saving ----
…