diff --git a/picker/scripts/design-context.js b/picker/scripts/design-context.js
index ed2bdac69..8f502a1a9 100644
--- a/picker/scripts/design-context.js
+++ b/picker/scripts/design-context.js
@@ -235,7 +235,7 @@ function paintCommitted(node) {
/* Clone one questionnaire drawing into a dcx frame. Inline styles travel
with the clone; ids do not (they would collide with the originals). */
-function proofHtml(source, { strategy = '', kind = 'board' } = {}) {
+function proofHtml(source, { strategy = '', kind = 'board', marks = null } = {}) {
if (!source) return '';
const clone = source.cloneNode(true);
clone.hidden = false;
@@ -249,6 +249,15 @@ function proofHtml(source, { strategy = '', kind = 'board' } = {}) {
wrap.className = `dcx-proof dcx-proof--${kind}`;
wrap.setAttribute('aria-hidden', 'true');
if (strategy) wrap.dataset.dcxStrategy = strategy;
+ /* Committed structural answers, restated on the frame. On the screens they
+ reach a carried artboard through checked inputs inside #picker-form,
+ which a clone in the document is outside of, so design-context.css
+ mirrors those bodies against these attributes instead. */
+ if (marks) {
+ for (const [key, value] of Object.entries(marks)) {
+ if (value) wrap.setAttribute(`data-dcx-${key}`, value);
+ }
+ }
paintCommitted(wrap);
wrap.appendChild(clone);
return wrap.outerHTML;
@@ -261,6 +270,22 @@ const surfaceBoard = (mode) => $(`[data-surface-stage] [data-surface="${mode}"]`
const surfaceTilePreview = (mode) => $(`input[name="surface-modes"][value="${mode}"]`)
?.closest('.picker-mode-tile')?.querySelector('.picker-preview');
+/* The boundaries screen keeps one carried ps artboard per surface, and by
+ that screen the drawing is the page as chosen so far. It is the clone
+ source for any proof that wants the whole material story on one page. */
+const carriedBoard = (mode) => $(`[data-question="boundaries"] .picker-artboard[data-surface="${mode}"]`);
+
+/* One surface's committed structural answers, as the data-dcx-* marks the
+ document stylesheet keys its mirrored bodies on. Layout is one answer for
+ the whole run, so every surface's frame carries the same value. */
+const materialMarks = (s, mode) => ({
+ surface: mode,
+ boundary: s.boundaries.find((entry) => entry.mode === mode)?.value || '',
+ corner: s.corners.find((entry) => entry.mode === mode)?.value || '',
+ depth: s.depth.find((entry) => entry.mode === mode)?.value || '',
+ layout: s.layout.value || '',
+});
+
/* One line per surface for the per-surface questions, marking whether the
user configured the surface or it kept the default for its kind. */
const surfaceDefs = (entries) => defs(entries.map((entry) => ({
@@ -510,15 +535,27 @@ function buildTypography(s, name) {
function buildIconography(s, name) {
const parts = [heading(6, 'Iconography', 'Icon library, license, where it lives.', name)];
if (s.icons.pack) {
- const sheet = proofHtml($('[data-icon-sheet]'), { kind: 'icons' });
+ /* The glyphs are fetched when screen 11 first opens, so a fast run can
+ reach the document before any landed, and a failed fetch leaves only
+ an error line. A sheet with no icon cells proves nothing about the
+ hand, so it is dropped rather than framed. */
+ const sheetSource = $('[data-icon-sheet]');
+ const sheet = sheetSource?.querySelector('.picker-icon-cell') ? proofHtml(sheetSource, { kind: 'icons' }) : '';
if (sheet) {
parts.push(block('The hand', sheet
+ note(`${escapeHtml(s.icons.pack)}’s canonical set, as the icons screen previewed it. One pack, one hand: no mixed sets.`)));
}
+ /* The chosen radio's own row copy: the character note and the grid,
+ stroke, and license summary the screen showed beside the name. Read
+ off the DOM rather than kept twice, the optionCopy convention. */
+ const packLabel = form.querySelector('input[name="icon-pack"]:checked')?.closest('label');
+ const packNote = packLabel?.querySelector('.picker-strategy-desc')?.textContent.trim() || '';
+ const packMeta = packLabel?.querySelector('.picker-icon-meta')?.textContent.trim() || '';
parts.push(block('Library', defs([
- { dt: 'Pack', dd: `${escapeHtml(s.icons.pack)}` },
- s.icons.license && { dt: 'License', dd: escapeHtml(s.icons.license) },
- s.icons.url && { dt: 'Home', dd: `${escapeHtml(s.icons.url)}` },
+ { dt: 'Pack', dd: `${escapeHtml(s.icons.pack)}${packMeta ? ` · ${escapeHtml(packMeta)}` : ''}` },
+ packNote ? { dt: 'Why this hand', dd: escapeHtml(packNote) } : null,
+ s.icons.license ? { dt: 'License', dd: escapeHtml(s.icons.license) } : null,
+ s.icons.url ? { dt: 'Home', dd: `${escapeHtml(s.icons.url)}` } : null,
].filter(Boolean)) + note('Chosen on the icons screen. The pack names the hand; stroke weight and metaphor rules resolve during implementation.')));
} else {
parts.push(block('Library', empty('No pack selected', 'The icons screen was not completed on this run.')));
@@ -538,12 +575,35 @@ const pickGrid = (entries) => `
+ ${proofHtml(board, {
+ strategy: s.strategy.find((entry) => entry.mode === surface.mode)?.value || '',
+ kind: 'board',
+ marks: materialMarks(s, surface.mode),
+ })}
+
+
${escapeHtml(surface.label)}
+
+ `;
+ }).filter(Boolean);
+ if (proofCards.length) {
+ parts.push(block('The page, as chosen', `
${proofCards.join('')}
`
+ + note('Each surface’s page anatomy carrying every structural answer below at once, the way the question screens accumulated them: color strategy, layout, boundaries, corners, depth.')));
+ }
/* Movement is only asked of the two surfaces that make a claim on attention,
so this list is one or two entries long and empty on a run of neither. An
empty one says the question was never put, which is a different thing from
a quiet answer and is worth saying out loud. */
if (s.motion.length) {
- parts.push(block('Motion per surface', surfaceDefs(s.motion)
+ parts.push(block('Motion per surface', pickGrid(s.motion)
+ note(interview.motionEnergy
? `The seed interview asked for
${escapeHtml(interview.motionEnergy)}; the motion screen answered it per surface above.`
: 'Asked of the landing page and the portfolio only. A tool and a document are moved through rather than watched, so their movement follows the interface rather than a house style.')));
@@ -571,22 +631,47 @@ function buildMaterial(s, name) {
function buildInterface(s, name) {
const parts = [heading(8, 'Interface', 'Per-surface decisions at a glance, component status.', name)];
- const ASPECTS = [['Color strategy', 'strategy'], ['Boundaries', 'boundaries'], ['Corners', 'corners'], ['Depth', 'depth']];
- parts.push(block('Decisions per surface', `
${s.surfaces.map((surface) => `
+ parts.push(block('Decisions per surface', `
${s.surfaces.map((surface) => {
+ const strategyValue = s.strategy.find((entry) => entry.mode === surface.mode)?.value || '';
+ const board = carriedBoard(surface.mode);
+ /* The matrix quotes every per-surface answer. A question this surface
+ was never asked reads Not asked rather than borrowing another
+ surface's answer; layout is one answer for the run and is quoted on
+ every card, the rule the Material page states. */
+ const rows = [
+ ['Color strategy', s.strategy.find((entry) => entry.mode === surface.mode)],
+ ['Boundaries', s.boundaries.find((entry) => entry.mode === surface.mode)],
+ ['Corners', s.corners.find((entry) => entry.mode === surface.mode)],
+ ['Depth', s.depth.find((entry) => entry.mode === surface.mode)],
+ ['Motion', s.motion.find((entry) => entry.mode === surface.mode)],
+ ['Layout', s.layout.value ? s.layout : undefined],
+ ];
+ return `
- ${proofHtml(surfaceBoard(surface.mode) || surfaceTilePreview(surface.mode), {
- strategy: s.strategy.find((entry) => entry.mode === surface.mode)?.value || '',
- kind: 'board',
- })}
+ ${board
+ ? proofHtml(board, { strategy: strategyValue, kind: 'board', marks: materialMarks(s, surface.mode) })
+ : proofHtml(surfaceBoard(surface.mode) || surfaceTilePreview(surface.mode), { strategy: strategyValue, kind: 'board' })}
${escapeHtml(surface.label)}
-
${ASPECTS.map(([label, key]) => {
- const entry = s[key].find((item) => item.mode === surface.mode);
- return `- ${escapeHtml(label)}
- ${escapeHtml(entry?.title || '—')}${entry && !entry.chosen ? ' default' : ''}
`;
- }).join('')}
+
${rows.map(([label, entry]) => `
+ - ${escapeHtml(label)}
- ${entry ? escapeHtml(entry.title) : 'Not asked'}${entry && !entry.chosen ? ' default' : ''}
`).join('')}
- `).join('')}
`
- + note('Each surface’s artboard in its own color strategy, with the four per-surface answers beneath it.')));
+ `;
+ }).join('')}
`
+ + note('Each surface’s anatomy carrying its committed answers, with every per-surface decision beneath it.')));
+ /* The run's own tokens in one place: what the seed DESIGN.md frontmatter
+ will carry. Every row omits when its screen was not completed. */
+ const kit = [
+ s.fonts.heading ? { dt: 'Faces', dd: `
${escapeHtml(s.fonts.heading)} for headings,
${escapeHtml(s.fonts.body)} for body` } : null,
+ s.scale.ratio ? { dt: 'Type scale', dd: `
${escapeHtml(s.scale.name)} · ratio
${s.scale.ratio.toFixed(3)} on a 16px base` } : null,
+ s.icons.pack ? { dt: 'Icons', dd: `
${escapeHtml(s.icons.pack)}${s.icons.license ? ` · ${escapeHtml(s.icons.license)}` : ''}` } : null,
+ s.palette.length ? { dt: 'Palette', dd: s.palette.map((entry) => `
${entry.hex}`).join(' · ') } : null,
+ s.layout.value ? { dt: 'Layout', dd: `
${escapeHtml(s.layout.title)}, one answer for the run` } : null,
+ ].filter(Boolean);
+ if (kit.length) {
+ parts.push(block('The kit', defs(kit)
+ + note('The tokens the seed DESIGN.md will carry in its frontmatter, gathered from their own pages in this document.')));
+ }
parts.push(block('Components', empty(
'No component library seeded yet',
'Components are documented on the first scan pass, once there is code to capture actual tokens and states from. Re-run
/impeccable document then.',
diff --git a/picker/styles/design-context.css b/picker/styles/design-context.css
index 5b7cf19ce..9fea15df9 100644
--- a/picker/styles/design-context.css
+++ b/picker/styles/design-context.css
@@ -1189,6 +1189,395 @@ body.dcx-open { overflow: hidden; background: linear-gradient(180deg, var(--ks-l
--pv-tertiary: var(--pkc-tertiary);
--pv-t-on-n: var(--pkc-t-on-p);
}
+/* ============================================================
+ Material proofs: the structural screens' own artboards, cloned.
+
+ A .dcx-proof--board can also frame a ps artboard clone (the anatomy
+ screens 07 to 10 draw on), not just a tile drawing. On screen those
+ boards take every committed answer through checked inputs inside
+ #picker-form, which a clone in the document is outside of, so the frame
+ restates each answer as a data-dcx-* attribute and the bodies below are
+ verbatim copies of the committed bodies the screens set. Change a body
+ here and in its named source together. The palette rides two ways: the
+ screens paint all thirteen --pv-* slots inline on every artboard they
+ showed (inline styles travel with a clone), and for a board whose screen
+ the run never opened, the frame's --pkc-* paint reaches the clone
+ through the same six-slot map the tile clones above use, with the
+ artboard's own class defaults covering the contrast slots. */
+.dcx-proof--board > .picker-artboard {
+ --pv-primary: var(--pkc-primary, var(--ks-champagne));
+ --pv-secondary: var(--pkc-secondary, var(--ks-patina));
+ --pv-tertiary: var(--pkc-tertiary, var(--ks-kinpaku));
+ --pv-neutral: var(--pkc-neutral, var(--ks-lacquer-raised));
+ --pv-n-ink: var(--pkc-n-ink, var(--ks-champagne));
+ --pv-p-ink: var(--pkc-p-ink, var(--ks-champagne));
+}
+
+/* Two rules the clone loses with its data-surface attribute (proofHtml
+ strips it): the landing board's right-gutter chrome, and the handset
+ print's freeform cut, which rides --pvs-drift and stays a rectangle
+ under the other layout answers. The frame's data-dcx-surface carries
+ the surface instead. Sources: picker/styles/screens/layout.css, the
+ [data-carry][data-surface="persuade"] rules. */
+.dcx-proof--board[data-dcx-surface="persuade"] > .picker-artboard :is(.ps-nav, .ps-editorial, .ps-footer) {
+ padding-right: var(--pvs-gutter-end);
+}
+
+.dcx-proof--board[data-dcx-surface="persuade"] > .picker-artboard .ps-phone-body > .ps-image {
+ clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--pvs-drift, 0) * 16%), 0 100%);
+}
+
+/* The strategy, restated for artboard clones: the [data-artboard] bodies
+ from picker.css. Restrained is the artboard's own defaults there and
+ stays bodiless here, exactly as the tile remaps above leave full
+ palette alone. */
+.dcx-proof--board[data-dcx-strategy="committed"] > .picker-artboard {
+ --pvs-headline: color-mix(in oklab, var(--pv-primary) 80%, var(--pv-strong));
+ --pvs-headline-text: var(--pv-p-on-n);
+ --pvs-eyebrow: var(--pv-primary);
+ --pvs-ghost: color-mix(in oklab, var(--pv-primary) 14%, var(--pv-neutral));
+ --pvs-ghost-edge: var(--pv-primary);
+ --pvs-image: var(--pv-primary);
+ --pvs-image-a: var(--pv-primary);
+ --pvs-image-b: color-mix(in oklab, var(--pv-primary) 72%, var(--pv-neutral));
+ --pvs-image-edge: var(--pv-primary);
+ --pvs-accent-a: var(--pv-primary);
+ --pvs-accent-b: color-mix(in oklab, var(--pv-primary) 70%, var(--pv-neutral));
+ --pvs-accent-c: var(--pv-primary);
+ --pvs-accent-d: color-mix(in oklab, var(--pv-primary) 18%, var(--pv-neutral));
+}
+
+.dcx-proof--board[data-dcx-strategy="full-palette"] > .picker-artboard {
+ --pvs-headline: color-mix(in oklab, var(--pv-primary) 80%, var(--pv-strong));
+ --pvs-headline-text: var(--pv-p-on-n);
+ --pvs-accent-text: var(--pv-t-on-n);
+ --pvs-eyebrow: var(--pv-tertiary);
+ --pvs-ghost: color-mix(in oklab, var(--pv-secondary) 18%, var(--pv-neutral));
+ --pvs-ghost-edge: var(--pv-secondary);
+ --pvs-image: var(--pv-primary);
+ --pvs-image-a: var(--pv-primary);
+ --pvs-image-b: color-mix(in oklab, var(--pv-secondary) 32%, var(--pv-primary));
+ --pvs-image-edge: var(--pv-primary);
+ --pvs-signal: var(--pv-tertiary);
+ --pvs-signal-ink: var(--pv-t-ink);
+ --pvs-signal-text: var(--pv-t-on-t);
+ --pvs-accent-a: var(--pv-secondary);
+ --pvs-accent-b: color-mix(in oklab, var(--pv-secondary) 42%, var(--pv-neutral));
+ --pvs-accent-c: var(--pv-primary);
+ --pvs-accent-d: var(--pv-tertiary);
+}
+
+.dcx-proof--board[data-dcx-strategy="drenched"] > .picker-artboard {
+ --pvs-ground: var(--pv-primary);
+ --pvs-ink: var(--pv-p-ink);
+ --pvs-surface: color-mix(in oklab, var(--pv-p-ink) 10%, var(--pv-primary));
+ --pvs-headline: var(--pv-p-ink);
+ --pvs-eyebrow: color-mix(in oklab, var(--pv-p-ink) 70%, var(--pv-primary));
+ --pvs-copy: color-mix(in oklab, var(--pv-p-ink) 55%, var(--pv-primary));
+ --pvs-title: var(--pv-p-ink);
+ --pvs-cta: var(--pv-p-ink);
+ --pvs-cta-ink: var(--pv-primary);
+ --pvs-cta-text: var(--pv-p-on-i);
+ --pvs-mark-text: var(--pv-p-ink);
+ --pvs-accent-text: var(--pv-t-on-p);
+ --pvs-copy-mix: 14%;
+ --pvs-bars-mix: 16%;
+ --pvs-ghost: color-mix(in oklab, var(--pv-p-ink) 22%, var(--pv-primary));
+ --pvs-ghost-edge: color-mix(in oklab, var(--pv-p-ink) 45%, var(--pv-primary));
+ --pvs-image: color-mix(in oklab, var(--pv-p-ink) 20%, var(--pv-primary));
+ --pvs-image-a: color-mix(in oklab, var(--pv-p-ink) 16%, var(--pv-primary));
+ --pvs-image-b: color-mix(in oklab, var(--pv-p-ink) 28%, var(--pv-primary));
+ --pvs-image-edge: color-mix(in oklab, var(--pv-p-ink) 40%, var(--pv-primary));
+ --pvs-signal: var(--pv-tertiary);
+ --pvs-signal-ink: var(--pv-t-ink);
+ --pvs-signal-text: var(--pv-t-on-t);
+ --pvs-bars: color-mix(in oklab, var(--pv-p-ink) 45%, var(--pv-primary));
+ --pvs-rule: color-mix(in oklab, var(--pv-p-ink) 28%, var(--pv-primary));
+ --pvs-accent-a: var(--pv-secondary);
+ --pvs-accent-b: var(--pv-tertiary);
+ --pvs-accent-c: color-mix(in oklab, var(--pv-p-ink) 30%, var(--pv-primary));
+ --pvs-accent-d: color-mix(in oklab, var(--pv-p-ink) 14%, var(--pv-primary));
+}
+
+/* Boundaries: the four committed bodies from styles/screens/boundaries.css. */
+.dcx-proof--board[data-dcx-boundary="open-space"] > .picker-artboard {
+ --pvs-divider-w: 0px;
+ --pvs-band: transparent;
+ --pvs-band-alt: transparent;
+ --pvs-band-top: transparent;
+ --pvs-panel: transparent;
+ --pvs-panel-edge-w: 0px;
+ --pvs-panel-pad: 0px;
+ --pvs-proof-inset: 0px;
+}
+
+.dcx-proof--board[data-dcx-boundary="thin-dividers"] > .picker-artboard {
+ --pvs-divider-w: 1px;
+ --pvs-divider-c: color-mix(in oklab, var(--pvs-ink) 24%, var(--pvs-ground));
+ --pvs-band: transparent;
+ --pvs-band-alt: transparent;
+ --pvs-band-top: transparent;
+ --pvs-panel: transparent;
+ --pvs-panel-edge-w: 0px;
+ --pvs-panel-pad: 0px;
+ --pvs-proof-inset: 0px;
+}
+
+.dcx-proof--board[data-dcx-boundary="surface-changes"] > .picker-artboard {
+ --pvs-divider-w: 0px;
+ --pvs-band: color-mix(in oklab, var(--pvs-ink) 7%, var(--pvs-ground));
+ --pvs-band-alt: color-mix(in oklab, var(--pvs-ink) 13%, var(--pvs-ground));
+ --pvs-band-top: color-mix(in oklab, var(--pvs-ink) 7%, var(--pvs-ground));
+ --pvs-panel: transparent;
+ --pvs-panel-edge-w: 0px;
+ --pvs-panel-pad: 0px;
+ --pvs-proof-inset: 0px;
+}
+
+.dcx-proof--board[data-dcx-boundary="cards-and-panels"] > .picker-artboard {
+ --pvs-divider-w: 0px;
+ --pvs-band: transparent;
+ --pvs-band-alt: color-mix(in oklab, var(--pvs-ink) 5%, var(--pvs-ground));
+ --pvs-band-top: transparent;
+ --pvs-panel: color-mix(in oklab, var(--pvs-ink) 5%, var(--pvs-ground));
+ --pvs-panel-edge: color-mix(in oklab, var(--pvs-ink) 20%, var(--pvs-ground));
+ --pvs-panel-edge-w: 1px;
+ --pvs-panel-pad: 5%;
+ --pvs-proof-inset: var(--pvs-gutter);
+}
+
+/* The open rail plate joins the card answer, as on the screen. */
+.dcx-proof--board[data-dcx-boundary="cards-and-panels"] > .picker-artboard .ps-ops-item {
+ background-color: var(--pvs-panel);
+}
+
+/* Corners: the four committed bodies from styles/screens/corners.css. */
+.dcx-proof--board[data-dcx-corner="sharp"] > .picker-artboard {
+ --pvs-radius-frame: 0px;
+ --pvs-radius-frame-phone: 0px;
+ --pvs-radius-surface: 0px;
+ --pvs-radius-control: 0px;
+ --pvs-radius-bar: 0px;
+}
+
+.dcx-proof--board[data-dcx-corner="slightly-soft"] > .picker-artboard {
+ --pvs-radius-frame: 4px;
+ --pvs-radius-frame-phone: 5px;
+ --pvs-radius-surface: 3px;
+ --pvs-radius-control: 3px;
+ --pvs-radius-bar: 2px;
+}
+
+.dcx-proof--board[data-dcx-corner="friendly"] > .picker-artboard {
+ --pvs-radius-frame: 7px;
+ --pvs-radius-frame-phone: 11px;
+ --pvs-radius-surface: 8px;
+ --pvs-radius-control: 8px;
+ --pvs-radius-bar: 3px;
+}
+
+.dcx-proof--board[data-dcx-corner="pill"] > .picker-artboard {
+ --pvs-radius-frame: 9px;
+ --pvs-radius-frame-phone: 15px;
+ --pvs-radius-surface: 12px;
+ --pvs-radius-control: 999px;
+ --pvs-radius-bar: 3px;
+}
+
+/* Depth: the three committed bodies from styles/screens/depth.css. */
+.dcx-proof--board[data-dcx-depth="flat"] > .picker-artboard {
+ --pvs-shadow-chrome: none;
+ --pvs-shadow-card: none;
+ --pvs-shadow-control: none;
+ --pvs-shadow-surface: none;
+}
+
+.dcx-proof--board[data-dcx-depth="soft-lift"] > .picker-artboard {
+ --pvs-shadow-chrome: 0 1px 2px rgb(0 0 0 / 0.1);
+ --pvs-shadow-card:
+ 0 1px 1px rgb(0 0 0 / 0.15),
+ 0 3px 7px rgb(0 0 0 / 0.14);
+ --pvs-shadow-control: 0 1px 2px rgb(0 0 0 / 0.24);
+ --pvs-shadow-surface: 0 2px 5px rgb(0 0 0 / 0.14);
+}
+
+.dcx-proof--board[data-dcx-depth="floating"] > .picker-artboard {
+ --pvs-shadow-chrome: 0 2px 6px rgb(0 0 0 / 0.16);
+ --pvs-shadow-card:
+ 0 2px 3px rgb(0 0 0 / 0.2),
+ 0 9px 18px rgb(0 0 0 / 0.24);
+ --pvs-shadow-control: 0 3px 6px rgb(0 0 0 / 0.32);
+ --pvs-shadow-surface: 0 6px 14px rgb(0 0 0 / 0.22);
+}
+
+/* Where a cast shadow is allowed to land, keyed to the boundary answer
+ exactly as depth.css keys it to the checked boundary input. */
+.dcx-proof--board[data-dcx-boundary="cards-and-panels"] > .picker-artboard :is(.ps-gallery-item, .ps-desktop > .ps-proof),
+.dcx-proof--board[data-dcx-boundary="surface-changes"] > .picker-artboard .ps-desktop > .ps-proof {
+ box-shadow: var(--pvs-shadow-card);
+}
+
+.dcx-proof--board:not([data-dcx-boundary="cards-and-panels"]) > .picker-artboard .ps-gallery-item {
+ box-shadow: none;
+}
+
+.dcx-proof--board:not([data-dcx-boundary="cards-and-panels"]) > .picker-artboard .ps-gallery-item > i {
+ box-shadow: var(--pvs-shadow-card);
+}
+
+.dcx-proof--board:not([data-dcx-boundary="surface-changes"]) > .picker-artboard :is(.ps-nav, .ps-phone-top) {
+ box-shadow: none;
+}
+
+.dcx-proof--board:not([data-dcx-boundary="surface-changes"]) > .picker-artboard :is(.ps-ops-rail, .ps-docs-rail) {
+ box-shadow: none;
+}
+
+.dcx-proof--board:not([data-dcx-boundary="surface-changes"]):not([data-dcx-boundary="cards-and-panels"]) > .picker-artboard .ps-ops-panel {
+ box-shadow: none;
+}
+
+.dcx-proof--board:not([data-dcx-boundary="cards-and-panels"]) > .picker-artboard :is(.ps-ops-chart, .ps-ops-table) {
+ box-shadow: none;
+}
+
+.dcx-proof--board:not([data-dcx-boundary="cards-and-panels"]) > .picker-artboard .ps-ops-plot > i {
+ box-shadow: var(--pvs-shadow-card);
+}
+
+.dcx-proof--board:not([data-dcx-boundary="surface-changes"]) > .picker-artboard .ps-docs-note {
+ box-shadow: none;
+}
+
+.dcx-proof--board[data-dcx-boundary="surface-changes"] > .picker-artboard .ps-index-row--flip {
+ box-shadow: var(--pvs-shadow-card);
+}
+
+/* Layout: the committed bodies from styles/screens/layout.css, the landing
+ measure and the region tracks folded into one body per answer (their
+ sources are two rules on the same committed selector). */
+.dcx-proof--board[data-dcx-layout="simple-grid"] > .picker-artboard {
+ --pvs-gutter: 6%;
+ --pvs-gutter-end: 6%;
+ --pvs-hero-cols: var(--pvs-w6) minmax(0, 1fr);
+ --pvs-hero-gap: var(--pvs-gut);
+ --pvs-editorial-cols: var(--pvs-w4) minmax(0, 1fr);
+ --pvs-editorial-gap: var(--pvs-gut);
+ --pvs-gallery-cols: repeat(4, minmax(0, 1fr));
+ --pvs-gallery-gap: calc(var(--pvs-gut) * 100 / 66.1333);
+ --pvs-section-align: start;
+ --pvs-rows: 9.4% 45.5% 9% 27.8% 8.3%;
+ --pvs-phone-gutter: 7%;
+ --pvs-phone-gallery-cols: 1fr 1fr;
+ --pvs-shell-cols:
+ calc(var(--pvs-w2) + var(--pvs-gut) / 2)
+ calc(var(--pvs-w7) + var(--pvs-gut))
+ minmax(0, 1fr);
+ --pvs-measure-cols:
+ calc(var(--pvs-w3) + var(--pvs-gut) / 2)
+ minmax(0, 1fr);
+ --pvs-metric-cols: repeat(3, minmax(0, 1fr));
+ --pvs-entry-cols: var(--pvs-w6) minmax(0, 1fr);
+ --pvs-entry-flip-cols: var(--pvs-w6) minmax(0, 1fr);
+}
+
+.dcx-proof--board[data-dcx-layout="simple-grid"] > .picker-artboard .ps-index-row--flip > .ps-image {
+ grid-area: 1 / 1;
+}
+
+.dcx-proof--board[data-dcx-layout="simple-grid"] > .picker-artboard .ps-index-row--flip > .ps-index-cap {
+ grid-area: 1 / 2;
+}
+
+.dcx-proof--board[data-dcx-layout="balanced"] > .picker-artboard {
+ --pvs-gutter: 6%;
+ --pvs-gutter-end: 6%;
+ --pvs-hero-cols: var(--pvs-w5) minmax(0, 1fr);
+ --pvs-hero-gap: var(--pvs-gut);
+ --pvs-editorial-cols: var(--pvs-w3) minmax(0, 1fr);
+ --pvs-editorial-gap: var(--pvs-gut);
+ --pvs-gallery-cols: 23.8fr 15.3333fr 15.3333fr 15.3333fr;
+ --pvs-gallery-gap: calc(var(--pvs-gut) * 100 / 74.6);
+ --pvs-section-align: center;
+ --pvs-rows: 9.4% 50.6% 9% 22.7% 8.3%;
+ --pvs-phone-gutter: 6%;
+ --pvs-phone-gallery-cols: 1.35fr 0.85fr;
+ --pvs-shell-cols:
+ calc(var(--pvs-w2) + var(--pvs-gut) / 2)
+ calc(var(--pvs-w8) + var(--pvs-gut))
+ minmax(0, 1fr);
+ --pvs-measure-cols:
+ calc(var(--pvs-w3) + var(--pvs-gut) / 2)
+ calc(var(--pvs-w6) + var(--pvs-gut))
+ minmax(0, 1fr);
+ --pvs-metric-cols: 1.45fr 1fr 1fr;
+ --pvs-entry-cols: var(--pvs-w7) minmax(0, 1fr);
+ --pvs-entry-flip-cols: minmax(0, 1fr) var(--pvs-w7);
+}
+
+.dcx-proof--board[data-dcx-layout="freeform"] > .picker-artboard {
+ --pvs-gutter: 6%;
+ --pvs-gutter-end: 6%;
+ --pvs-hero-cols: var(--pvs-w5) minmax(0, 1fr);
+ --pvs-hero-gap: var(--pvs-gut);
+ --pvs-editorial-cols: var(--pvs-w3) minmax(0, 1fr);
+ --pvs-editorial-gap: var(--pvs-gut);
+ --pvs-gallery-cols: repeat(4, minmax(0, 1fr));
+ --pvs-gallery-gap: calc(var(--pvs-gut) * 100 / 74.6);
+ --pvs-section-align: start;
+ --pvs-rows: 9.4% 48% 8.4% 26.2% 8%;
+ --pvs-phone-gutter: 6%;
+ --pvs-phone-gallery-cols: 1.5fr 0.9fr;
+ --pvs-drift: 1;
+ --pvs-entry-cols: var(--pvs-w7) minmax(0, 1fr);
+ --pvs-entry-flip-cols: minmax(0, 1fr) var(--pvs-w7);
+}
+
+/* Freeform's one move on the landing hero: the print cut loose of the
+ grid, with the copy holding the page's left measure. */
+.dcx-proof--board[data-dcx-layout="freeform"] > .picker-artboard .ps-desktop .ps-hero {
+ position: relative;
+ display: block;
+ padding: 0;
+}
+
+.dcx-proof--board[data-dcx-layout="freeform"] > .picker-artboard .ps-desktop .ps-hero > .ps-image {
+ position: absolute;
+ inset: 0;
+ height: 100%;
+ clip-path: polygon(40% 0, 100% 0, 100% 78%, 66% 84%);
+}
+
+.dcx-proof--board[data-dcx-layout="freeform"] > .picker-artboard .ps-desktop .ps-hero > .ps-hero-copy {
+ position: relative;
+ z-index: 1;
+ box-sizing: border-box;
+ width: 44%;
+ height: 100%;
+ align-content: center;
+ padding-left: var(--pvs-gutter, 6%);
+}
+
+/* Freeform on the index: the prints laid down askew with the caption
+ stepping onto the corner, the committed rules that are surface-scoped
+ on screen and read the frame's surface mark here. */
+.dcx-proof--board[data-dcx-layout="freeform"][data-dcx-surface="experience"] > .picker-artboard .ps-index-row > .ps-image {
+ rotate: -1.7deg;
+}
+
+.dcx-proof--board[data-dcx-layout="freeform"][data-dcx-surface="experience"] > .picker-artboard .ps-index-row--flip > .ps-image {
+ rotate: 1.4deg;
+}
+
+.dcx-proof--board[data-dcx-layout="freeform"][data-dcx-surface="experience"] > .picker-artboard .ps-index-cap {
+ position: relative;
+ z-index: 1;
+ translate: -9% 0;
+}
+
+.dcx-proof--board[data-dcx-layout="freeform"][data-dcx-surface="experience"] > .picker-artboard .ps-index-row--flip > .ps-index-cap {
+ translate: 9% 0;
+}
/* The scale sheet and specimen size themselves against their rail on screen
05; in the document they stand at natural height and full width. */
diff --git a/picker/styles/picker.css b/picker/styles/picker.css
index 139b3284c..a06fb0e13 100644
--- a/picker/styles/picker.css
+++ b/picker/styles/picker.css
@@ -1429,6 +1429,17 @@ body.picker-page {
height: calc(5.58708 * var(--pv-x));
background: var(--pv-primary);
border-radius: 2px;
+ display: grid;
+ place-items: center;
+ color: var(--pv-neutral);
+}
+
+.pv-pill::after {
+ content: "";
+ width: calc(8.5 * var(--pv-x));
+ height: calc(1.3 * var(--pv-x));
+ background: currentColor;
+ border-radius: 2px;
}
/* The hero is a size container so the page it draws scales with the card rather
@@ -1951,7 +1962,7 @@ body.picker-page {
flex: none;
width: calc(6.92 * var(--pv-x));
height: var(--h);
- background: var(--po-wash);
+ background: var(--po-ink-soft);
}
.po-chart .po-peak {
@@ -2051,10 +2062,10 @@ body.picker-page {
/* A square stood on its corner, two borders lit. Half of it falls outside the
drawn chevron, so the margins are placing the tip rather than the box. */
.po-chev {
- align-self: start;
+ align-self: center;
width: calc(2 * var(--pv-x));
aspect-ratio: 1;
- margin: calc(1.86 * var(--pv-x)) calc(3.14 * var(--pv-x)) 0 auto;
+ margin: 0 calc(3.14 * var(--pv-x)) 0 auto;
border-right: 1px solid var(--po-ink);
border-bottom: 1px solid var(--po-ink);
transform: rotate(45deg);
@@ -2154,8 +2165,9 @@ body.picker-page {
}
.po-phone-body .po-chev {
+ align-self: center;
width: calc(2.77 * var(--pv-x));
- margin: calc(2.65 * var(--pv-x)) calc(5.03 * var(--pv-x)) 0 auto;
+ margin: 0 calc(5.03 * var(--pv-x)) 0 auto;
}
.po-prow {