← back to Wallco Ai
wallco.ai · professional/trade functions — 3 new designer-workflow features. (1) /design/:id/tearsheet — print-ready 1-page PDF view with hero+room+specs+DW contact, @media print rules, 'Print or Save as PDF' button, noindex (per-client docs not for crawlers). (2) /compare?ids=72,161,139 — side-by-side compare table (up to 4 designs), per-design Remove buttons + View/Sample/Tear-sheet actions per col, Print + Copy-share-link buttons, localStorage[wallco-compare] backed (empty /compare auto-redirects to stored list). (3) PDP CTA gets 4 trade-tool buttons: Tear Sheet (PDF), Email to Client (mailto with pre-filled subject+body+tearsheet link), + Compare (localStorage toggle with In-compare/Compare state), View Compare (N) — gold pill, only visible when list non-empty. All additive, all reversible. Verified: /design/72/tearsheet → 200 5255B, /compare?ids=72,161,139 → 200 24511B, /compare empty → 200 11644B (renders empty-state w/ localStorage auto-redirect), PDP has 6 pro-tool refs.
d6bebc35c06a13eec4c036a9df009d47bd8b3941 · 2026-05-12 08:13:23 -0700 · SteveStudio2
Files touched
Diff
commit d6bebc35c06a13eec4c036a9df009d47bd8b3941
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue May 12 08:13:23 2026 -0700
wallco.ai · professional/trade functions — 3 new designer-workflow features. (1) /design/:id/tearsheet — print-ready 1-page PDF view with hero+room+specs+DW contact, @media print rules, 'Print or Save as PDF' button, noindex (per-client docs not for crawlers). (2) /compare?ids=72,161,139 — side-by-side compare table (up to 4 designs), per-design Remove buttons + View/Sample/Tear-sheet actions per col, Print + Copy-share-link buttons, localStorage[wallco-compare] backed (empty /compare auto-redirects to stored list). (3) PDP CTA gets 4 trade-tool buttons: Tear Sheet (PDF), Email to Client (mailto with pre-filled subject+body+tearsheet link), + Compare (localStorage toggle with In-compare/Compare state), View Compare (N) — gold pill, only visible when list non-empty. All additive, all reversible. Verified: /design/72/tearsheet → 200 5255B, /compare?ids=72,161,139 → 200 24511B, /compare empty → 200 11644B (renders empty-state w/ localStorage auto-redirect), PDP has 6 pro-tool refs.
---
server.js | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 259 insertions(+)
diff --git a/server.js b/server.js
index 85cb66f..d0a7486 100644
--- a/server.js
+++ b/server.js
@@ -3290,6 +3290,63 @@ ${htmlHeader('/designs')}
<span class="cta-share-label">Share</span>
</button>
</div>
+
+ <!-- Trade / professional functions row -->
+ <div class="pro-tools" role="group" aria-label="Trade tools" style="display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 4px;padding-top:14px;border-top:1px dashed var(--line,#ddd)">
+ <a href="/design/${design.id}/tearsheet" target="_blank" rel="noopener noreferrer" class="btn-outline" style="font:12px var(--sans);padding:7px 14px;text-decoration:none;display:inline-flex;align-items:center;gap:6px" title="Open print-ready tear sheet (1-page PDF)">
+ <span aria-hidden="true">⎙</span> Tear Sheet (PDF)
+ </a>
+ <a id="cta-email-client" href="mailto:?subject=${encodeURIComponent('wallco.ai design — ' + (design.title || 'Design #' + design.id))}&body=${encodeURIComponent('Take a look at this wallpaper pattern I found:\n\n' + (design.title || 'Design #' + design.id) + '\nhttps://wallco.ai/design/' + design.id + '\n\nTear sheet (print/PDF):\nhttps://wallco.ai/design/' + design.id + '/tearsheet\n\nFree trade samples available from Designer Wallcoverings — info@designerwallcoverings.com\n')}" class="btn-outline" style="font:12px var(--sans);padding:7px 14px;text-decoration:none;display:inline-flex;align-items:center;gap:6px" title="Email this design to a client (opens your mail app)">
+ <span aria-hidden="true">✉</span> Email to Client
+ </a>
+ <button id="cta-add-compare" type="button" data-design-id="${design.id}" class="btn-outline" style="font:12px var(--sans);padding:7px 14px;cursor:pointer;display:inline-flex;align-items:center;gap:6px" title="Add to side-by-side compare (up to 4)">
+ <span aria-hidden="true">+</span> <span class="cta-add-compare-label">Compare</span>
+ </button>
+ <a id="cta-view-compare" href="/compare" class="btn-outline" style="font:12px var(--sans);padding:7px 14px;text-decoration:none;display:none;align-items:center;gap:6px;background:var(--gold,#c9a14b);color:var(--accent,#0d0d0d);border-color:var(--gold,#c9a14b)" title="View your compare list">
+ View Compare (<span id="cta-compare-count">0</span>)
+ </a>
+ </div>
+ <script>
+ (function(){
+ var addBtn = document.getElementById('cta-add-compare');
+ var viewBtn = document.getElementById('cta-view-compare');
+ var countEl = document.getElementById('cta-compare-count');
+ if (!addBtn || !viewBtn) return;
+ var thisId = parseInt(addBtn.dataset.designId, 10);
+ function getList(){ try { return JSON.parse(localStorage.getItem('wallco-compare') || '[]').filter(function(x){ return typeof x === 'number' && x>0; }); } catch(e){ return []; } }
+ function setList(arr){ try { localStorage.setItem('wallco-compare', JSON.stringify(arr.slice(0,4))); } catch(e){} }
+ function refresh(){
+ var list = getList();
+ if (countEl) countEl.textContent = list.length;
+ if (list.length > 0) {
+ viewBtn.style.display = 'inline-flex';
+ viewBtn.setAttribute('href', '/compare?ids=' + list.join(','));
+ } else {
+ viewBtn.style.display = 'none';
+ }
+ var lbl = addBtn.querySelector('.cta-add-compare-label');
+ if (list.indexOf(thisId) >= 0) {
+ if (lbl) lbl.textContent = 'In compare';
+ addBtn.dataset.in = '1';
+ addBtn.style.background = 'var(--card-bg, #faf8f3)';
+ } else {
+ if (lbl) lbl.textContent = 'Compare';
+ addBtn.dataset.in = '0';
+ addBtn.style.background = '';
+ }
+ }
+ addBtn.addEventListener('click', function(){
+ var list = getList();
+ var idx = list.indexOf(thisId);
+ if (idx >= 0) { list.splice(idx, 1); }
+ else if (list.length < 4) { list.push(thisId); }
+ else { alert('Compare holds 4 designs max. Remove one to add another.'); return; }
+ setList(list);
+ refresh();
+ });
+ refresh();
+ })();
+ </script>
<script>
(function(){
var btn = document.getElementById('cta-share');
@@ -4138,6 +4195,208 @@ ${HAMBURGER_JS}
</html>`);
});
+// ── /design/:id/tearsheet — print-ready single-page PDF for trade use
+// Designers email this to clients or print to PDF. Optimized for 8.5×11 portrait.
+// No header, no hamburger — just hero + room + specs + DW contact + QR-style URL block.
+app.get('/design/:id/tearsheet', (req, res) => {
+ const id = parseInt(req.params.id, 10);
+ if (!Number.isFinite(id) || id < 1) return res.status(404).send('Not found');
+ let design = DESIGNS.find(d => d.id === id);
+ if (!design) return res.status(404).send('Design not found');
+ const roomKey = (design.room_mockups || []).includes('living_room') ? 'living_room'
+ : (design.room_mockups || []).includes('bedroom') ? 'bedroom'
+ : (design.room_mockups || []).includes('office') ? 'office'
+ : (design.room_mockups || [])[0];
+ const roomImg = roomKey ? `/designs/room/design_${design.id}_${roomKey}.png` : null;
+ const motifs = (design.motifs || []).slice(0, 6).join(' · ');
+ const created = design.created_at ? new Date(design.created_at).toISOString().slice(0,10) : '';
+ const designUrl = `https://wallco.ai/design/${design.id}`;
+ const esc = (s) => String(s||'').replace(/[<>&"]/g, c => ({'<':'<','>':'>','&':'&','"':'"'}[c]));
+ res.type('html').send(`<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><title>${esc(design.title)} — Tear Sheet | wallco.ai</title>
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<meta name="robots" content="noindex,nofollow">
+<style>
+ @page { size: letter portrait; margin: 0.5in; }
+ * { box-sizing: border-box; }
+ body { font-family: 'Georgia', 'Times New Roman', serif; color:#1a1a1a; margin:0; padding:32px; background:#fff; max-width:8.5in; margin-inline:auto; }
+ .ts-header { display:flex; justify-content:space-between; align-items:baseline; border-bottom:2px solid #1a1a1a; padding-bottom:10px; margin-bottom:18px; }
+ .ts-brand { font:14px/1 -apple-system, system-ui, sans-serif; letter-spacing:.12em; text-transform:uppercase; font-weight:600; }
+ .ts-tag { font:11px/1 -apple-system, system-ui, sans-serif; color:#666; letter-spacing:.04em; }
+ .ts-title { font:300 38px/1.1 Georgia, serif; margin:0 0 6px 0; }
+ .ts-sub { font:13px/1.4 -apple-system, system-ui, sans-serif; color:#555; margin-bottom:18px; }
+ .ts-hero { display:grid; grid-template-columns: 1fr 1fr; gap:14px; margin-bottom:18px; }
+ .ts-hero .swatch, .ts-hero .room { aspect-ratio: 1/1; background:#f4f1ea center/cover no-repeat; border:1px solid #d8d2c4; }
+ .ts-specs { display:grid; grid-template-columns: 130px 1fr; gap:6px 14px; font:12px/1.5 -apple-system, system-ui, sans-serif; margin-bottom:16px; }
+ .ts-specs dt { color:#666; text-transform:uppercase; letter-spacing:.06em; font-size:10px; padding-top:2px; }
+ .ts-specs dd { margin:0; color:#1a1a1a; font-weight:500; }
+ .ts-colorblock { display:flex; align-items:center; gap:10px; padding:10px 14px; background:#faf8f3; border:1px solid #ebe5d6; border-radius:4px; margin-bottom:18px; font:12px -apple-system, system-ui, sans-serif; }
+ .ts-colorchip { width:32px; height:32px; border-radius:50%; border:1px solid rgba(0,0,0,.15); flex-shrink:0; }
+ .ts-section { font:11px/1 -apple-system, system-ui, sans-serif; text-transform:uppercase; letter-spacing:.1em; color:#888; margin:14px 0 6px; }
+ .ts-footer { margin-top:24px; padding-top:14px; border-top:1px solid #ddd; display:grid; grid-template-columns: 2fr 1fr; gap:20px; font:11px/1.5 -apple-system, system-ui, sans-serif; color:#444; }
+ .ts-footer strong { color:#1a1a1a; }
+ .ts-url { font:11px/1.4 ui-monospace, Menlo, monospace; color:#1a1a1a; word-break: break-all; }
+ .no-print { margin: 18px 0; }
+ .no-print button { padding:10px 18px; font:13px -apple-system, system-ui, sans-serif; background:#1a1a1a; color:#fff; border:0; border-radius:4px; cursor:pointer; margin-right:8px; }
+ .no-print a { padding:10px 18px; font:13px -apple-system, system-ui, sans-serif; background:#fff; color:#1a1a1a; text-decoration:none; border:1px solid #1a1a1a; border-radius:4px; display:inline-block; }
+ @media print { .no-print { display:none; } body { padding:0; } }
+</style></head><body>
+<div class="no-print">
+ <button onclick="window.print()">Print or Save as PDF</button>
+ <a href="/design/${design.id}">← Back to design</a>
+</div>
+<div class="ts-header">
+ <div class="ts-brand">wallco.ai · Designer Wallcoverings</div>
+ <div class="ts-tag">Tear Sheet · ${created}</div>
+</div>
+<h1 class="ts-title">${esc(design.title)}</h1>
+<div class="ts-sub">Design #${design.id} · ${esc(design.category || 'mixed')}${motifs ? ' · ' + esc(motifs) : ''}</div>
+
+<div class="ts-hero">
+ <div class="swatch" style="background-image:url('${esc(design.image_url)}')" aria-label="${esc(design.title)} pattern"></div>
+ ${roomImg
+ ? `<div class="room" style="background-image:url('${esc(roomImg)}')" aria-label="In-room preview"></div>`
+ : `<div class="room" style="display:flex;align-items:center;justify-content:center;font:11px -apple-system,system-ui,sans-serif;color:#888;text-align:center;padding:20px">Room preview rendered on request</div>`
+ }
+</div>
+
+<div class="ts-colorblock">
+ <div class="ts-colorchip" style="background:${esc(design.dominant_hex || '#888')}"></div>
+ <div><strong>Dominant color:</strong> ${esc(design.dominant_hex || 'n/a')} · This is the design's primary palette anchor; complementary tones are derived from the full image extraction.</div>
+</div>
+
+<div class="ts-section">Pattern Specs</div>
+<dl class="ts-specs">
+ <dt>SKU / Handle</dt><dd>${esc(design.handle || 'wallco-' + String(design.id).padStart(4,'0'))}</dd>
+ <dt>Category</dt><dd>${esc(design.category || 'mixed')}</dd>
+ ${motifs ? `<dt>Motifs</dt><dd>${esc(motifs)}</dd>` : ''}
+ <dt>Tile Size</dt><dd>24×24 in seamless · 150 DPI</dd>
+ <dt>Repeat</dt><dd>24" horizontal · 24" vertical (full drop)</dd>
+ <dt>Format</dt><dd>AI-original · seamless tile · scalable to any wall</dd>
+ <dt>Substrate</dt><dd>Type II vinyl · nonwoven paper · peel-and-stick · canvas mural</dd>
+ <dt>Fire Rating</dt><dd>Class A available (Type II vinyl substrate, ASTM E84)</dd>
+ <dt>Cleanability</dt><dd>Scrubbable (vinyl) · Spongeable (paper)</dd>
+ <dt>Lead Time</dt><dd>Sample: 3–5 business days · Production: ~2 weeks</dd>
+ <dt>Sample Size</dt><dd>8.5 × 11 in</dd>
+</dl>
+
+<div class="ts-section">Direct URL</div>
+<div class="ts-url">${esc(designUrl)}</div>
+
+<div class="ts-footer">
+ <div>
+ <strong>Designer Wallcoverings</strong><br>
+ 15442 Ventura Bl #102, Sherman Oaks CA 91403<br>
+ <strong>info@designerwallcoverings.com</strong> · designerwallcoverings.com<br>
+ AI-original wallpaper, hand-curated. Free trade samples.
+ </div>
+ <div style="text-align:right">
+ Generated ${new Date().toISOString().slice(0,10)}<br>
+ <span style="font:10px ui-monospace, Menlo, monospace; color:#888">${esc(design.handle || 'wallco-'+design.id)}</span>
+ </div>
+</div>
+</body></html>`);
+});
+
+// ── /compare — side-by-side compare up to 4 designs
+// ?ids=72,161,139 (server-rendered) OR uses localStorage[wallco-compare] if ids absent + browser hits with JS.
+app.get('/compare', (req, res) => {
+ const rawIds = String(req.query.ids || '').split(',').map(s => parseInt(s.trim(), 10)).filter(n => Number.isFinite(n) && n > 0).slice(0, 4);
+ const items = rawIds.map(id => DESIGNS.find(d => d.id === id)).filter(Boolean);
+ const esc = (s) => String(s||'').replace(/[<>&"]/g, c => ({'<':'<','>':'>','&':'&','"':'"'}[c]));
+ const cols = items.length || 1;
+ const colWidth = items.length ? `${100/items.length}%` : '100%';
+ const cellsForRow = (fn) => items.map(d => `<td style="padding:10px 14px;border-top:1px solid var(--line,#eee);font:13px var(--sans,system-ui);width:${colWidth};vertical-align:top">${fn(d)}</td>`).join('');
+ res.type('html').send(`${htmlHead({
+ title: 'Compare Designs — wallco.ai',
+ description: items.length ? `Side-by-side comparison of ${items.length} wallco.ai designs.` : 'Compare wallco.ai designs side-by-side.',
+ canonical: 'https://wallco.ai/compare'
+ })}
+<body>
+${htmlHeader('/compare')}
+<main>
+<section class="catalog-section" style="padding:24px 40px">
+ <h1 style="font:300 36px/1.1 var(--serif,Georgia,serif);margin:0 0 6px">Compare designs</h1>
+ <p style="font:13px var(--sans,system-ui);color:var(--ink-soft,#555);margin:0 0 18px">${items.length ? `${items.length} of 4 selected.` : 'No designs selected. Use "+ Compare" on any design page to add up to 4.'}</p>
+
+ ${items.length === 0 ? `
+ <div id="compare-empty" style="padding:40px;border:1px dashed var(--line,#ccc);border-radius:8px;text-align:center;font:14px var(--sans,system-ui);color:var(--ink-faint,#888)">
+ <p>Visit <a href="/designs" style="color:var(--ink,#111)">/designs</a>, click <strong>+ Compare</strong> on the designs you want to evaluate side-by-side, then return here.</p>
+ <p style="font:12px var(--sans,system-ui);margin-top:14px">Or share a link directly: <code>/compare?ids=72,161</code></p>
+ </div>
+ <script>
+ // If localStorage has stored ids, auto-redirect to the URL form so the share-link works.
+ try {
+ var stored = JSON.parse(localStorage.getItem('wallco-compare') || '[]');
+ if (Array.isArray(stored) && stored.length > 0) {
+ window.location.href = '/compare?ids=' + stored.slice(0,4).join(',');
+ }
+ } catch(e) {}
+ </script>
+ ` : `
+ <table style="width:100%;border-collapse:collapse;table-layout:fixed">
+ <thead><tr>${items.map(d => `<th style="padding:0;width:${colWidth};vertical-align:top">
+ <a href="/design/${d.id}" style="display:block;text-decoration:none;color:inherit">
+ <div style="aspect-ratio:1;background:#f4f1ea url('${esc(d.image_url)}') center/cover;border-radius:6px;border:1px solid var(--line,#ddd);margin-bottom:10px"></div>
+ <div style="font:500 16px/1.3 var(--serif,Georgia,serif);color:var(--ink,#111)">${esc(d.title || 'Design #' + d.id)}</div>
+ <div style="font:11px/1.4 var(--sans,system-ui);color:var(--ink-faint,#888);margin-top:3px"><span style="display:inline-block;width:9px;height:9px;border-radius:50%;background:${esc(d.dominant_hex||'#888')};border:1px solid rgba(0,0,0,.12);vertical-align:middle;margin-right:4px"></span>${esc(d.category || 'mixed')}</div>
+ </a>
+ <button type="button" data-rmid="${d.id}" class="compare-rm" style="margin-top:8px;padding:4px 10px;font:11px var(--sans,system-ui);background:transparent;border:1px solid var(--line,#ddd);color:var(--ink-soft,#555);border-radius:3px;cursor:pointer">Remove</button>
+ </th>`).join('')}</tr></thead>
+ <tbody style="font:13px var(--sans,system-ui)">
+ <tr><td colspan="${cols}" style="padding:14px 0 6px;font:11px var(--sans,system-ui);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint,#888);border-bottom:1px solid var(--line,#eee)">Specs</td></tr>
+ <tr>${cellsForRow(d => `<strong>SKU</strong> · ${esc(d.handle || 'wallco-'+String(d.id).padStart(4,'0'))}`)}</tr>
+ <tr>${cellsForRow(d => `<strong>Category</strong> · ${esc(d.category || 'mixed')}`)}</tr>
+ <tr>${cellsForRow(d => `<strong>Motifs</strong> · ${esc((d.motifs||[]).slice(0,5).join(', ') || '—')}`)}</tr>
+ <tr>${cellsForRow(d => `<strong>Color</strong> · <span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${esc(d.dominant_hex||'#888')};border:1px solid rgba(0,0,0,.12);vertical-align:middle;margin-right:4px"></span>${esc(d.dominant_hex || 'n/a')}`)}</tr>
+ <tr>${cellsForRow(d => `<strong>Room previews</strong> · ${(d.room_mockups||[]).length}`)}</tr>
+ <tr>${cellsForRow(d => `<strong>Created</strong> · ${d.created_at ? new Date(d.created_at).toISOString().slice(0,10) : '—'}`)}</tr>
+ <tr>${cellsForRow(_d => `<strong>Tile</strong> · 24×24 in seamless`)}</tr>
+ <tr>${cellsForRow(_d => `<strong>Fire rating</strong> · Class A (Type II vinyl)`)}</tr>
+ <tr><td colspan="${cols}" style="padding:14px 0 6px;font:11px var(--sans,system-ui);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint,#888);border-bottom:1px solid var(--line,#eee);padding-top:18px">Actions</td></tr>
+ <tr>${items.map(d => `<td style="padding:10px 14px;border-top:1px solid var(--line,#eee);font:13px var(--sans,system-ui);vertical-align:top">
+ <a href="/design/${d.id}" style="display:inline-block;padding:6px 12px;font:12px var(--sans,system-ui);background:var(--ink,#111);color:var(--bg,#fff);text-decoration:none;border-radius:4px;margin-right:6px">View</a>
+ <a href="/samples?design=${d.id}" style="display:inline-block;padding:6px 12px;font:12px var(--sans,system-ui);background:transparent;color:var(--ink,#111);text-decoration:none;border:1px solid var(--ink,#111);border-radius:4px;margin-right:6px">Sample</a>
+ <a href="/design/${d.id}/tearsheet" target="_blank" rel="noopener noreferrer" style="display:inline-block;padding:6px 12px;font:12px var(--sans,system-ui);background:transparent;color:var(--ink,#111);text-decoration:none;border:1px solid var(--ink,#111);border-radius:4px">Tear sheet</a>
+ </td>`).join('')}</tr>
+ </tbody>
+ </table>
+ <div style="margin-top:24px;display:flex;gap:12px;flex-wrap:wrap">
+ <button type="button" id="compare-print" style="padding:10px 18px;font:13px var(--sans,system-ui);background:var(--ink,#111);color:var(--bg,#fff);border:0;border-radius:4px;cursor:pointer">Print or save as PDF</button>
+ <button type="button" id="compare-copy" style="padding:10px 18px;font:13px var(--sans,system-ui);background:transparent;color:var(--ink,#111);border:1px solid var(--ink,#111);border-radius:4px;cursor:pointer">Copy share link</button>
+ <a href="/designs" style="padding:10px 18px;font:13px var(--sans,system-ui);background:transparent;color:var(--ink,#111);text-decoration:none;border:1px solid var(--line,#ddd);border-radius:4px">Browse more designs</a>
+ </div>
+ <script>
+ (function(){
+ var stored = ${JSON.stringify(rawIds)};
+ try { localStorage.setItem('wallco-compare', JSON.stringify(stored)); } catch(e){}
+ document.querySelectorAll('.compare-rm').forEach(function(btn){
+ btn.addEventListener('click', function(){
+ var rid = parseInt(btn.dataset.rmid,10);
+ var next = stored.filter(function(x){ return x !== rid; });
+ try { localStorage.setItem('wallco-compare', JSON.stringify(next)); } catch(e){}
+ window.location.href = next.length ? '/compare?ids=' + next.join(',') : '/compare';
+ });
+ });
+ var pBtn = document.getElementById('compare-print');
+ if (pBtn) pBtn.addEventListener('click', function(){ window.print(); });
+ var cBtn = document.getElementById('compare-copy');
+ if (cBtn) cBtn.addEventListener('click', async function(){
+ var url = location.origin + '/compare?ids=' + stored.join(',');
+ try { await navigator.clipboard.writeText(url); cBtn.textContent = 'Copied!'; setTimeout(function(){ cBtn.textContent='Copy share link'; }, 1800); }
+ catch(e) { window.prompt('Copy:', url); }
+ });
+ })();
+ </script>
+ `}
+</section>
+</main>
+${FOOTER}
+${HAMBURGER_JS}
+</body>
+</html>`);
+});
+
// ── MURALS
app.get('/murals', (req, res) => {
const murals = DESIGNS.filter(d => d.kind === 'mural' || d.kind === 'mural_panel');
← 4b1b3ed wallco.ai · /api/designs/search now returns facets — motifs
·
back to Wallco Ai
·
wallco.ai · autocomplete dropdown now renders a 'Refine' mot 340a3c9 →