feat: add four storefront identities

This commit is contained in:
2026-06-29 12:06:18 +01:00
parent e4bcd7578a
commit 561e948cb4
14 changed files with 391 additions and 0 deletions

30
config/store-config.ts Normal file
View File

@@ -0,0 +1,30 @@
/** Shape of a storefront's content config. Same shape for every store —
* the values differ, the structure never does. */
export type Review = { q: string; a: string };
export type StoreConfig = {
/** NEXT_PUBLIC_STORE_CODE; also the @store / shop-system id. */
code: string;
/** Switcher tab label in the showcase headbar. */
label: string;
/** Storefront wordmark. */
brandName: string;
/** Small line under / beside the wordmark. */
brandSub: string;
nav: string[];
cartLabel: string;
ctaLabel: string;
hero: {
eyebrow: string;
title: string;
body: string;
cta: string;
};
collection: { title: string; meta: string };
featured: { kicker: string; title: string; body: string; cta: string };
reviews: Review[];
footer: { links: string[]; note: string };
/** cents × multiplier ÷ 100 → display dollars (exec 16, crypto 3, collector 28, supper 1). */
priceMultiplier: number;
priceStyle: 'usd' | 'crypto' | 'appraisal' | 'plain';
};

34
config/store_collector.ts Normal file
View File

@@ -0,0 +1,34 @@
import type { StoreConfig } from './store-config';
export const store: StoreConfig = {
code: 'collector',
label: 'Collector',
brandName: 'The Collectors Den',
brandSub: 'Graded · Vaulted · Appreciating',
nav: ['The Index', 'Provenance', 'The Vault', 'Appraisals'],
cartLabel: 'Portfolio',
ctaLabel: 'Acquire lot',
hero: {
eyebrow: 'Alternative Assets · Duck Class',
title: 'Ducks that have only ever appreciated.',
body: 'Independently graded, fully insured, sealed in the vault. Every lot in the catalogue has held — or grown — its value across every market on record.',
cta: 'View the Index',
},
collection: { title: 'The Standing Catalogue', meta: 'Six lots · all appreciating' },
featured: {
kicker: 'The Vault',
title: 'Climate-controlled, fully insured, never sold short.',
body: 'Every acquisition is graded by an independent panel, sealed with a certificate of authenticity, and held in the vault under your name. Liquidate any lot in forty-eight hours.',
cta: 'Request an appraisal',
},
reviews: [
{
q: 'Eleven years, two hundred and forty lots, and not one of them underwater. Try that with equities.',
a: 'Private collector, portfolio appraised at seven figures',
},
{ q: 'I no longer keep my faith in banks. I keep it in graded ducks, sealed in the vault.', a: 'Member, Assoc. of Duck Numismatics' },
],
footer: { links: ['The Index', 'The Vault', 'Appraisals', 'Provenance'], note: 'Member · Assoc. of Duck Numismatics' },
priceMultiplier: 28,
priceStyle: 'appraisal',
};

31
config/store_crypto.ts Normal file
View File

@@ -0,0 +1,31 @@
import type { StoreConfig } from './store-config';
export const store: StoreConfig = {
code: 'crypto',
label: 'Crypto',
brandName: 'DEGEN//DUCKS',
brandSub: 'Floor 0.012Ξ · 4200 minted',
nav: ['Drops', 'Rarity', 'Roadmap', 'Whitepaper'],
cartLabel: 'Stash',
ctaLabel: 'PUMP IT',
hero: {
eyebrow: 'LIVE · FLOOR 0.012Ξ',
title: 'Your unshakeable companion through every dip.',
body: 'When the chart bleeds red, your duck stays diamond. gm. buy the duck. hold the line. wagmi.',
cta: 'Mint Yours →',
},
collection: { title: '🔥 Latest Drops', meta: 'Floor 0.012Ξ · last sale 0.069Ξ' },
featured: {
kicker: 'Stake · Squeak · Earn',
title: 'Put your duck to work in the pond.',
body: 'Stake your duck in the pond and farm $QUACK while you sleep. Mint, stake, earn — wagmi.',
cta: 'Enter the pond',
},
reviews: [
{ q: 'down 60% this week but my HODL Duck hasnt blinked once. we are so back.', a: '@degenpond · ♥ 4.2K' },
{ q: 'talked to my Moon Duck during the crash instead of my PM. healthier honestly. gm.', a: '@onlyupjpeg · ♥ 9.1K' },
],
footer: { links: ['Discord', 'X', 'OpenSea', 'Docs'], note: 'not financial advice · gm' },
priceMultiplier: 3,
priceStyle: 'crypto',
};

