openvibers/openvibe.domains Commits
openvibe.domains' hero is one name priced (#66)
Changes
@@@ -315,3 +315,19 @@ html[data-ov-motion=reduced] .dm-bar span { transform: none !important; transiti.dm-board::-webkit-scrollbar { display: none; }.dm-board > li { flex: 0 0 62%; min-width: 0; scroll-snap-align: start; }}++++/* ── The home's picture: one name, priced (MOTION-1, 2026-11-02) ──────────────────────────────────────────────────── */++.dm-motif { width: min(100%, 370px); margin: 0 auto; display: grid; gap: 10px; padding: 16px; border-radius: 18px; border: 1px solid var(--ovk-line, rgba(255, 255, 255, .1)); background: var(--ovk-panel, #141c2a); box-shadow: 0 12px 30px rgba(0, 0, 0, .22); font-variant-numeric: tabular-nums; }++.dm-mf-name { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }++.dm-mf-name b { font: 700 17px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--ovk-text, #e8ecf3); overflow-wrap: anywhere; }++.dm-mf-ok { font-size: 12.5px; font-weight: 700; color: var(--ovk-ok-text, var(--ovk-ok, #22c55e)); white-space: nowrap; }++.dm-mf-cols, .dm-mf-row { display: grid; grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, .7fr)); gap: 8px; align-items: center; }++.dm-mf-cols { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--ovk-muted, #93a0b4); padding: 0 10px; }++.dm-mf-cols span:not(:first-child), .dm-mf-row > span:not(:first-child), .dm-mf-row b { text-align: right; }++.dm-mf-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }++.dm-mf-row { padding: 9px 10px; border-radius: 10px; background: var(--ovk-bg, #0f1522); font-size: 13px; color: var(--ovk-muted, #93a0b4); }++.dm-mf-row b { color: var(--ovk-text, #e8ecf3); }++.dm-mf-reg { display: grid; gap: 1px; color: var(--ovk-text, #e8ecf3); min-width: 0; }++.dm-mf-reg em { font-style: normal; font-size: 11px; font-weight: 700; color: var(--room-strong, var(--ovk-accent, #3b82f6)); }++.dm-mf-reg em.is-trap { color: var(--ovk-warn-text, var(--ovk-warn, #f59e0b)); }++.dm-mf-row.is-best { outline: 2px solid color-mix(in srgb, var(--room-accent, var(--ovk-accent, #3b82f6)) 55%, transparent); outline-offset: -2px; }@@@ -31,6 +31,20 @@ const esc = kit.esc;const PUBLIC = path.join(__dirname, '..', '..', 'public');const hashes = new Map();++/**++ * The home's picture (MOTION-1, 2026-11-02: sc.room drew its default motif): one name, priced the way every page here++ * prices it. Three registrars with the first year, the renewal and what three years really cost, the cheapest to keep++ * marked, and the cheap first year that renews high flagged. A sample, said as one.++ */++function domainsMotif() {++ const row = (name, y1, renew, three, mark = '') => `<li class="dm-mf-row${mark === 'best' ? ' is-best' : ''}"><span class="dm-mf-reg">${name}${mark === 'best' ? '<em>cheapest to keep</em>' : mark === 'trap' ? '<em class="is-trap">renews high</em>' : ''}</span><span>${y1}</span><span>${renew}</span><b>${three}</b></li>`;++ return `<div class="dm-motif" aria-hidden="true">++<div class="dm-mf-name"><b>sushitruck.dev</b><span class="dm-mf-ok">✓ available</span></div>++<div class="dm-mf-cols"><span>registrar · sample</span><span>1st yr</span><span>renews</span><span>3 years</span></div>++<ol class="dm-mf-list">${row('Porkbun', '$11', '$11', '$33', 'best')}${row('Namecheap', '$9', '$16', '$41')}${row('Budget Reg', '$2', '$22', '$46', 'trap')}</ol>++</div>`;++}++function asset(rel) {if (!hashes.has(rel)) {let v = 'dev';@@@ -158,6 +172,7 @@ function home({ endings, lists, canonical } = {}) {lede: 'Type a name. See every ending and every registrar, with what you will really pay over one, three and five years, and the renewal traps flagged.',search: { action: '/search', name: 'q', placeholder: 'Price a name, like sushitruck', label: 'A name to price', button: 'Price it', hints: ['sushitruck', 'mybrand.dev', 'cozy cafe', 'pixel.studio'] },chips: popular.map((r) => ({ label: r.display || dot(r.tld), href: tldHref(r.tld) })),++ motif: domainsMotif(),},proof: [{ value: all.length, label: all.length === 1 ? 'ending priced' : 'endings priced', asOf: checked ? day(checked) : null },@@@ -25,6 +25,12 @@ const { boot, check, done } = require('./helpers');assert.ok(r.text.includes('<section class="sc-sec" id="faq"') || r.text.includes('aria-labelledby="sc-questions"'), 'the questions keep their section');});++ await check('the hero is one name priced, not sc.room\'s default motif (MOTION-1)', async () => {++ const r = await t.get('/');++ assert.match(r.text, /<div class="dm-motif" aria-hidden="true">/);++ assert.match(r.text, /sushitruck\.dev[\s\S]*?cheapest to keep[\s\S]*?renews high/);++ assert.ok(!/class="sc-room-motif"/.test(r.text), 'no default motif');++ });await check('the CSS guard: the folds are closed on a phone only, and the endings are one row', async () => {const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'domains.css'), 'utf8');assert.match(css, /\.hm-fold-block > summary \{[^}]*min-height: 44px/, 'a fold is a 44 px target');
@@ -315,3 +315,19 @@ html[data-ov-motion=reduced] .dm-bar span { transform: none !important; transiti
@@ -315,3 +315,19 @@ html[data-ov-motion=reduced] .dm-bar span { transform: none !important; transiti
.dm-board::-webkit-scrollbar { display: none; }
.dm-board::-webkit-scrollbar { display: none; }
.dm-board > li { flex: 0 0 62%; min-width: 0; scroll-snap-align: start; }
.dm-board > li { flex: 0 0 62%; min-width: 0; scroll-snap-align: start; }
}
}
+
+/* ── The home's picture: one name, priced (MOTION-1, 2026-11-02) ──────────────────────────────────────────────────── */
+.dm-motif { width: min(100%, 370px); margin: 0 auto; display: grid; gap: 10px; padding: 16px; border-radius: 18px; border: 1px solid var(--ovk-line, rgba(255, 255, 255, .1)); background: var(--ovk-panel, #141c2a); box-shadow: 0 12px 30px rgba(0, 0, 0, .22); font-variant-numeric: tabular-nums; }
+.dm-mf-name { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
+.dm-mf-name b { font: 700 17px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--ovk-text, #e8ecf3); overflow-wrap: anywhere; }
+.dm-mf-ok { font-size: 12.5px; font-weight: 700; color: var(--ovk-ok-text, var(--ovk-ok, #22c55e)); white-space: nowrap; }
+.dm-mf-cols, .dm-mf-row { display: grid; grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, .7fr)); gap: 8px; align-items: center; }
+.dm-mf-cols { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--ovk-muted, #93a0b4); padding: 0 10px; }
+.dm-mf-cols span:not(:first-child), .dm-mf-row > span:not(:first-child), .dm-mf-row b { text-align: right; }
+.dm-mf-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
+.dm-mf-row { padding: 9px 10px; border-radius: 10px; background: var(--ovk-bg, #0f1522); font-size: 13px; color: var(--ovk-muted, #93a0b4); }
+.dm-mf-row b { color: var(--ovk-text, #e8ecf3); }
+.dm-mf-reg { display: grid; gap: 1px; color: var(--ovk-text, #e8ecf3); min-width: 0; }
+.dm-mf-reg em { font-style: normal; font-size: 11px; font-weight: 700; color: var(--room-strong, var(--ovk-accent, #3b82f6)); }
+.dm-mf-reg em.is-trap { color: var(--ovk-warn-text, var(--ovk-warn, #f59e0b)); }
+.dm-mf-row.is-best { outline: 2px solid color-mix(in srgb, var(--room-accent, var(--ovk-accent, #3b82f6)) 55%, transparent); outline-offset: -2px; }
@@ -31,6 +31,20 @@ const esc = kit.esc;
@@ -31,6 +31,20 @@ const esc = kit.esc;
const PUBLIC = path.join(__dirname, '..', '..', 'public');
const PUBLIC = path.join(__dirname, '..', '..', 'public');
const hashes = new Map();
const hashes = new Map();
+/**
+ * The home's picture (MOTION-1, 2026-11-02: sc.room drew its default motif): one name, priced the way every page here
+ * prices it. Three registrars with the first year, the renewal and what three years really cost, the cheapest to keep
+ * marked, and the cheap first year that renews high flagged. A sample, said as one.
+ */
+function domainsMotif() {
+ const row = (name, y1, renew, three, mark = '') => `<li class="dm-mf-row${mark === 'best' ? ' is-best' : ''}"><span class="dm-mf-reg">${name}${mark === 'best' ? '<em>cheapest to keep</em>' : mark === 'trap' ? '<em class="is-trap">renews high</em>' : ''}</span><span>${y1}</span><span>${renew}</span><b>${three}</b></li>`;
+ return `<div class="dm-motif" aria-hidden="true">
+<div class="dm-mf-name"><b>sushitruck.dev</b><span class="dm-mf-ok">✓ available</span></div>
+<div class="dm-mf-cols"><span>registrar · sample</span><span>1st yr</span><span>renews</span><span>3 years</span></div>
+<ol class="dm-mf-list">${row('Porkbun', '$11', '$11', '$33', 'best')}${row('Namecheap', '$9', '$16', '$41')}${row('Budget Reg', '$2', '$22', '$46', 'trap')}</ol>
+</div>`;
+}
+
function asset(rel) {
function asset(rel) {
if (!hashes.has(rel)) {
if (!hashes.has(rel)) {
let v = 'dev';
let v = 'dev';
@@ -158,6 +172,7 @@ function home({ endings, lists, canonical } = {}) {
@@ -158,6 +172,7 @@ function home({ endings, lists, canonical } = {}) {
lede: 'Type a name. See every ending and every registrar, with what you will really pay over one, three and five years, and the renewal traps flagged.',
lede: 'Type a name. See every ending and every registrar, with what you will really pay over one, three and five years, and the renewal traps flagged.',
search: { action: '/search', name: 'q', placeholder: 'Price a name, like sushitruck', label: 'A name to price', button: 'Price it', hints: ['sushitruck', 'mybrand.dev', 'cozy cafe', 'pixel.studio'] },
search: { action: '/search', name: 'q', placeholder: 'Price a name, like sushitruck', label: 'A name to price', button: 'Price it', hints: ['sushitruck', 'mybrand.dev', 'cozy cafe', 'pixel.studio'] },
chips: popular.map((r) => ({ label: r.display || dot(r.tld), href: tldHref(r.tld) })),
chips: popular.map((r) => ({ label: r.display || dot(r.tld), href: tldHref(r.tld) })),
+ motif: domainsMotif(),
},
},
proof: [
proof: [
{ value: all.length, label: all.length === 1 ? 'ending priced' : 'endings priced', asOf: checked ? day(checked) : null },
{ value: all.length, label: all.length === 1 ? 'ending priced' : 'endings priced', asOf: checked ? day(checked) : null },
@@ -25,6 +25,12 @@ const { boot, check, done } = require('./helpers');
@@ -25,6 +25,12 @@ const { boot, check, done } = require('./helpers');
assert.ok(r.text.includes('<section class="sc-sec" id="faq"') || r.text.includes('aria-labelledby="sc-questions"'), 'the questions keep their section');
assert.ok(r.text.includes('<section class="sc-sec" id="faq"') || r.text.includes('aria-labelledby="sc-questions"'), 'the questions keep their section');
});
});
+ await check('the hero is one name priced, not sc.room\'s default motif (MOTION-1)', async () => {
+ const r = await t.get('/');
+ assert.match(r.text, /<div class="dm-motif" aria-hidden="true">/);
+ assert.match(r.text, /sushitruck\.dev[\s\S]*?cheapest to keep[\s\S]*?renews high/);
+ assert.ok(!/class="sc-room-motif"/.test(r.text), 'no default motif');
+ });
await check('the CSS guard: the folds are closed on a phone only, and the endings are one row', async () => {
await check('the CSS guard: the folds are closed on a phone only, and the endings are one row', async () => {
const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'domains.css'), 'utf8');
const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'domains.css'), 'utf8');
assert.match(css, /\.hm-fold-block > summary \{[^}]*min-height: 44px/, 'a fold is a 44 px target');
assert.match(css, /\.hm-fold-block > summary \{[^}]*min-height: 44px/, 'a fold is a 44 px target');