diff --git a/web/agent.js b/web/agent.js
index 952a908..dcf2af3 100644
--- a/web/agent.js
+++ b/web/agent.js
@@ -486,7 +486,7 @@
const j = await r.json().catch(() => null);
if (!r.ok) { status.textContent = (j && j.detail) || `accept failed (${r.status})`; status.className = 'small agent-line-no'; btn.disabled = false; return; }
host.innerHTML = '';
- status.textContent = `Accepted — saved to data/protocols/${j.protocol_id || pid}.json. An accepted protocol fills the sandbox form step by step, each step still confirmed.`;
+ status.textContent = `Accepted — saved to data/protocols/${j.protocol_id || pid}.json. An accepted protocol fills the Explore form step by step, each step still confirmed.`;
status.className = 'small agent-line-ok';
} catch (e) {
status.textContent = 'the accept request failed: ' + (e && e.message || e);
diff --git a/web/figures.js b/web/figures.js
index 3c96394..6d1d504 100644
--- a/web/figures.js
+++ b/web/figures.js
@@ -68,7 +68,7 @@
}
// ---------------------------------------------------------------- n · T · tau_E
- const PRESET = { // ITER: this app's sandbox baseline (physics.simulate). MAST: shot #30166 at t = 0.27 s (n̄e, Te0, W/P_loss).
+ const PRESET = { // ITER: this app's Explore baseline (physics.simulate). MAST: shot #30166 at t = 0.27 s (n̄e, Te0, W/P_loss).
iter: { n: 1.015, T: 7.6, tau: 3.81, label: 'ITER: this app’s model, volume-average T' },
mast: { n: 0.386, T: 1.22, tau: 0.049, label: 'MAST #30166 at 0.27 s: measured, core electron T' },
};
diff --git a/web/guide.js b/web/guide.js
index 6caa016..9a88d75 100644
--- a/web/guide.js
+++ b/web/guide.js
@@ -72,7 +72,7 @@
['mouseover', 'focusin'].forEach((ev) => document.addEventListener(ev, (e) => { const t = e.target.closest && e.target.closest('.term'); if (t) showTip(t); }));
['mouseout', 'focusout'].forEach((ev) => document.addEventListener(ev, (e) => { if (e.target.closest && e.target.closest('.term')) tip.hidden = true; }));
- // Lab-mode help, from the same glossary: tooltips on panel labels, one line under each sandbox slider, "?" back to a lesson.
+ // Lab-mode help, from the same glossary: tooltips on panel labels, one line under each Explore slider, "?" back to a lesson.
function labHelp() {
Object.values(G).forEach((g) => (g.on || []).forEach((name) => { const el = target(name); if (el && !el.title) el.title = `${g.name}: ${g.def}`; }));
const KNOB = { Ip: 'ip', B: 'bt', n: 'density', P_aux: 'heating', H: 'h-factor', Zeff: 'zeff' };
@@ -107,7 +107,7 @@
const keep = new Set(); // the lit elements and every ancestor up to the tab
inTab.forEach((el) => { for (let n = el; n && n !== section.parentNode; n = n.parentNode) keep.add(n); });
const dock = parseFloat(getComputedStyle(document.body).getPropertyValue('--dock')) || 0, r = inTab[0].getBoundingClientRect();
- if (r.top < 0 || r.top > innerHeight - dock - 80) inTab[0].scrollIntoView({ block: 'start', behavior: 'smooth' }); // its top is off screen (pages that scroll)
+ if (r.top < 0 || r.top > innerHeight - dock - 80) inTab[0].scrollIntoView({ block: 'start', behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' }); // its top is off screen (pages that scroll)
keep.forEach((node) => {
if (inTab.includes(node)) return; // a lit element keeps all of its children
[...node.children].forEach((c) => {
diff --git a/web/lessons.json b/web/lessons.json
index 33afc8a..20560af 100644
--- a/web/lessons.json
+++ b/web/lessons.json
@@ -185,7 +185,7 @@
{ "id": "5.5", "ch": 5, "title": "What the comparison shows",
"tab": "replay", "shot": 30166, "slice": { "t": 0.27 }, "focus": ["insight", "logbook"],
- "body": "Two readings of the same shot, side by side. On the left, what the twin works out from the data: confinement relative to each law, the closest approach to a limit and when, and how the learned correction did on this shot. On the right, what the physicist in the control room wrote. When they agree, you can trust both a little more. When they differ, you have found something worth a closer look." },
+ "body": "Two readings of the same shot, side by side. On the left, what the twin works out from the data: confinement relative to each law, which limit the shot came closest to, and how the learned correction did on this shot. On the right, what the physicist in the control room wrote. When they agree, you can trust both a little more. When they differ, you have found something worth a closer look." },
{ "id": "6.1", "ch": 6, "title": "Same recipe, more power",
"tab": "replay", "shot": 30192, "slice": 0, "view": "cutaway", "highlight": null, "focus": ["bar", "strip-p", "strip-w", "strip-lim"],
diff --git a/web/replay.js b/web/replay.js
index 4543a19..8552f51 100644
--- a/web/replay.js
+++ b/web/replay.js
@@ -583,9 +583,11 @@
if (q.H98_median !== null)
out.push(`Over ${q.n_steady} near-steady slices the measured confinement time is ${fmt(q.H98_median)}× the IPB98(y,2) H-mode law and ${fmt(q.H89_median)}× the ITER89-P L-mode law.`);
const name = LIMIT_LABEL[q.peak_limit] || q.peak_limit;
+ // Limit proximity is drawn twice already — gauges for the instant, the limits strip for the run —
+ // so the prose names the limit and points at them instead of restating the same numbers a third time.
out.push(q.peak_limit_fraction >= 1
- ? `${name} limit exceeded: ${fmt(q.peak_limit_fraction)}× at t = ${fmt(q.peak_limit_t_s, 3)} s.`
- : `Closest approach to a limit: ${name} at ${fmt(q.peak_limit_fraction)}× (t = ${fmt(q.peak_limit_t_s, 3)} s).`);
+ ? `The ${name} limit is crossed during this shot — its gauge reads red at the crossing, and the limits strip shows when.`
+ : `Closest approach to a limit: ${name}. Watch its gauge as you scrub, and read the approach off the limits strip.`);
// Only claimed for ohmic shots: with beams the energy holds fast ions and the late phase may well be H-mode.
const ohmic = (s.meta.heating || '').toLowerCase() === 'ohmic';
if (ohmic && q.P_loss_median_MW !== null && q.P_loss_median_MW > 2 * q.P_LH_median_MW && Math.abs(q.H89_median - 1) < 0.25)
@@ -1286,7 +1288,7 @@
if (!b) return;
if (b.dataset.act === 'cancel') return SL.cancelAll(); // fetched shots stay plotted
if (b.dataset.act === 'exit') return exitCompare();
- if (b.dataset.act === 'search') return $('rp-srch-panel').scrollIntoView({ behavior: 'smooth', block: 'start' });
+ if (b.dataset.act === 'search') return $('rp-srch-panel').scrollIntoView({ behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth', block: 'start' });
if (b.dataset.act === 'share') return void shareCompare(b);
if (b.dataset.act === 'retry') // "Retry failed": every failed chip refetches under the shared machine
return void cmp.ids.filter(x => SL.get(x) === 'error').forEach(id => cmpFetch(id, id === cmp.ref ? p => renderShot(p) : null));
diff --git a/web/style.css b/web/style.css
index ea31ff3..6e140ed 100644
--- a/web/style.css
+++ b/web/style.css
@@ -301,7 +301,6 @@ body.guided header { margin-left:calc(-1 * var(--rail)); }
.entry-action { margin:14px 0 0; }
.entry-action a { color:var(--accent); font:600 13px/1.4 system-ui, sans-serif; text-decoration:underline; }
.entry-action a:hover { color:var(--fg); }
-.entry-action a:focus-visible { outline:1px solid var(--accent); outline-offset:2px; }
/* command palette (web/palette.js): native