openvibers/openvibe.vision Commits
Vision v2: See it. Use it everywhere. Task-led home, network row, consent scene, trimmed nav, real switches (#3)
Changes
@@@ -102,3 +102,34 @@ html.ov-motion .vi-race-box { animation: vi-pop .6s cubic-bezier(.2,.9,.3,1.2) b@media (max-width: 759px) { .sc-room-pic .vi-race-box, .sc-room-pic .vi-race-tag { display: none; } }@media (prefers-reduced-motion: reduce) { .vi-race-img path, .vi-race-box { animation: none !important; } }html[data-ov-motion=reduced] .vi-race-img path, html[data-ov-motion=reduced] .vi-race-box { animation: none !important; }++++/* Vision v2: the task grid, the network row and the consent demo */++.vi-tasks { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 230px), 1fr)); }++.vi-task { display: grid; gap: 6px; height: 100%; padding: 16px 18px; border-radius: 18px; border: 1px solid var(--border, #1f2d47); background: var(--bg-card, #0f1729); color: inherit; text-decoration: none; transition: border-color .15s, transform .15s; }++.vi-task b { font-size: 1.05rem; color: var(--room-strong); }++.vi-task span { color: var(--text-secondary, #96a7c2); font-size: .9rem; line-height: 1.5; }++.vi-task:hover { border-color: var(--room-accent); transform: translateY(-1px); }++.vi-task:focus-visible { outline: 2px solid var(--room-accent); outline-offset: 2px; }++.vi-net { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr)); }++.vi-net li { display: grid; gap: 2px; padding: 11px 14px; border-radius: 14px; background: color-mix(in srgb, var(--room-accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--room-accent) 22%, transparent); }++.vi-net b { font-size: .9rem; }++.vi-net span { font-size: .82rem; color: var(--text-secondary, #96a7c2); }++.vi-demo-consent { list-style: none; margin: 0; padding: 14px; display: grid; gap: 10px; border-radius: 18px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }++.vi-demo-consent li { display: grid; grid-template-columns: auto 1fr; column-gap: 12px; align-items: center; }++.vi-demo-consent b { font-size: .95rem; }++.vi-demo-consent small { grid-column: 2; color: var(--text-muted, #7386a3); }++.vi-demo-switch { grid-row: span 2; position: relative; width: 38px; height: 22px; border-radius: 999px; background: var(--border, #1f2d47); }++.vi-demo-switch::after { content: ''; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: var(--text-muted, #7386a3); transition: transform .2s; }++.vi-demo-switch.is-on { background: color-mix(in srgb, var(--room-accent) 55%, transparent); }++.vi-demo-switch.is-on::after { transform: translateX(16px); background: var(--room-strong); }++@media (prefers-reduced-motion: reduce) { .vi-task, .vi-demo-switch::after { transition: none; } .vi-task:hover { transform: none; } }++++/* The consent switch: a real toggle (the checkbox keeps role="switch" for assistive tech) */++.vi-check:has(> input[role=switch]) { display: inline-flex; align-items: center; gap: 10px; padding: 0; border: 0; background: none; font-weight: 700; cursor: pointer; }++.vi-check > input[role=switch] { appearance: none; -webkit-appearance: none; position: relative; flex: none; width: 44px; height: 26px; margin: 0; border-radius: 999px; background: var(--border, #1f2d47); cursor: pointer; transition: background .2s; }++.vi-check > input[role=switch]::after { content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: var(--text-muted, #7386a3); transition: transform .2s, background .2s; }++.vi-check > input[role=switch]:checked { background: color-mix(in srgb, var(--room-accent) 55%, transparent); }++.vi-check > input[role=switch]:checked::after { transform: translateX(18px); background: var(--room-strong); }++.vi-check > input[role=switch]:focus-visible { outline: 2px solid var(--room-accent); outline-offset: 2px; }++.vi-check > input[role=switch]:disabled { opacity: .5; cursor: not-allowed; }++@media (prefers-reduced-motion: reduce) { .vi-check > input[role=switch], .vi-check > input[role=switch]::after { transition: none; } }@@@ -20,15 +20,11 @@ const TAGLINE = 'See with the best model for the job.';const PUBLIC_DIR = path.join(__dirname, '..', '..', 'public');const NAV = [{ label: 'Home', href: '/' },-- { label: 'Ask a picture', href: '/ask' },-- { label: 'OCR', href: '/ocr' },-- { label: 'Find', href: '/find' },++ { label: 'Look', href: '/ask' },{ label: 'Read', href: '/read' },++ { label: 'Find', href: '/find' },{ label: 'Watch', href: '/watch' },-- { label: 'Compare', href: '/compare' },-- { label: 'UI check', href: '/ui-check' },{ label: 'People', href: '/people' },-- { label: 'Privacy', href: '/people/privacy' },{ label: 'Developers', href: '/developers' },];@@@ -20,20 +20,22 @@ function motif() {}function home({ signedIn = false, ai = false } = {}) {-- const tryIt = `<section class="vi-section vi-try" id="vi-try" aria-labelledby="vi-try-h">--<div class="vi-head-row"><h2 id="vi-try-h">Ask a picture</h2><span class="vi-count">${ai ? 'local OCR first, then OpenVibe.AI' : 'local engines only on this host right now'}</span></div>--<p class="vi-lede">Describe, read, translate, identify or count. Upload a file or paste a public image URL; the local reader answers a read question before any model is called.</p>--<div class="vi-actions"><a class="vi-btn" href="/ask">Ask a picture</a><a class="vi-btn vi-btn-quiet" href="/ocr">Read text only</a><a class="vi-btn vi-btn-quiet" href="/compare">Compare models</a><a class="vi-btn vi-btn-quiet" href="/ui-check">Check a page</a></div>--${signedIn ? '' : '<p class="vi-fine">Ask and compare save an answer to your account, so they need <a href="/auth/login?next=%2Fask">an OpenVibe sign-in</a>. OCR and the UI check work without one.</p>'}--</section>`;++ const tryIt = `<section class="vi-section vi-tasks-wrap" id="vi-try" aria-labelledby="vi-try-h">++<div class="vi-head-row"><h2 id="vi-try-h">What do you want to do?</h2><span class="vi-count">${ai ? 'local engines first, then OpenVibe.AI' : 'local engines on this host'}</span></div>++<ul class="vi-tasks"><li><a class="vi-task" href="/ask"><b>Look</b><span>Ask anything about a picture: what it shows, how many, what it says.</span></a></li><li><a class="vi-task" href="/read"><b>Read</b><span>Receipts, signs, screenshots and documents, read on this host first, for nothing.</span></a></li><li><a class="vi-task" href="/ask"><b>Translate</b><span>A sign, a menu or a stream overlay, read and put into your language.</span></a></li><li><a class="vi-task" href="/find"><b>Find</b><span>Search your own pictures by words or by another picture. Only you see the results.</span></a></li><li><a class="vi-task" href="/watch"><b>Watch</b><span>Point it at a still from your camera and get told when a delivery, an animal or a change shows up.</span></a></li><li><a class="vi-task" href="/people"><b>People</b><span>Faces with your consent: verify yourself, find yourself, tag co-streamers who opted in, or just blur.</span></a></li><li><a class="vi-task" href="/compare"><b>Compare</b><span>One picture, several models, side by side, with what each answer cost and how long it took.</span></a></li><li><a class="vi-task" href="/ui-check"><b>Check a page</b><span>Overflow, clipped text, low contrast and overlap, found in HTML, a URL or a screenshot.</span></a></li></ul>++${signedIn ? '' : '<p class="vi-fine">Reading, blurring and the page check work without an account. Saving answers, your picture search and faces need <a href="/auth/login?next=%2F">an OpenVibe sign-in</a>.</p>'}++<p class="vi-fine"><a href="/people">Your face choices</a> · <a href="/people/privacy">How faces are kept</a></p>++</section>++<section class="vi-section vi-net-wrap" aria-labelledby="vi-net-h"><div class="vi-head-row"><h2 id="vi-net-h">Works across OpenVibe</h2><a class="vi-more" href="/developers">The API</a></div>++<ul class="vi-net"><li><b>Pics and Media</b><span>alt text for every image</span></li><li><b>Food</b><span>a receipt becomes your pantry</span></li><li><b>Deals</b><span>photograph it, compare offers</span></li><li><b>Cam</b><span>alerts on your own cameras</span></li><li><b>Live</b><span>scene changes and moments</span></li><li><b>Design</b><span>the page check</span></li><li><b>Search</b><span>search by picture</span></li><li><b>Bench</b><span>which model sees best, per dollar</span></li></ul></section>`;const room = sc.room({room: 'vision',hero: {-- title: 'See with the best model for the job',-- lede: 'Ask a picture. Read its text with a local OCR engine first. Compare the local reader with a routed vision model on the same image, with the cost and the time beside each answer. Check a page for overflow and layering faults. Local face tools use separate consent for each identifying purpose.',-- start: { href: '/ask', label: 'Ask a picture' },-- chips: [{ label: 'Read text', href: '/ocr' }, { label: 'Compare models', href: '/compare' }, { label: 'UI check', href: '/ui-check' }, { label: 'The API', href: '/developers' }],-- proof: [{ value: '0.0000', label: 'USD for the local OCR first pass' }],++ title: 'See it. Use it everywhere.',++ lede: 'Point Vision at a picture and do something with it: read a receipt into your pantry, find your own photos by words, get told when your camera sees a delivery, or check a page before it ships. Every answer says which model saw it, what it cost and what was kept.',++ start: { href: '/ask', label: 'Look at a picture' },++ chips: [{ label: 'Read', href: '/read' }, { label: 'Find', href: '/find' }, { label: 'Watch', href: '/watch' }, { label: 'People', href: '/people' }, { label: 'The API', href: '/developers' }],++ proof: [{ value: '0.0000', label: 'USD for a local read' }, { value: '8', label: 'tools, one API' }],motif: motif(),},scenes: [@@@ -55,6 +57,12 @@ ${signedIn ? '' : '<p class="vi-fine">Ask and compare save an answer to your acchtml: `<div class="vi-demo-check"><div class="vi-demo-page"><span class="vi-demo-overflow">this card is 1400px wide</span><span class="vi-demo-clip">a headline that gets cut off at the edge</span></div><ul><li class="is-bad"><b>overflow</b><span>1400px in a 390px view</span></li><li class="is-bad"><b>cut off</b><span>nowrap inside 120px</span></li><li class="is-bad"><b>contrast</b><span>2.4:1, needs 4.5:1</span></li></ul></div>`,action: { href: '/ui-check', label: 'Check a page' },},++ {++ title: 'Faces, only with consent',++ text: 'Each face feature is its own switch with its own consent: verify yourself, find yourself in your own pictures, tag co-streamers who enrolled, or group the people in your own library. Matching runs on this host; templates are encrypted and erased the moment you switch a feature off. Detecting, counting and blurring identify nobody. Vision never identifies a stranger.',++ html: `<ul class="vi-demo-consent"><li><span class="vi-demo-switch is-on" aria-hidden="true"></span><b>Verify yourself</b><small>your face, your account</small></li><li><span class="vi-demo-switch is-on" aria-hidden="true"></span><b>Find yourself</b><small>in your own pictures</small></li><li><span class="vi-demo-switch" aria-hidden="true"></span><b>Cast tags</b><small>co-streamers who enrolled</small></li><li><span class="vi-demo-switch" aria-hidden="true"></span><b>Library people</b><small>off unless you turn it on</small></li></ul>`,++ action: { href: '/people', label: 'Your choices' },++ },],faq: { items: [['Does Vision recognise faces?', 'Only for a person who separately opts into a specific purpose: self verification, self search, cast tagging or private library grouping. Detect and blur never identify anyone. Public AI text never describes a person\'s looks.'],
@@ -102,3 +102,34 @@ html.ov-motion .vi-race-box { animation: vi-pop .6s cubic-bezier(.2,.9,.3,1.2) b
@@ -102,3 +102,34 @@ html.ov-motion .vi-race-box { animation: vi-pop .6s cubic-bezier(.2,.9,.3,1.2) b
@media (max-width: 759px) { .sc-room-pic .vi-race-box, .sc-room-pic .vi-race-tag { display: none; } }
@media (max-width: 759px) { .sc-room-pic .vi-race-box, .sc-room-pic .vi-race-tag { display: none; } }
@media (prefers-reduced-motion: reduce) { .vi-race-img path, .vi-race-box { animation: none !important; } }
@media (prefers-reduced-motion: reduce) { .vi-race-img path, .vi-race-box { animation: none !important; } }
html[data-ov-motion=reduced] .vi-race-img path, html[data-ov-motion=reduced] .vi-race-box { animation: none !important; }
html[data-ov-motion=reduced] .vi-race-img path, html[data-ov-motion=reduced] .vi-race-box { animation: none !important; }
+
+/* Vision v2: the task grid, the network row and the consent demo */
+.vi-tasks { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 230px), 1fr)); }
+.vi-task { display: grid; gap: 6px; height: 100%; padding: 16px 18px; border-radius: 18px; border: 1px solid var(--border, #1f2d47); background: var(--bg-card, #0f1729); color: inherit; text-decoration: none; transition: border-color .15s, transform .15s; }
+.vi-task b { font-size: 1.05rem; color: var(--room-strong); }
+.vi-task span { color: var(--text-secondary, #96a7c2); font-size: .9rem; line-height: 1.5; }
+.vi-task:hover { border-color: var(--room-accent); transform: translateY(-1px); }
+.vi-task:focus-visible { outline: 2px solid var(--room-accent); outline-offset: 2px; }
+.vi-net { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr)); }
+.vi-net li { display: grid; gap: 2px; padding: 11px 14px; border-radius: 14px; background: color-mix(in srgb, var(--room-accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--room-accent) 22%, transparent); }
+.vi-net b { font-size: .9rem; }
+.vi-net span { font-size: .82rem; color: var(--text-secondary, #96a7c2); }
+.vi-demo-consent { list-style: none; margin: 0; padding: 14px; display: grid; gap: 10px; border-radius: 18px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }
+.vi-demo-consent li { display: grid; grid-template-columns: auto 1fr; column-gap: 12px; align-items: center; }
+.vi-demo-consent b { font-size: .95rem; }
+.vi-demo-consent small { grid-column: 2; color: var(--text-muted, #7386a3); }
+.vi-demo-switch { grid-row: span 2; position: relative; width: 38px; height: 22px; border-radius: 999px; background: var(--border, #1f2d47); }
+.vi-demo-switch::after { content: ''; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: var(--text-muted, #7386a3); transition: transform .2s; }
+.vi-demo-switch.is-on { background: color-mix(in srgb, var(--room-accent) 55%, transparent); }
+.vi-demo-switch.is-on::after { transform: translateX(16px); background: var(--room-strong); }
+@media (prefers-reduced-motion: reduce) { .vi-task, .vi-demo-switch::after { transition: none; } .vi-task:hover { transform: none; } }
+
+/* The consent switch: a real toggle (the checkbox keeps role="switch" for assistive tech) */
+.vi-check:has(> input[role=switch]) { display: inline-flex; align-items: center; gap: 10px; padding: 0; border: 0; background: none; font-weight: 700; cursor: pointer; }
+.vi-check > input[role=switch] { appearance: none; -webkit-appearance: none; position: relative; flex: none; width: 44px; height: 26px; margin: 0; border-radius: 999px; background: var(--border, #1f2d47); cursor: pointer; transition: background .2s; }
+.vi-check > input[role=switch]::after { content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: var(--text-muted, #7386a3); transition: transform .2s, background .2s; }
+.vi-check > input[role=switch]:checked { background: color-mix(in srgb, var(--room-accent) 55%, transparent); }
+.vi-check > input[role=switch]:checked::after { transform: translateX(18px); background: var(--room-strong); }
+.vi-check > input[role=switch]:focus-visible { outline: 2px solid var(--room-accent); outline-offset: 2px; }
+.vi-check > input[role=switch]:disabled { opacity: .5; cursor: not-allowed; }
+@media (prefers-reduced-motion: reduce) { .vi-check > input[role=switch], .vi-check > input[role=switch]::after { transition: none; } }
@@ -20,15 +20,11 @@ const TAGLINE = 'See with the best model for the job.';
@@ -20,15 +20,11 @@ const TAGLINE = 'See with the best model for the job.';
const PUBLIC_DIR = path.join(__dirname, '..', '..', 'public');
const PUBLIC_DIR = path.join(__dirname, '..', '..', 'public');
const NAV = [
const NAV = [
{ label: 'Home', href: '/' },
{ label: 'Home', href: '/' },
- { label: 'Ask a picture', href: '/ask' },
- { label: 'OCR', href: '/ocr' },
- { label: 'Find', href: '/find' },
+ { label: 'Look', href: '/ask' },
{ label: 'Read', href: '/read' },
{ label: 'Read', href: '/read' },
+ { label: 'Find', href: '/find' },
{ label: 'Watch', href: '/watch' },
{ label: 'Watch', href: '/watch' },
- { label: 'Compare', href: '/compare' },
- { label: 'UI check', href: '/ui-check' },
{ label: 'People', href: '/people' },
{ label: 'People', href: '/people' },
- { label: 'Privacy', href: '/people/privacy' },
{ label: 'Developers', href: '/developers' },
{ label: 'Developers', href: '/developers' },
];
];
@@ -20,20 +20,22 @@ function motif() {
@@ -20,20 +20,22 @@ function motif() {
}
}
function home({ signedIn = false, ai = false } = {}) {
function home({ signedIn = false, ai = false } = {}) {
- const tryIt = `<section class="vi-section vi-try" id="vi-try" aria-labelledby="vi-try-h">
-<div class="vi-head-row"><h2 id="vi-try-h">Ask a picture</h2><span class="vi-count">${ai ? 'local OCR first, then OpenVibe.AI' : 'local engines only on this host right now'}</span></div>
-<p class="vi-lede">Describe, read, translate, identify or count. Upload a file or paste a public image URL; the local reader answers a read question before any model is called.</p>
-<div class="vi-actions"><a class="vi-btn" href="/ask">Ask a picture</a><a class="vi-btn vi-btn-quiet" href="/ocr">Read text only</a><a class="vi-btn vi-btn-quiet" href="/compare">Compare models</a><a class="vi-btn vi-btn-quiet" href="/ui-check">Check a page</a></div>
-${signedIn ? '' : '<p class="vi-fine">Ask and compare save an answer to your account, so they need <a href="/auth/login?next=%2Fask">an OpenVibe sign-in</a>. OCR and the UI check work without one.</p>'}
-</section>`;
+ const tryIt = `<section class="vi-section vi-tasks-wrap" id="vi-try" aria-labelledby="vi-try-h">
+<div class="vi-head-row"><h2 id="vi-try-h">What do you want to do?</h2><span class="vi-count">${ai ? 'local engines first, then OpenVibe.AI' : 'local engines on this host'}</span></div>
+<ul class="vi-tasks"><li><a class="vi-task" href="/ask"><b>Look</b><span>Ask anything about a picture: what it shows, how many, what it says.</span></a></li><li><a class="vi-task" href="/read"><b>Read</b><span>Receipts, signs, screenshots and documents, read on this host first, for nothing.</span></a></li><li><a class="vi-task" href="/ask"><b>Translate</b><span>A sign, a menu or a stream overlay, read and put into your language.</span></a></li><li><a class="vi-task" href="/find"><b>Find</b><span>Search your own pictures by words or by another picture. Only you see the results.</span></a></li><li><a class="vi-task" href="/watch"><b>Watch</b><span>Point it at a still from your camera and get told when a delivery, an animal or a change shows up.</span></a></li><li><a class="vi-task" href="/people"><b>People</b><span>Faces with your consent: verify yourself, find yourself, tag co-streamers who opted in, or just blur.</span></a></li><li><a class="vi-task" href="/compare"><b>Compare</b><span>One picture, several models, side by side, with what each answer cost and how long it took.</span></a></li><li><a class="vi-task" href="/ui-check"><b>Check a page</b><span>Overflow, clipped text, low contrast and overlap, found in HTML, a URL or a screenshot.</span></a></li></ul>
+${signedIn ? '' : '<p class="vi-fine">Reading, blurring and the page check work without an account. Saving answers, your picture search and faces need <a href="/auth/login?next=%2F">an OpenVibe sign-in</a>.</p>'}
+<p class="vi-fine"><a href="/people">Your face choices</a> · <a href="/people/privacy">How faces are kept</a></p>
+</section>
+<section class="vi-section vi-net-wrap" aria-labelledby="vi-net-h"><div class="vi-head-row"><h2 id="vi-net-h">Works across OpenVibe</h2><a class="vi-more" href="/developers">The API</a></div>
+<ul class="vi-net"><li><b>Pics and Media</b><span>alt text for every image</span></li><li><b>Food</b><span>a receipt becomes your pantry</span></li><li><b>Deals</b><span>photograph it, compare offers</span></li><li><b>Cam</b><span>alerts on your own cameras</span></li><li><b>Live</b><span>scene changes and moments</span></li><li><b>Design</b><span>the page check</span></li><li><b>Search</b><span>search by picture</span></li><li><b>Bench</b><span>which model sees best, per dollar</span></li></ul></section>`;
const room = sc.room({
const room = sc.room({
room: 'vision',
room: 'vision',
hero: {
hero: {
- title: 'See with the best model for the job',
- lede: 'Ask a picture. Read its text with a local OCR engine first. Compare the local reader with a routed vision model on the same image, with the cost and the time beside each answer. Check a page for overflow and layering faults. Local face tools use separate consent for each identifying purpose.',
- start: { href: '/ask', label: 'Ask a picture' },
- chips: [{ label: 'Read text', href: '/ocr' }, { label: 'Compare models', href: '/compare' }, { label: 'UI check', href: '/ui-check' }, { label: 'The API', href: '/developers' }],
- proof: [{ value: '0.0000', label: 'USD for the local OCR first pass' }],
+ title: 'See it. Use it everywhere.',
+ lede: 'Point Vision at a picture and do something with it: read a receipt into your pantry, find your own photos by words, get told when your camera sees a delivery, or check a page before it ships. Every answer says which model saw it, what it cost and what was kept.',
+ start: { href: '/ask', label: 'Look at a picture' },
+ chips: [{ label: 'Read', href: '/read' }, { label: 'Find', href: '/find' }, { label: 'Watch', href: '/watch' }, { label: 'People', href: '/people' }, { label: 'The API', href: '/developers' }],
+ proof: [{ value: '0.0000', label: 'USD for a local read' }, { value: '8', label: 'tools, one API' }],
motif: motif(),
motif: motif(),
},
},
scenes: [
scenes: [
@@ -55,6 +57,12 @@ ${signedIn ? '' : '<p class="vi-fine">Ask and compare save an answer to your acc
@@ -55,6 +57,12 @@ ${signedIn ? '' : '<p class="vi-fine">Ask and compare save an answer to your acc
html: `<div class="vi-demo-check"><div class="vi-demo-page"><span class="vi-demo-overflow">this card is 1400px wide</span><span class="vi-demo-clip">a headline that gets cut off at the edge</span></div><ul><li class="is-bad"><b>overflow</b><span>1400px in a 390px view</span></li><li class="is-bad"><b>cut off</b><span>nowrap inside 120px</span></li><li class="is-bad"><b>contrast</b><span>2.4:1, needs 4.5:1</span></li></ul></div>`,
html: `<div class="vi-demo-check"><div class="vi-demo-page"><span class="vi-demo-overflow">this card is 1400px wide</span><span class="vi-demo-clip">a headline that gets cut off at the edge</span></div><ul><li class="is-bad"><b>overflow</b><span>1400px in a 390px view</span></li><li class="is-bad"><b>cut off</b><span>nowrap inside 120px</span></li><li class="is-bad"><b>contrast</b><span>2.4:1, needs 4.5:1</span></li></ul></div>`,
action: { href: '/ui-check', label: 'Check a page' },
action: { href: '/ui-check', label: 'Check a page' },
},
},
+ {
+ title: 'Faces, only with consent',
+ text: 'Each face feature is its own switch with its own consent: verify yourself, find yourself in your own pictures, tag co-streamers who enrolled, or group the people in your own library. Matching runs on this host; templates are encrypted and erased the moment you switch a feature off. Detecting, counting and blurring identify nobody. Vision never identifies a stranger.',
+ html: `<ul class="vi-demo-consent"><li><span class="vi-demo-switch is-on" aria-hidden="true"></span><b>Verify yourself</b><small>your face, your account</small></li><li><span class="vi-demo-switch is-on" aria-hidden="true"></span><b>Find yourself</b><small>in your own pictures</small></li><li><span class="vi-demo-switch" aria-hidden="true"></span><b>Cast tags</b><small>co-streamers who enrolled</small></li><li><span class="vi-demo-switch" aria-hidden="true"></span><b>Library people</b><small>off unless you turn it on</small></li></ul>`,
+ action: { href: '/people', label: 'Your choices' },
+ },
],
],
faq: { items: [
faq: { items: [
['Does Vision recognise faces?', 'Only for a person who separately opts into a specific purpose: self verification, self search, cast tagging or private library grouping. Detect and blur never identify anyone. Public AI text never describes a person\'s looks.'],
['Does Vision recognise faces?', 'Only for a person who separately opts into a specific purpose: self verification, self search, cast tagging or private library grouping. Detect and blur never identify anyone. Public AI text never describes a person\'s looks.'],