@keyframes pulse{50%{opacity:.25}}@keyframes fold{from{transform:rotate(-20deg) scaleY(.4) translateY(-10%)}to{transform:rotate(25deg) scaleY(.9) translateY(10%)}}
.relay{position:relative;left:50%;width:100vw;margin-left:-50vw;overflow-x:clip;text-align:left}
.relay img{opacity:1!important;filter:none!important;visibility:visible!important}
html:has(.relay),body:has(.relay){overflow-x:clip}
.relay {
&{--accent:#E0336B;--ink:#232323;--paper:#fff;--paper-2:#f6f4f2;--mute:#6b6b6b}
*{box-sizing:border-box;margin:0;padding:0}
&{background:var(--paper);color:var(--ink);font:400 17px/1.55 "DM Sans",sans-serif;overflow-x:hidden}
img{display:block;width:100%;height:auto}
a{color:inherit}
h2,h3,.num{font-family:"Bricolage Grotesque",sans-serif;font-weight:800;letter-spacing:-.03em;line-height:.95}
.wrap{max-width:1360px;margin:0 auto;padding:0 16px}
@media(min-width:768px){.wrap{padding:0 48px}}
section{padding:96px 0}
.eyebrow{font-size:12px;letter-spacing:.2em;text-transform:uppercase;color:var(--mute);margin-bottom:14px}
h2{font-size:clamp(40px,6vw,84px)}
#work h2{font-size:clamp(32px,4vw,56px)}
.lede{font-size:20px;max-width:52ch;margin-top:22px;color:var(--mute)}
.tag{display:inline-block;font-size:11px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;padding:5px 9px;background:var(--ink);color:#fff}
.tag.pub{background:var(--accent)}
/* trusted by */
.trusted{padding:72px 0;border-top:1px solid #e6e3e0}
.trusted .eyebrow{text-align:center}
.logos{display:grid;grid-template-columns:repeat(2,1fr);gap:40px 24px;align-items:center;justify-items:center;margin-top:36px}
@media(min-width:768px){.logos{grid-template-columns:repeat(4,1fr)}}
.logos li{list-style:none;height:88px;display:flex;align-items:center}
.logos img{width:auto;max-width:180px;max-height:88px;object-fit:contain}
.logos img.sq{border-radius:12px}
.logos .wm{font:800 26px/1 "Bricolage Grotesque",sans-serif;letter-spacing:-.03em;white-space:nowrap}
/* nav */
nav{position:fixed;inset:0 0 auto;z-index:50;transition:background .35s}
nav.solid{background:var(--accent)}
nav .wrap{display:flex;justify-content:space-between;align-items:center;height:68px}
.mark{font:700 30px/1 Fredoka,sans-serif;color:#fff;letter-spacing:-.02em;text-decoration:none}
.mark small{font:600 13px "DM Sans",sans-serif;letter-spacing:.22em;text-transform:uppercase;margin-left:12px;opacity:.85;vertical-align:middle}
@media(max-width:480px){.mark small{margin-left:8px;font-size:11px;letter-spacing:.18em}}
.mark i{display:inline-block;width:8px;height:8px;border-radius:50%;background:#fff;margin-left:2px;animation:pulse 2s infinite}
nav ul{display:flex;gap:28px;list-style:none}
nav a{color:#fff;text-decoration:none;font-weight:500;font-size:15px}
@media(max-width:640px){nav ul li:not(:last-child){display:none}}
/* hero */
.hero{position:relative;height:100svh;background:#1a0f12;overflow:hidden;display:grid;place-items:center;color:#fff}
.ribbon{position:absolute;width:140vmax;height:40vmax;border-radius:50%;filter:blur(2px);background:radial-gradient(closest-side,var(--accent),transparent);opacity:.9;animation:fold 6s ease-in-out infinite alternate}
.ribbon.b{animation-duration:7.5s;transform:rotate(60deg);opacity:.55;filter:blur(30px)}
.hero svg{position:relative;width:min(560px,86vw)}
.cue{position:absolute;bottom:28px;font-size:12px;letter-spacing:.2em;text-transform:uppercase;opacity:.7}
/* work */
.filters{display:flex;flex-wrap:wrap;gap:8px;margin:28px 0 36px}
.filters button{font:500 14px "DM Sans";border:1.5px solid var(--ink);background:none;padding:8px 16px;border-radius:99px;cursor:pointer}
.filters button.on{background:var(--ink);color:#fff}
.grid{display:flex;gap:10px;align-items:flex-start}
.grid .col{flex:1;min-width:0;display:flex;flex-direction:column;gap:10px}
.tile{position:relative;overflow:hidden;display:block;text-decoration:none}
.tile img{transition:transform .5s cubic-bezier(.2,.7,.2,1)}
.tile:hover img{transform:scale(1.02)}
.tile.vid::after{content:"";position:absolute;right:14px;bottom:14px;width:52px;height:52px;border-radius:50%;background:rgba(0,0,0,.55) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23fff' d='M8 5v14l11-7z'/%3E%3C/svg%3E") 55% 50%/24px no-repeat;pointer-events:none;transition:transform .3s,background-color .3s}
.tile.vid:hover::after{transform:scale(1.1);background-color:var(--accent)}
.tile.vid img{aspect-ratio:16/9;object-fit:cover}
.tile.vid img[alt^="Julia"]{object-position:center 12%}
.tile .tag{position:absolute;top:12px;left:12px;z-index:1}
.tile.vid .tag,.tile[data-f="email"] .tag{left:auto;right:12px}
.tile .cap{position:absolute;inset:auto 0 0;padding:48px 16px 14px;color:#fff;background:linear-gradient(transparent,rgba(0,0,0,.72))}
.tile.vid .cap{padding-right:80px}
.tile.vid .cap span{font-size:13px;line-height:1.4}
.tile.vid:hover .cap,.tile.vid:focus-visible .cap{inset:0;display:flex;flex-direction:column;justify-content:flex-end;padding:16px;background:rgba(0,0,0,.84)}
.tile.vid:hover .cap span,.tile.vid:focus-visible .cap span{max-height:none}
.tile.vid:hover::after,.tile.vid:focus-visible::after{opacity:0}
.tile .cap b{display:block;font-size:15px;line-height:1.25}
.tile .cap span{display:block;font-size:14px;line-height:1.35;max-height:0;opacity:0;overflow:hidden;transition:max-height .35s,opacity .35s}
.tile:hover .cap span,.tile:focus-visible .cap span{max-height:6em;opacity:1;margin-top:4px}
.cap b{display:block;font-family:"Bricolage Grotesque";font-size:22px;letter-spacing:-.02em}
.cap span{font-size:14px;opacity:.9}
@media(hover:none){.tile .cap span{max-height:6em;opacity:1;margin-top:4px}
.tile.vid .cap,.tile.vid:hover .cap,.tile.vid:focus-visible .cap{position:relative;inset:auto;display:block;padding:16px 84px 18px 16px;background:var(--ink)}
.tile.vid .cap span,.tile.vid:hover .cap span,.tile.vid:focus-visible .cap span{display:block;max-height:none;opacity:1;margin-top:6px}
.tile.vid::after,.tile.vid:hover::after,.tile.vid:focus-visible::after{display:none}
.tile.vid .cap::after{content:"";position:absolute;right:14px;top:50%;width:52px;height:52px;margin-top:-26px;border-radius:50%;background:rgba(255,255,255,.14) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23fff' d='M8 5v14l11-7z'/%3E%3C/svg%3E") 55% 50%/24px no-repeat}}
.note{margin-top:18px;font-size:14px;color:var(--mute)}
/* featured project */
.feature{margin-top:36px;display:grid;gap:32px;align-items:center}
@media(min-width:900px){.feature{grid-template-columns:1.3fr 1fr;gap:56px}}
.feature-main{position:relative;overflow:hidden}
.feature-main a{display:block}
.feature h3 a{text-decoration:none}
.feature h3 a:hover{text-decoration:underline;text-underline-offset:6px}
.feature .note a{font-weight:700;color:var(--ink);font-size:16px}
.feature-main .tag{position:absolute;top:16px;left:16px}
.feature-body{display:grid;gap:32px}
.feature h3{font-size:clamp(36px,4.4vw,64px)}
.feature h3 span{color:var(--accent)}
.feature-line{font-size:20px;margin:14px 0 22px;max-width:36ch}
.feature ul{list-style:none;display:grid;gap:14px}
.feature li b{display:block}
.feature li span{color:var(--mute);font-size:15px}
/* services */
.dark{background:var(--ink);color:#fff}
.dark .eyebrow{color:#9a9a9a}
.dark .lede{color:#bdbdbd}
.dark h3{color:var(--accent);font-size:clamp(30px,3.6vw,48px);margin:6px 0 12px}
.dark .row{display:grid;gap:28px;align-items:center;padding:44px 0;border-top:1px solid #3a3a3a}
.dark .row:first-of-type{margin-top:48px}
@media(min-width:900px){.dark .row{grid-template-columns:1fr 1fr;gap:64px}.dark .row:nth-of-type(even) .media{order:2}}
.dark .media{aspect-ratio:16/10;overflow:hidden}
.dark .media img{height:100%;object-fit:cover}
.dark .sn{font:800 15px "Bricolage Grotesque";color:#9a9a9a}
.dark p{color:#c9c9c9;max-width:44ch}
.startone{margin-top:40px;padding-top:32px;border-top:1px solid #3a3a3a;font:800 clamp(24px,2.6vw,34px)/1.1 "Bricolage Grotesque";letter-spacing:-.02em}
/* why */
.why{display:grid;gap:40px;margin-top:48px}
@media(min-width:900px){.why{grid-template-columns:repeat(3,1fr)}}
.why h4{font:800 28px/1 "Bricolage Grotesque";letter-spacing:-.02em;margin-bottom:12px}
.why h4::before{content:"";display:block;width:36px;height:4px;background:var(--accent);margin-bottom:18px}
.why p{color:var(--mute)}
.practice{margin-top:56px;background:var(--paper-2);padding:24px 28px;border-left:4px solid var(--accent);max-width:820px}
/* process */
.strip{display:grid;gap:12px;margin-top:44px}
@media(min-width:900px){.strip{grid-template-columns:repeat(4,1fr)}}
.leg{border-top:3px solid var(--ink);padding-top:20px}
.leg .num{font-size:44px;color:var(--accent)}
.leg h4{font-size:22px;margin:8px 0}
.leg p{color:var(--mute);font-size:15px}
.approve{margin-top:40px;font-weight:700;font-size:19px}
/* contact */
.contact{background:var(--accent);color:#fff;padding:120px 0}
.contact h2{font-size:clamp(56px,11vw,180px)}
.contact h2 small{display:block;font-size:.32em;letter-spacing:-.01em;opacity:.8;margin-bottom:.2em}
.contact .sub{font-size:22px;margin-top:28px;max-width:36ch}
.cta{display:flex;flex-wrap:wrap;gap:16px 24px;align-items:center;margin-top:40px}
.btn{background:#fff;color:var(--ink);padding:18px 28px;font-weight:700;text-decoration:none;font-size:17px}
.btn:hover{background:var(--ink);color:#fff}
.contact .cta a:not(.btn){color:#fff;font-weight:500}
footer{background:var(--ink);color:#9a9a9a;font-size:14px;padding:28px 0}
/* reveal */
.rv{opacity:0;transform:translateY(24px);transition:opacity .8s,transform .8s cubic-bezier(.2,.7,.2,1)}
.rv.in{opacity:1;transform:none}
.tile.rv,.feature-main.rv,.logos.rv,.row .media.rv{opacity:1;transition:transform .6s cubic-bezier(.2,.7,.2,1)}
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}.rv{opacity:1;transform:none}}
}
Selected work
Recent projects
Websites, content and video.
Latest · Concept
A full website for a live-seafood and Sichuan restaurant.
Bilingual EN / 中文 An EN / 中 toggle switches the whole page between languages.
Custom animation A dark, gold-lit hero that feels like walking in at dinner.
Dish-led storytelling Signature dishes told one by one, with a reservation button always in reach.
View the concept site →
More work
All Websites Social Email Video
(function(){
// Folder that holds the images. Change this if your Media Library path is different.
var BASE='https://nicholaschoo.me/wp-content/uploads/2026/10/';
var root=document.querySelector('.relay');
var fix=function(r){r.querySelectorAll('img[src^="w/"]').forEach(function(i){i.setAttribute('src',BASE+i.getAttribute('src').slice(2))})};
// [file, title, line, category, status, link]
const work=[
["sph-print-tour","SPH Media: Print Tour","Short documentary of SPH Media's Print Centre tour. I filmed it on site, wrote the script, edited in CapCut and added the text overlays. 43 seconds, made for their Facebook group.","video","Video","https://www.youtube.com/watch?v=4N1Z8HtcMN4"],
["jg-debate-finals.png","Julia Gabriel: Debate Finals","Highlight reel for the Singapore Secondary Schools Debating Championships Grand Finals, held 24 April 2026 at Anglo-Chinese Junior College, for Julia Gabriel Centre.","video","Video","https://www.youtube.com/watch?v=LXxCSHO-qlE"],
["wubble-voice-accents.png","Wubble AI: Voice & Accents","Storyboarded and produced end to end, with the soundtrack generated through the Wubble API so the music fits the visuals. A live example of Wubble's audio in a real workflow.","video","Video","/voice-accents/"],
["sfx-tutorial-hero","Wubble AI: SFX Tutorial","A walkthrough of Wubble Sound Effects, from one prompt to a finished sound. Built in Remotion from screen recordings, with the music and interface sounds generated by Wubble.","video","Video","/wubble-sfx-tutorial/"],
["hakkabond-site","Hakka Bond","Brand site with live reservations","web","Published","https://hakkabond.com"],
["hakka-food","Hakka Bond: Food","The dish, styled and shot to be ordered","social","Published"],
["hakka-culture","Hakka Bond: Culture","Where the recipes come from, and why","social","Published"],
["pan","Hakka Bond: Entertainment","People eating, reacting, coming back","social","Published"],
["folium-hero","Folium: Hero","The product in hand, the shot that stops a scroll","social","Concept","/website-design-concept-works-2/"],
["wubble-email-sfx","Wubble AI - Email Newsletter","“Your soundtrack now lives inside Adobe”","email","Published","/email-newsletter/"],
["folium-site","Folium","Botanical skincare · editorial storytelling · e-commerce ready","web","Concept","/website-design-concept-works-2/"],
["folium-texture","Folium: Texture","Proof the formula behaves the way the copy claims","social","Concept","/website-design-concept-works-2/"],
["char-site","Char","Fire-driven cooking · bold wood-fired hero · menu-led restaurant site","web","Concept","/website-design-concept-works-2/"],
["folium-range","Folium: Range","The full line, so the brand reads as a brand","social","Concept","/website-design-concept-works-2/"]];
const grid=root.querySelector('#grid');
grid.innerHTML=work.map(([f,t,l,c,s,href])=>{
const tagName=href?'a':'div',attrs=href?` href="${href}"${href[0]=='/'?'':' target="_blank" rel="noopener"'}`:' tabindex="0"';
return `
${s} ${t} ${l}
${tagName}>`}).join('');
fix(grid);
const all=[...grid.querySelectorAll('.tile')];let cur='all',nc=0;
const layout=()=>{nc=innerWidth>=900?3:innerWidth>=600?2:1;grid.innerHTML='';const cs=[...Array(nc)].map(()=>grid.appendChild(document.createElement('div')));cs.forEach(c=>c.className='col');
const ready=all.every(t=>t.querySelector('img').complete);
all.filter(t=>cur==='all'||t.dataset.f===cur).forEach((t,i)=>{const grp=cur==='all'&&nc>1,hb=t.querySelector('b').textContent.startsWith('Hakka Bond'),pool=grp?(hb?[cs[nc-1]]:cs.slice(0,nc-1)):cs;const c=ready?pool.reduce((a,b)=>b.offsetHeight
{const i=t.querySelector('img');return i.complete?0:new Promise(r=>{i.onload=i.onerror=r})})).then(layout);addEventListener('load',()=>setTimeout(layout,60));addEventListener('resize',()=>{if((innerWidth>=900?3:innerWidth>=600?2:1)!==nc)layout()});
document.getElementById('filters').onclick=e=>{const f=e.target.dataset.f;if(!f)return;
document.querySelectorAll('#filters button').forEach(b=>b.classList.toggle('on',b===e.target));cur=f;layout()};
const io=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){e.target.classList.add('in');io.unobserve(e.target)}}),{threshold:.12});
document.querySelectorAll('.rv').forEach(el=>io.observe(el));
if(window.top!==window||document.body.classList.contains('elementor-editor-active'))root.querySelectorAll('.rv').forEach(el=>el.classList.add('in'));
const nav=document.getElementById('nav'),hero=document.querySelector('.hero');
const place=()=>{const ab=document.getElementById('wpadminbar');nav.style.top=ab?Math.max(0,ab.getBoundingClientRect().bottom)+'px':''};place();addEventListener('scroll',place,{passive:true});addEventListener('resize',place);
addEventListener('scroll',()=>nav.classList.toggle('solid',scrollY>hero.offsetHeight-70),{passive:true});
fix(root);
})();