28 lines
20 KiB
HTML
28 lines
20 KiB
HTML
<!doctype html>
|
||
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><meta name="color-scheme" content="light"><title>Hope · Ribbon</title><link rel="icon" href="ribbon-icon.svg"><style>
|
||
:root{--white:#fff;--ink:#302438;--muted:#73677d;--pink:#edb1ce;--lavender:#b8a6e8;--blue:#adcef3;--surface:#f6f4f9;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:var(--ink);background:#faf9fc;font-synthesis:none}*{box-sizing:border-box}body{margin:0}button,input,select{font:inherit}button,a,input,select{touch-action:manipulation}button{color:inherit;cursor:pointer;min-width:44px;min-height:44px;border:0;background:none}button:hover{background-color:#f1edf6}button:disabled{opacity:.38;cursor:default}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible{outline:3px solid #8060a6;outline-offset:4px}a{color:inherit}svg{display:block}[hidden]{display:none!important}.showcase{max-width:1150px;margin:auto;min-height:100vh;display:grid;grid-template-columns:1fr 430px;gap:120px;align-items:center;padding:45px 60px}.intro{max-width:450px}.back{display:inline-flex;align-items:center;min-height:44px;font-size:14px;text-decoration:none;color:var(--muted)}.direction{font-size:14px;margin-top:50px}.intro h1{font-size:76px;font-weight:450;letter-spacing:-.035em;line-height:1;margin:20px 0 25px}.intro .lead{font-size:23px;line-height:1.5;letter-spacing:-.02em;margin:0;max-width:26ch}.description{font-size:15px;line-height:1.7;color:var(--muted);max-width:38ch;margin:25px 0 0}.identity{display:flex;align-items:center;gap:28px;margin:40px 0}.large-icon{width:104px;height:104px;filter:drop-shadow(0 8px 12px #30243816)}.small-icon{width:32px;height:32px;margin-bottom:12px}.identity span{font-size:12px;color:var(--muted)}.note{font-size:12px;line-height:1.7;color:var(--muted);max-width:42ch}.phone{width:410px;padding:9px;background:linear-gradient(120deg,#fefefe,#e5e3e8 45%,#fff 75%);border-radius:55px;box-shadow:0 25px 70px #43335016,0 2px 3px #fff inset}.screen{width:390px;height:800px;position:relative;overflow:hidden;background:white;border-radius:46px;display:flex;flex-direction:column}.statusbar{height:56px;flex:none;display:flex;align-items:center;justify-content:space-between;padding:0 29px;font-size:14px;font-weight:650}.island{position:absolute;left:50%;top:14px;transform:translateX(-50%);width:108px;height:29px;background:#211e25;border-radius:24px}.signals{font-size:11px;letter-spacing:2px}.topbar{display:flex;align-items:center;justify-content:space-between;padding:9px 25px 0;height:67px}.wordmark{font-size:25px;font-weight:550;letter-spacing:-.04em}.top-actions{display:flex;gap:3px}.icon{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}.icon-button{display:grid;place-items:center;border-radius:50%}.voice-home{position:relative;flex:1;min-height:0;display:flex;flex-direction:column}.welcome{padding:27px 29px 0}.welcome h2{font-size:45px;line-height:1.09;font-weight:450;letter-spacing:-.035em;margin:0}.welcome p{font-size:14px;line-height:1.6;color:var(--muted);margin:14px 0 0}.sculpture{position:relative;height:180px;flex:none;margin-top:0;display:grid;place-items:center}.mesh{width:460px;height:190px;position:absolute;left:50%;transform:translateX(-50%)}.mesh path{fill:none;stroke:url(#ribbon-gradient);stroke-width:.7}.sculpture:before{content:"";position:absolute;width:310px;height:100px;background:radial-gradient(ellipse,#e8dcf54a,transparent 70%);filter:blur(15px)}.voice-status{text-align:center;min-height:56px;margin-top:-6px}.voice-status strong{font-weight:500;font-size:15px;display:block}.timer{font-variant-numeric:tabular-nums;font-size:13px;color:var(--muted);display:block;margin-top:7px;letter-spacing:.08em}.controls{display:flex;align-items:center;justify-content:center;gap:31px;height:143px;flex:none}.minor{width:46px;height:46px;border-radius:50%;background:#f7f5fa;display:grid;place-items:center}.mic{width:83px;height:83px;border-radius:50%;position:relative;background:linear-gradient(130deg,#fff 25%,#f1e9f8 85%);box-shadow:0 6px 18px #ad91bf20,inset 0 0 0 1px #fff;display:grid;place-items:center;isolation:isolate}.mic:before,.mic:after{content:"";position:absolute;z-index:-1;border-radius:50%;inset:-14px;background:conic-gradient(from 50deg,#d9e6fa77,#eee2fb55,#f6d8e977,#d9e6fa77)}.mic:after{inset:-27px;background:conic-gradient(from 220deg,#f7e4ed55,#e8e4f944,#e0edfa55,#f7e4ed55)}.mic .icon{width:30px;height:30px;stroke-width:1.45}.state-listening .mic:before{animation:breathe 2.8s ease-in-out infinite}.state-working .mic:before{animation:breathe 1.6s ease-in-out infinite}.transcript-toggle{align-self:center;font-size:12px;color:var(--muted);margin-top:-5px}.composer{margin:9px 23px 0;padding:6px 7px 6px 18px;display:flex;align-items:center;background:#fbfafd;box-shadow:0 5px 24px #6d4c9010;border-radius:50px;gap:7px}.composer input{width:100%;min-width:0;border:0;background:none;font-size:13px;color:var(--ink);height:40px;outline-offset:2px}.composer input::placeholder{color:var(--muted)}.composer button{border-radius:50%;display:grid;place-items:center;background:#f1ecf7}.demo-note{text-align:center;font-size:10px;color:var(--muted);margin:15px 15px 0;line-height:1.5}.home-indicator{height:5px;background:#322d39;border-radius:8px;width:124px;margin:17px auto 9px;flex:none}.caption{text-align:center;font-size:11px;color:var(--muted);margin-top:18px}.panel{flex:1;padding:35px 27px;overflow:auto}.panel h2{font-size:34px;letter-spacing:-.025em;font-weight:450;margin:0 0 14px}.panel p{font-size:14px;line-height:1.65;color:var(--muted)}.row{padding:18px 0;border-bottom:1px solid #efebf4;display:block;text-align:left;width:100%;font-size:15px}.row small{display:block;font-size:12px;margin-top:7px;color:var(--muted)}.setting{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:20px 0;border-bottom:1px solid #efebf4;font-size:14px}.setting select{min-height:44px;border:0;border-radius:8px;padding:7px;background:var(--surface);color:var(--ink)}.setting input{width:44px;height:44px;accent-color:#8167a1}.sheet{position:absolute;bottom:0;left:0;right:0;max-height:60%;overflow:auto;background:#fff;border-radius:22px 22px 0 0;box-shadow:0 -15px 60px #30243818;padding:22px 25px 35px;z-index:2}.sheet-head{display:flex;align-items:center;justify-content:space-between;gap:10px}.sheet h3{font-weight:550;font-size:18px;margin:0}.sheet p{font-size:14px;line-height:1.65;margin:10px 0 20px}.sheet small{font-size:11px;color:var(--muted)}.sheet .close{border-radius:50%;font-size:23px}.sr-only{position:absolute;clip:rect(0,0,0,0);width:1px;height:1px;overflow:hidden}.text-mode .sculpture{height:190px}.text-mode .controls{display:none}.text-mode .voice-status{margin-bottom:40px}.text-mode .composer{margin-top:36px}.text-mode .composer input{font-size:14px}.mode-label{font-size:11px;margin:5px 0 0;text-align:center;color:var(--muted)}@keyframes breathe{50%{transform:scale(1.07);opacity:.65}}
|
||
@media(max-width:850px){.showcase{gap:45px;padding:35px;grid-template-columns:1fr 410px}.intro h1{font-size:60px}.intro .lead{font-size:19px}}
|
||
@media(max-width:700px){body{background:white}.showcase{display:block;padding:0;min-height:100dvh}.intro,.caption{display:none}.device-wrap{width:100%;display:flex;justify-content:center}.phone{width:100%;padding:0;border-radius:0;background:white;box-shadow:none}.screen{width:100%;max-width:430px;height:max(800px,100dvh);border-radius:0;margin:auto}.mobile-back{display:inline-flex!important;position:absolute;top:7px;left:16px;z-index:3;font-size:12px;min-height:44px;align-items:center;text-decoration:none}.statusbar{visibility:hidden}.island{display:none}.welcome{padding-top:26px}.sculpture{margin-top:20px}.voice-home{justify-content:flex-start}.demo-note{margin-top:auto;padding-top:13px}.home-indicator{margin-bottom:max(9px,env(safe-area-inset-bottom))}}
|
||
.welcome{padding-top:17px}.welcome h2{font-size:42px;line-height:1.05}.welcome p{margin-top:11px;font-size:13px}.sculpture{height:137px;margin-top:0}.voice-status{min-height:49px}.controls{height:111px}.transcript-toggle{min-height:44px;margin-top:0}.composer{margin-top:2px}.demo-note{font-size:9px;margin-top:12px}.text-mode .sculpture{height:150px}.text-mode .voice-status{margin-bottom:15px}@media(max-width:700px){.statusbar{height:44px}.topbar{height:60px}.welcome{padding-top:16px}.sculpture{height:142px;margin-top:0}.controls{height:108px}.demo-note{margin-top:auto;padding-top:8px}.transcript-toggle{min-height:44px}.home-indicator{margin-top:12px}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important}}
|
||
</style></head><body>
|
||
<svg width="0" height="0" aria-hidden="true" style="position:absolute"><defs><symbol id="mic-icon" viewBox="0 0 24 24"><rect x="9" y="2" width="6" height="13" rx="3"/><path d="M5 10v3a7 7 0 0 0 14 0v-3M12 20v2M8 22h8"/></symbol><symbol id="activity-icon" viewBox="0 0 24 24"><path d="M4 5h16M4 12h16M4 19h10"/></symbol><symbol id="settings-icon" viewBox="0 0 24 24"><path d="M4 6h16M4 18h16"/><circle cx="9" cy="6" r="3" fill="white"/><circle cx="16" cy="18" r="3" fill="white"/></symbol><symbol id="send-icon" viewBox="0 0 24 24"><path d="M12 19V5m-6 6 6-6 6 6"/></symbol></defs></svg>
|
||
<main class="showcase"><aside class="intro"><a class="back" href="new-index.html">← Explore the new directions</a><p class="direction">Hope / Ribbon</p><h1>A voice,<br>given shape.</h1><p class="lead">Room for your thoughts.<br>A little less interface.</p><p class="description">A diaphanous ribbon follows the conversation. Pink folds into lavender, then blue. Your voice is the centre; the transcript is a quiet companion.</p><div class="identity"><img class="large-icon" src="ribbon-icon.svg" alt="Ribbon R app icon"><div><img class="small-icon" src="ribbon-icon.svg" alt="Ribbon icon at 32 pixels"><span>A folded R. Two strands, one voice.</span></div></div><p class="note">Interactive design study. All conversations are simulated sample data. No network requests, microphone permissions, audio playback, or account connection.</p></aside><section class="device-wrap" aria-label="Interactive Ribbon phone"><div><div class="phone"><div class="screen" id="screen"><a class="mobile-back" href="new-index.html" hidden>← New directions</a><div class="statusbar" aria-hidden="true"><span>9:41</span><span class="island"></span><span class="signals">▮▮▮ ◒ ▰</span></div><header class="topbar"><span class="wordmark">hope</span><div class="top-actions"><button class="icon-button" id="activity" aria-label="Open Activity"><svg class="icon"><use href="#activity-icon"/></svg></button><button class="icon-button" id="settings" aria-label="Open Settings"><svg class="icon"><use href="#settings-icon"/></svg></button></div></header>
|
||
<section class="voice-home" id="home"><div class="welcome"><h2 id="headline">Say it<br>your way.</h2><p id="subline">A thought, a question, a little of everything.<br>Start wherever you are.</p></div><div class="sculpture" aria-hidden="true"><svg class="mesh" viewBox="0 0 440 230"><defs><linearGradient id="ribbon-gradient" x1="0" x2="1"><stop stop-color="#eeb5cf"/><stop offset=".35" stop-color="#e8b4db"/><stop offset=".62" stop-color="#bfa7e4"/><stop offset="1" stop-color="#a6c9ec"/></linearGradient></defs><g id="mesh-lines"></g></svg></div><div class="voice-status" role="status" aria-live="polite"><strong id="status">A little space to speak</strong><span class="timer" id="timer" aria-label="Elapsed recording time">00:00</span></div><div class="controls"><button class="minor" id="pause" aria-label="Pause simulated recording" disabled><svg class="icon" viewBox="0 0 24 24"><path d="M9 6v12M15 6v12"/></svg></button><button class="mic" id="mic" aria-label="Start simulated listening"><svg class="icon"><use href="#mic-icon"/></svg></button><button class="minor" id="end" aria-label="End simulated recording" disabled><svg class="icon" viewBox="0 0 24 24"><rect x="7" y="7" width="10" height="10" rx="1"/></svg></button></div><button id="transcript-toggle" class="transcript-toggle" aria-expanded="false" aria-controls="transcript-sheet">Show transcript ↗</button><form class="composer" id="composer"><label class="sr-only" for="message">Type a message to Hope</label><input id="message" placeholder="Or let a few words out…" maxlength="1000" autocomplete="off"><button id="text-button" type="button" aria-label="Switch to text mode"><svg class="icon" viewBox="0 0 24 24"><path d="M5 7h14M12 7v11M8 18h8"/></svg></button><button id="send" type="submit" aria-label="Send sample text message" hidden><svg class="icon"><use href="#send-icon"/></svg></button></form><p class="mode-label" id="mode-label" hidden>Text mode · <button id="voice-button" type="button">Back to voice</button></p><p class="demo-note">SIMULATED DEMO · Your microphone stays off<br>Sample replies only. Nothing leaves this page.</p></section>
|
||
<section class="panel" id="activity-panel" hidden><button class="back" id="activity-back">← Back to voice</button><h2>Activity</h2><p>A few thoughts you’ve shared.<br>Local sample data, not a real history.</p><div id="events"></div><button class="row" id="sample">A little room in the day<small>Today · Sample conversation · View transcript ↗</small></button><div class="row">A train to the coast<small>Yesterday · Sample conversation</small></div></section><section class="panel" id="settings-panel" hidden><button class="back" id="settings-back">← Back to voice</button><h2>Make it yours.</h2><p>Preferences for this prototype only.<br>No audio is played.</p><label class="setting">Sample voice<select id="voice"><option>Ava</option><option>Emma</option><option>Andrew</option></select></label><label class="setting">Spoken replies<input id="spoken" type="checkbox" checked></label><p id="preference-note" role="status">Your preferences stay here until you reload.</p><h3>Private by design</h3><p>This is a local, simulated design study. It does not request your microphone, contact a server, or connect to an account.</p></section><section class="sheet" id="transcript-sheet" aria-label="Sample transcript" hidden><div class="sheet-head"><h3>A few words, kept here.</h3><button class="close" id="close-sheet" aria-label="Close transcript">×</button></div><p id="empty-transcript">Your transcript will appear here after a simulated voice turn or text message.</p><div id="transcript" role="log" aria-live="polite"></div><small>Sample transcript · No audio was recorded</small></section><div class="home-indicator" aria-hidden="true"></div></div></div><p class="caption">RIBBON · Voice-first, sculptural, quietly alive.</p></div></section></main>
|
||
<script>
|
||
(()=>{'use strict';const $=id=>document.getElementById(id),reduced=matchMedia('(prefers-reduced-motion: reduce)');let state='idle',elapsed=0,last=0,phase=0,mode='voice',tick=null,replyTimer=null,focusReturn=null;const sampleQuestion='Help me find a little room in my day.',sampleReply='Let one thing wait. Take ten minutes for a walk, a slow cup of tea, or simply nothing. You don’t have to fill every little space.';const paths=[];for(let i=0;i<72;i++){const p=document.createElementNS('http://www.w3.org/2000/svg','path');p.setAttribute('opacity',String(.2+.35*Math.sin(Math.PI*i/71)));$('mesh-lines').append(p);paths.push(p)}
|
||
function draw(t=0){paths.forEach((p,i)=>{const v=(i/71-.5)*2;let d='';for(let x=0;x<=440;x+=4){const u=x/440,envelope=Math.pow(Math.sin(Math.PI*u),.75);const center=115+32*Math.sin(u*Math.PI*2-1.15+t*.13)*envelope;const spread=(48+27*Math.cos(u*Math.PI*3+.3))*envelope;const twist=Math.sin(u*Math.PI*2.4+v*1.3+t*.25)*25*envelope;const y=center+v*spread*Math.cos(u*Math.PI*1.7+t*.08)+twist;d+=(x?'L':'M')+x.toFixed(1)+','+y.toFixed(2)}p.setAttribute('d',d)})}draw();function frame(time){if(!reduced.matches&&['listening','working','reply'].includes(state)&&time-last>65){phase+=.035;draw(phase);last=time}requestAnimationFrame(frame)}requestAnimationFrame(frame);
|
||
function update(next){state=next;$('home').classList.remove('state-listening','state-working');if(['listening','working'].includes(next))$('home').classList.add('state-'+next);const labels={idle:'A little space to speak',listening:'Listening to you…',paused:'Take your time. Paused.',working:'Finding the words…',reply:'A little room, just for you.'};$('status').textContent=labels[next];$('mic').disabled=next==='working';$('mic').setAttribute('aria-label',next==='listening'?'Finish simulated listening':'Start simulated listening');$('pause').disabled=!['listening','paused'].includes(next);$('pause').setAttribute('aria-label',next==='paused'?'Resume simulated recording':'Pause simulated recording');$('pause').setAttribute('aria-pressed',String(next==='paused'));$('end').disabled=!['listening','paused','working'].includes(next)}
|
||
function clock(){const s=Math.floor(elapsed);$('timer').textContent=String(Math.floor(s/60)).padStart(2,'0')+':'+String(s%60).padStart(2,'0')}
|
||
function clear(){clearInterval(tick);clearTimeout(replyTimer);tick=null;replyTimer=null}
|
||
function add(role,text){$('empty-transcript').hidden=true;const section=document.createElement('div'),label=document.createElement('small'),p=document.createElement('p');label.textContent=role;p.textContent=text;section.append(label,p);$('transcript').append(section)}
|
||
function event(text){const row=document.createElement('div');row.className='row';row.textContent=text;const small=document.createElement('small');small.textContent='Just now · Local demo';row.append(small);$('events').prepend(row)}
|
||
function finish(text=sampleQuestion){clear();add('You',text);update('working');replyTimer=setTimeout(()=>{add('Hope',sampleReply);update('reply');$('headline').textContent='A little room.\nJust for you.';$('headline').style.whiteSpace='pre-line';$('subline').textContent='A sample reply, ready when you are.';event(mode==='text'?'A written thought':'A little room in the day');},1500)}
|
||
function start(){clear();elapsed=0;clock();$('headline').textContent='Say it\nyour way.';$('headline').style.whiteSpace='pre-line';$('subline').textContent='This is simulated listening. Your mic is off.';update('listening');tick=setInterval(()=>{if(state==='listening'){elapsed+=.25;clock();if(elapsed>=5)finish()}},250)}
|
||
function page(which){clear();update('idle');closeSheet();$('home').hidden=which!=='home';$('activity-panel').hidden=which!=='activity';$('settings-panel').hidden=which!=='settings';if(which!=='home')$(which+'-back').focus()}
|
||
function openSheet(){focusReturn=document.activeElement;$('transcript-sheet').hidden=false;$('transcript-toggle').setAttribute('aria-expanded','true');$('close-sheet').focus()}
|
||
function closeSheet(){if($('transcript-sheet').hidden)return;$('transcript-sheet').hidden=true;$('transcript-toggle').setAttribute('aria-expanded','false');if(focusReturn&&focusReturn.isConnected)focusReturn.focus()}
|
||
function setMode(next){clear();update('idle');mode=next;$('home').classList.toggle('text-mode',next==='text');$('text-button').hidden=next==='text';$('send').hidden=next!=='text';$('mode-label').hidden=next!=='text';$('status').textContent=next==='text'?'Some things are easier to write.':'A little space to speak';$('message').placeholder=next==='text'?'Write what’s on your mind…':'Or let a few words out…';if(next==='text')$('message').focus()}
|
||
$('mic').onclick=()=>state==='listening'?finish():start();$('pause').onclick=()=>update(state==='paused'?'listening':'paused');$('end').onclick=()=>{clear();update('idle');$('subline').textContent='The simulated recording has ended. Start again anytime.'};$('activity').onclick=()=>page('activity');$('settings').onclick=()=>page('settings');$('activity-back').onclick=$('settings-back').onclick=()=>page('home');$('transcript-toggle').onclick=openSheet;$('close-sheet').onclick=closeSheet;document.addEventListener('keydown',e=>{if(e.key==='Escape')closeSheet()});$('text-button').onclick=()=>setMode('text');$('voice-button').onclick=()=>setMode('voice');$('message').addEventListener('focus',()=>{if(mode==='voice')setMode('text')});$('composer').onsubmit=e=>{e.preventDefault();const text=$('message').value.trim();if(!text){$('message').focus();return}if(mode!=='text')setMode('text');$('message').value='';finish(text)};$('sample').onclick=()=>{if(!$('transcript').children.length){add('You',sampleQuestion);add('Hope',sampleReply)}openSheet()};$('voice').onchange=()=>{$('preference-note').textContent=$('voice').value+' selected for the simulated voice. No audio plays.'};$('spoken').onchange=()=>{$('preference-note').textContent='Spoken replies '+($('spoken').checked?'enabled':'disabled')+' as a demo preference. No audio plays.'};
|
||
})();
|
||
</script></body></html> |