The creative that fills the sidebar and inline promo slots guests and Free users see on the web. Three families: Pro promos, free-account promos, and coming-soon notices.
The whole-window break is Interstitial ad. Power tips moved to their own page on 2026-08-12 — Power tips. They are help, not promo, so they run for every plan and in the extension, which carries no promo creative at all.
HousePromo · new — one component, format="sidebar|inline", creative from a data arraySmallButton · ui/buttons/SmallButton.vue — the green CTA, variant:greenInlineAd · ui/components/Upsell/InlineAd.vue — the 120px stub this replaces on mobilecdn.jsdelivr.net/joypixels/assets/11.0/png/unicode/64/<script setup>
import SmallButton from '@/buttons/SmallButton.vue'
import { useUpsellStore } from '@/store/upsell'
const props = defineProps({
ad: { type: Object, required: true }, // one entry from houseAds
format: { type: String, default: 'sidebar' } // 'sidebar' | 'inline'
});
const upsell = useUpsellStore()
/* the Pro creative set — sidebar + inline share it */
export const houseAds = [
{ id: 'pro-smart-recents', emoji: '23f0',
title: 'Recents that sort themselves.',
line: 'Smart Recents keeps what you actually use up front.',
cta: 'Go Pro', action: 'pro' },
{ id: 'pro-unlimited-lists', emoji: '1f4da',
title: 'Build every list you want.',
line: 'Lists and collections go unlimited with Pro.',
cta: 'Go Pro', action: 'pro' },
{ id: 'pro-expanded-search', emoji: '1f50d',
title: 'More ways to find it.',
line: 'Expanded search digs deeper into every keyword.',
cta: 'Go Pro', action: 'pro' },
{ id: 'pro-no-ads', emoji: '1f9f9',
title: 'This spot could be empty.',
line: 'Pro removes the ads. All of them.',
cta: 'Go Pro', action: 'pro' },
]
</script>
<template>
<div class="house-promo" :class="'house-promo--' + props.format">
<img class="__emoji" :src="cdn(props.ad.emoji)" alt="" />
<div class="__body">
<div class="__title">{{ props.ad.title }}</div>
<div class="__line">{{ props.ad.line }}</div>
</div>
<SmallButton variant="green" :label="props.ad.cta"
@click="upsell.updateShowPro(true)" />
</div>
</template>
.house-promo {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
width: 218px; /* 250px sidebar minus 16px pads */
padding: 20px 18px 18px;
border-radius: 16px;
background: var(--metal-950); /* sales surface: flat, both schemes */
}
.house-promo .__emoji { width: 40px; height: 40px; }
.house-promo .__title {
font-weight: 700;
font-size: 1.5rem;
line-height: 1.25;
color: var(--white);
}
.house-promo .__line {
font-size: 1.3rem;
line-height: 1.45;
color: var(--metal-300);
}
.house-promo .small-button { margin-top: 6px; }
/* inline — one row, 120px tall (InlineAd.vue's slot height) */
.house-promo--inline {
flex-direction: row;
align-items: center;
gap: 14px;
width: 100%;
height: 120px;
padding: 0 18px;
}
.house-promo--inline .__emoji { width: 44px; height: 44px; }
.house-promo--inline .__body { flex: 1; min-width: 0; }
.house-promo--inline .small-button { margin: 0; flex: none; }
HousePromo · new — same component as card A, different creative entriesSmallButton · ui/buttons/SmallButton.vue — variant:green/* appended to houseAds — eligibility keeps them guest-only */
{ id: 'free-favorites', emoji: '2764',
title: 'Keep your favorites.',
line: 'A free account saves favorites, combos, and settings.',
lineInline: 'Favorites, combos, and settings, saved.',
cta: 'Sign up free', ctaInline: 'Sign up',
action: 'join', audience: 'guest' },
{ id: 'free-sync', emoji: '1f501',
title: 'Same setup, every device.',
line: 'A free account follows you from browser to browser.',
cta: 'Sign up free', action: 'join', audience: 'guest' },
<HousePromo :ad="ad" format="sidebar" /> // or format="inline"
.house-promo {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
width: 218px;
padding: 20px 18px 18px;
border-radius: 16px;
background: var(--metal-950);
}
.house-promo .__emoji { width: 40px; height: 40px; }
.house-promo .__title {
font-weight: 700;
font-size: 1.5rem;
line-height: 1.25;
color: var(--white);
}
.house-promo .__line {
font-size: 1.3rem;
line-height: 1.45;
color: var(--metal-300);
}
.house-promo .small-button { margin-top: 6px; }
.house-promo--inline {
flex-direction: row;
align-items: center;
gap: 14px;
width: 100%;
height: 120px;
padding: 0 18px;
}
.house-promo--inline .__emoji { width: 44px; height: 44px; }
.house-promo--inline .__body { flex: 1; min-width: 0; }
.house-promo--inline .small-button { margin: 0; flex: none; }
HousePromo · new — same component; a soon: true entry renders the pill instead of the button{ id: 'soon-extension', emoji: '1f9e9',
title: 'EmojiCopy in your toolbar.',
line: 'The browser extension keeps your emoji one click away.',
soon: true },
{ id: 'soon-mac', emoji: '1f5a5',
title: 'EmojiCopy for Mac.',
line: 'Your lists and favorites, on the desktop.',
soon: true },
/* text-only pill, never a button, never a leading dot */
.house-promo .__soon {
margin-top: 6px;
padding: 5px 12px;
border-radius: var(--r-pill);
background: rgba(255, 255, 255, .12);
font-weight: 700;
font-size: 1.1rem;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--white);
}