From c3fe6d8064c886ef9badb4b1a413f109de920113 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 28 Jul 2026 01:06:14 +0000
Subject: [PATCH] Sync generated provider output
---
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
.../impeccable/scripts/serve-question.mjs | 49 +++++++++++++------
15 files changed, 495 insertions(+), 240 deletions(-)
diff --git a/.agents/skills/impeccable/scripts/serve-question.mjs b/.agents/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.agents/skills/impeccable/scripts/serve-question.mjs
+++ b/.agents/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.claude/skills/impeccable/scripts/serve-question.mjs b/.claude/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.claude/skills/impeccable/scripts/serve-question.mjs
+++ b/.claude/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.cursor/skills/impeccable/scripts/serve-question.mjs b/.cursor/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.cursor/skills/impeccable/scripts/serve-question.mjs
+++ b/.cursor/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.gemini/skills/impeccable/scripts/serve-question.mjs b/.gemini/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.gemini/skills/impeccable/scripts/serve-question.mjs
+++ b/.gemini/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.github/skills/impeccable/scripts/serve-question.mjs b/.github/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.github/skills/impeccable/scripts/serve-question.mjs
+++ b/.github/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.grok/skills/impeccable/scripts/serve-question.mjs b/.grok/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.grok/skills/impeccable/scripts/serve-question.mjs
+++ b/.grok/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.kiro/skills/impeccable/scripts/serve-question.mjs b/.kiro/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.kiro/skills/impeccable/scripts/serve-question.mjs
+++ b/.kiro/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.opencode/skills/impeccable/scripts/serve-question.mjs b/.opencode/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.opencode/skills/impeccable/scripts/serve-question.mjs
+++ b/.opencode/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.pi/skills/impeccable/scripts/serve-question.mjs b/.pi/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.pi/skills/impeccable/scripts/serve-question.mjs
+++ b/.pi/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.qoder/skills/impeccable/scripts/serve-question.mjs b/.qoder/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.qoder/skills/impeccable/scripts/serve-question.mjs
+++ b/.qoder/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.rovodev/skills/impeccable/scripts/serve-question.mjs b/.rovodev/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.rovodev/skills/impeccable/scripts/serve-question.mjs
+++ b/.rovodev/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.trae-cn/skills/impeccable/scripts/serve-question.mjs b/.trae-cn/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.trae-cn/skills/impeccable/scripts/serve-question.mjs
+++ b/.trae-cn/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.trae/skills/impeccable/scripts/serve-question.mjs b/.trae/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.trae/skills/impeccable/scripts/serve-question.mjs
+++ b/.trae/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/.vibe/skills/impeccable/scripts/serve-question.mjs b/.vibe/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/.vibe/skills/impeccable/scripts/serve-question.mjs
+++ b/.vibe/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`
@@ -477,7 +487,7 @@ function page() {
.face.text-only .body { padding-top: 12px; }
.media { position: relative; width: 100%; aspect-ratio: 16/9; flex: none; }
.media img { width: 100%; height: 100%; object-fit: cover; display: block; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
- .face.back { background: var(--ks-lacquer-deep); }
+ .face.back { background: var(--ks-lacquer-raised); }
.back-bar { margin-top: auto; background: var(--ks-lacquer-raised); }
.hero-blank { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 40%, var(--ks-graphite-2) 50%, var(--ks-graphite) 60%); }
.back-bar { flex: none; flex-direction: row; align-items: center; justify-content: space-between; gap: .8rem; }
@@ -497,6 +507,13 @@ function page() {
.tag { font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ks-text-muted); border: 1px solid var(--ks-rule); border-radius: 4px; padding: 3px 7px; }
.fact { font-size: .8rem; color: var(--ks-text-muted); line-height: 1.45; }
.fact-label { display: inline-block; font-family: var(--ks-mono); font-size: .6rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ks-text-faint); margin-right: .55em; transform: translateY(-1px); }
+ .fact.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ /* The back is the full read: first viewport, the case, the whole risk, and
+ the board when the world has one. */
+ .back-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: 14px 14px 0; }
+ .media.back-media { aspect-ratio: 16/6; }
+ .media.back-media img { width: 100%; height: 100%; object-fit: cover; }
+ .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; }
/* Inspiration rides picture-in-picture: the catalog world explains where the
direction comes from without promising what the build will look like. */
/* Hovering the inspiration takes over the whole media region; the sketch is
diff --git a/plugin/skills/impeccable/scripts/serve-question.mjs b/plugin/skills/impeccable/scripts/serve-question.mjs
index 555219aba..195d9f0ab 100644
--- a/plugin/skills/impeccable/scripts/serve-question.mjs
+++ b/plugin/skills/impeccable/scripts/serve-question.mjs
@@ -151,7 +151,7 @@ if (hasFlag('schema')) {
canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', viewport: 'The arrangement a visitor expects, at full craft.', sketch: '.impeccable/sketches/canon.webp' },
steer: true,
}, null, 2));
- console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation.');
+ console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability.');
process.exit(0);
}
@@ -310,6 +310,8 @@ function page() {
// carries them; a plain body falls back to the prose block. Palette chips
// and material tags give a text-only direction an immediate identity that
// no generation luck can distort.
+ const fact = (label, value, cls = '') => value ? `
`);
@@ -321,31 +323,38 @@ function page() {
idBits.push(option.materials.slice(0, 4).map((m) => `${esc(m)}`).join(''));
}
if (idBits.length) rows.push(`
${idBits.join('')}
`);
- const fact = (label, value) => value ? `
${label}${esc(value)}
` : '';
- rows.push(fact('First viewport', option.viewport));
- rows.push(fact('The case', option.case));
- rows.push(fact('Risk', option.risk));
+ // The front carries only what the choice needs: thesis, identity, and the
+ // honest risk clamped to two lines. First viewport and the case read on
+ // the card's back; once the sketch lands, the first viewport is a picture.
+ rows.push(fact('Risk', option.risk, 'clamp'));
if (!option.thesis && option.body) rows.push(`
${esc(option.body)}
`);
- else if (option.body && option.thesis) rows.push(`
${esc(option.body)}
`);
+ else if (option.body && option.thesis && !hasBack(option)) rows.push(`