34
config/store_executive.ts Normal file
View File

@@ -0,0 +1,34 @@
import type { StoreConfig } from './store-config';
export const store: StoreConfig = {
code: 'executive',
label: 'Executive',
brandName: 'Onyx',
brandSub: 'Private Collection',
nav: ['The Collection', 'Bespoke', 'Concierge'],
cartLabel: 'Bag',
ctaLabel: 'Acquire',
hero: {
eyebrow: 'Est. MMXXIV · Private Collection',
title: 'Rehearse like the room is already watching.',
body: 'Hand-finished executive ducks for the founders, chairs and closers who refuse to wing the hard conversation.',
cta: 'Enter the Boardroom',
},
collection: { title: 'The Collection', meta: 'Six pieces · made to order' },
featured: {
kicker: 'The Atelier',
title: 'Monogram, hand-set into the base.',
body: 'A two-week commission. Your initials, your firm, the date of the round. Strictly limited, never repeated.',
cta: 'Commission a piece',
},
reviews: [
{
q: 'I rehearsed the layoffs speech in front of The Chairman for a week. Delivered it without flinching.',
a: 'Managing Partner, a fund you have heard of',
},
{ q: 'My board calls are sharper. The duck does not nod along — and that is the point.', a: 'Founder & CEO, Series C' },
],
footer: { links: ['Concierge', 'The Atelier', 'Press', 'Terms'], note: '© MMXXVI Onyx Executive' },
priceMultiplier: 16,
priceStyle: 'usd',
};

31
config/store_supper.ts Normal file
View File

@@ -0,0 +1,31 @@
import type { StoreConfig } from './store-config';
export const store: StoreConfig = {
code: 'supper',
label: 'Supper',
brandName: 'Rubber Dubber',
brandSub: 'Supper Club · Since 1960',
nav: ['Shop', 'The Table', 'Recipes', 'Our Story'],
cartLabel: 'Basket',
ctaLabel: 'Add to table',
hero: {
eyebrow: 'A seat at the table',
title: 'Nobody should have to eat supper alone.',
body: 'Meet the little companions who pull up a chair, keep you company, and never once steal your fries.',
cta: 'Set the Table →',
},
collection: { title: 'Pull up a chair', meta: 'Six companions, each with their own appetite.' },
featured: {
kicker: 'This week at the table',
title: 'Tomato Soup pairs beautifully with a thunderstorm.',
body: 'And grilled cheese, naturally. Theres a recipe card tucked into every box — handwritten, a little smudged.',
cta: 'See the pairing',
},
reviews: [
{ q: 'I set a place for Sunday Roast every week now. Supper feels a little less quiet.', a: 'Marta, eats at seven' },
{ q: 'My kid insists Tomato Soup gets its own spoon. We oblige.', a: 'Dev, table for three' },
],
footer: { links: ['Shop', 'Recipes', 'Journal', 'Stockists'], note: 'Made warm · est. 1960' },
priceMultiplier: 1,
priceStyle: 'plain',
};

19
config/stores.ts Normal file
View File

@@ -0,0 +1,19 @@
/**
* Single source of truth for the storefronts that get built, deployed and
* linked from the showcase headbar switcher.
*
* code — NEXT_PUBLIC_STORE_CODE; the @store / shop-system id. Must match a
* config/store_<code>.ts file.
* label — switcher tab label.
* worker — the Cloudflare Worker this storefront deploys to.
* url — public URL the switcher tab links to (each storefront is its own
* deployment). Override per environment as needed.
*/
export type StoreEntry = { code: string; label: string; worker: string; url: string };
export const stores: StoreEntry[] = [
{ code: 'executive', label: 'Executive', worker: 'rubberquack-executive', url: 'https://executive.rubberquack.dev' },
{ code: 'crypto', label: 'Crypto', worker: 'rubberquack-crypto', url: 'https://crypto.rubberquack.dev' },
{ code: 'supper', label: 'Supper', worker: 'rubberquack-supper', url: 'https://supper.rubberquack.dev' },
{ code: 'collector', label: 'Collector', worker: 'rubberquack-collector', url: 'https://collector.rubberquack.dev' },
];