Ground-truth extraction of every questionnaire preview except type scale and iconography: 75 cells, one per valid surface and option combination. The control bar repaints every cell through the picker\'s own custom-property machinery; hover a Motion cell to restart its scene from the first frame.
',
+ controls,
+ sectionsHtml,
+ '
+
+
+
+
')) {
+ throw new Error('Source gallery has no closing body tag');
+}
+
+for (const { cell } of EXPERIMENTS) {
+ if (!html.includes('data-cell="' + cell + '"')) {
+ throw new Error('Target cell not found: ' + cell);
+ }
+}
+
+let replica = html.replace('', experiment + '\n');
+replica = replica.replace(
+ '
Questionnaire previews, ground-truth gallery
',
+ '
Questionnaire previews (improved experiment)
',
+);
+replica = replica.replace(
+ '
Questionnaire previews
',
+ '
Questionnaire previews (improved experiment)
',
+);
+replica = replica.replace(
+ /
Ground-truth extraction[\s\S]*?<\/p>/,
+ '
Replica of the ground-truth gallery with color-only experiments on '
+ + 'Landing page + Drenched, '
+ + 'App UI + Committed, and '
+ + 'App UI + Full palette. '
+ + 'Baseline: previews-gallery.html. '
+ + 'Restrained is unchanged (reference).
',
+);
+
+await writeFile(OUTPUT, replica);
+console.log('wrote previews-gallery-improved.html (' + (replica.length / 1024 / 1024).toFixed(1) + ' MB)');
diff --git a/tmp/questionaire/build-gallery.mjs b/tmp/questionaire/build-gallery.mjs
new file mode 100644
index 000000000..fb4f97816
--- /dev/null
+++ b/tmp/questionaire/build-gallery.mjs
@@ -0,0 +1,545 @@
+
+/* Plan 6 builder. Reads capture.json and writes previews-gallery.html: one
+ self-contained page with a sticky control bar (palette presets 1-4, per
+ role custom color inputs, font presets 1-4, jump navigation) over 86 cells,
+ each an iframe rebuilt from the captured ground truth. Zero runtime network
+ requests: all CSS resources are data URIs and every captured is
+ neutralized. */
+
+import { readFile, writeFile } from 'node:fs/promises';
+import { CELL_LIST, FONT_PRESETS, PALETTE_PRESETS, SECTIONS } from './gallery-cells.mjs';
+
+const OUT_DIR = '/Users/abdulwahab/impeccable/tmp/questionaire';
+const BLANK_GIF = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
+
+const neutralizeImages = (html) =>
+ html.replace(/(]*?\bsrc=")[^"]*(")/g, '$1' + BLANK_GIF + '$2');
+
+/* One external form-state block per cell: every radio group the picker CSS
+ can read, except the group whose radios already live inside the captured
+ section markup, plus the committed palette fields. surface-modes checkboxes
+ are added except on screen 01b, whose section carries its own. */
+function externalInputs(cell) {
+ const parts = [];
+ if (cell.screen !== '01b') {
+ for (const s of ['persuade', 'operate', 'read', 'experience']) {
+ parts.push('');
+ }
+ }
+ parts.push('');
+ for (const role of ['primary', 'secondary', 'tertiary', 'neutral']) {
+ parts.push('');
+ }
+ const sectionGroup = SECTIONS.find((s) => s.id === cell.section).group;
+ for (const [group, value] of Object.entries(cell.formState)) {
+ if (group === sectionGroup) continue;
+ parts.push('');
+ }
+ return parts.join('');
+}
+
+/* The standalone document each cell iframe runs. Same placeholder scheme as
+ tmp/motion-previews-premium.html. */
+const IFRAME_TEMPLATE = [
+ '',
+ '',
+ '