openvibers/openvibe.vip Commits

Home: a taste and a door on phones (#121)

OP OpenVibers committed 15 days ago · 3683b3d8e4 · 2 files, +30 −6

The creators are a two-column list of names on a phone (each still opens its
page), the figures are one row, what a plan can include and how a membership
works fold under one heading, offering a membership and selling one to software
fold under "Offer a membership" (the hero's #for-creators lands on that visible
heading), and the hero has two actions.

Changes

ddiff --git a/public/css/vip.css b/public/css/vip.css
iindex 4725983..9f76f8b 100644
---- a/public/css/vip.css
++++ b/public/css/vip.css
@@@ -88,3 +88,26 @@ code { background: rgba(255, 255, 255, .06); padding: 1px 5px; border-radius: 4p
.vip-faq details[open] { padding-bottom: 12px; }
.vip-faq summary { padding: 8px 0; margin: 0; }
.vip-faq p { margin: 4px 0 0; color: var(--vip-muted); }
++
++/* The home on phones (2026-10-29): a taste and a door. The creators are a two-column list of names (each opens its
++ page), the figures are one row, and the explainers fold under their headings. */
++.vip-fold { border: 1px solid var(--border, #1f2d47); border-radius: 16px; background: var(--bg-secondary, #101828); }
++.vip-fold > summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; padding: 0 18px; cursor: pointer; list-style: none; }
++.vip-fold > summary::-webkit-details-marker { display: none; }
++.vip-fold > summary h2 { margin: 0; }
++.vip-fold > summary span { color: var(--text-muted, #7386a3); font-weight: 600; font-size: .86rem; text-align: right; }
++.vip-fold > summary:focus-visible { outline: 2px solid var(--accent, #3b82f6); outline-offset: -2px; border-radius: 16px; }
++.vip-fold .sc-sec { max-width: none; margin: 0; padding: 0 18px 18px; }
++@media (max-width: 599px) {
++ .creator-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
++ .creator-list > li { margin: 0; min-width: 0; }
++ .creator-list a { min-height: 48px; padding: 8px 10px; gap: 8px; }
++ .creator-list .vip-avatar { width: 28px; height: 28px; }
++ .creator-list .vip-creator-text .muted { display: none; }
++ .creator-list .vip-creator-text b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
++ .vip-stats { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); grid-template-columns: none; gap: 8px; }
++ .vip-stats > div { padding: 10px 12px; }
++ .vip-stats dt { font-size: 1.05rem; }
++ .vip-stats dd { font-size: .76rem; line-height: 1.35; }
++ .vip-fold > summary span { display: none; }
++}
ddiff --git a/server/web/pages.js b/server/web/pages.js
iindex 0d66512..9d0bc5e 100644
---- a/server/web/pages.js
++++ b/server/web/pages.js
@@@ -100,12 +100,14 @@ function home({ creators, live = [], networkUrl = 'https://openvibe.network' })
title: 'Memberships across', accent: 'OpenVibe',
lede: "Back the creators you watch with a monthly membership. Join once and it's recognised across OpenVibe: a badge by your name in chat, members-only posts, a members' room, whatever the plan lists.",
actions: creators.length
-- ? [{ label: 'Find a creator', href: '#creators', primary: true }, { label: 'Offer a plan', href: '#for-creators' }, { label: 'Your memberships', href: '/me' }]
++ // Two, so they sit side by side on a phone; your memberships are in the app bar.
++ ? [{ label: 'Find a creator', href: '#creators', primary: true }, { label: 'Offer a plan', href: '#for-creators' }]
: [{ label: 'Offer a plan', href: '/dashboard', primary: true }, { label: 'Your memberships', href: '/me' }],
note: 'Payments, renewals, cancellations and refunds are handled by OpenVibe.Billing.',
html: `<dl class="vip-stats">${stats.join('')}</dl>`,
}) + liveSection + `
<section class="sc-sec" id="creators" aria-labelledby="creators-h"><h2 id="creators-h">Creators with a membership</h2><p class="sc-lede">Each page lists the creator's plan, what it includes and its price before you join.</p>${list}<p class="muted creators-cta">Are you a creator? <a href="/dashboard">Offer a plan</a>.</p></section>`
++ + '<section class="sc-sec" aria-labelledby="how-h"><details class="vip-fold"><summary><h2 id="how-h">How a membership works</h2><span>perks, joining, the terms you keep</span></summary>'
+ showcase.features({
title: 'What a plan can include',
lede: 'A creator builds each plan from perks; the plan lists exactly which, and each OpenVibe site that honours a perk recognises it.',
@@@ -116,15 +118,14 @@ function home({ creators, live = [], networkUrl = 'https://openvibe.network' })
{ icon: 'ov:community', title: 'Roles and emotes', text: 'A role in the community, emotes to use in chat.' },
],
}) + showcase.steps({
-- title: 'How a membership works',
items: [
{ title: "Pick a creator's plan", text: 'Each creator page lists their plan, what it includes and its price.' },
{ title: 'Join through OpenVibe.Billing', text: 'Pay from your Vibes credit or another way Billing offers; renew automatically or not, your choice.' },
{ title: 'Keep the terms you joined under', text: 'Plans are versioned: the version you joined stays yours while your membership lasts, and every version stays public in the terms history.' },
],
-- }) + showcase.steps({
-- id: 'for-creators',
-- title: 'Offer a membership',
++ }) + '</details></section>'
++ + '<section class="sc-sec" id="for-creators" aria-labelledby="offer-h"><details class="vip-fold"><summary><h2 id="offer-h">Offer a membership</h2><span>your plan, and selling one to your software</span></summary>'
++ + showcase.steps({
lede: 'Streaming on OpenVibe.Live already? Your channel subscription is your plan here, ready to describe and price.',
items: [
{ title: 'Open your dashboard', href: '/dashboard', text: 'Sign in with your OpenVibe account; your plan and its members are all on one page.' },
@@@ -156,7 +157,7 @@ if (e.active) unlockPro(); // expired, unknown or unreachable all answer no`,
# {"active":true,"status":"active","expires_at":"…",…}`,
},
],
-- }) + `
++ }) + `</details></section>
<section class="sc-sec" id="questions" aria-labelledby="questions-h"><h2 id="questions-h">Questions</h2><div class="vip-faq">${HOME_FAQ.map((f) => `<details><summary>${esc(f.q)}</summary><p>${esc(f.a)}</p></details>`).join('')}</div></section>`;
}