feat: add four storefront identities
This commit is contained in:
30
config/store-config.ts
Normal file
30
config/store-config.ts
Normal 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
34
config/store_collector.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import type { StoreConfig } from './store-config';
|
||||||
|
|
||||||
|
export const store: StoreConfig = {
|
||||||
|
code: 'collector',
|
||||||
|
label: 'Collector',
|
||||||
|
brandName: 'The Collector’s 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
31
config/store_crypto.ts
Normal 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
34
config/store_executive.ts
Normal 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
31
config/store_supper.ts
Normal 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. There’s 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
19
config/stores.ts
Normal 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' },
|
||||||
|
];
|
||||||
14
src/data/overrides/collector.ts
Normal file
14
src/data/overrides/collector.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import type { StoreMeta } from './types';
|
||||||
|
|
||||||
|
export const collector: StoreMeta = {
|
||||||
|
name: ['First Mallard', 'Black Lacquer', 'The Porcelain', 'The Bullion', 'Cobalt Reserve', 'The Vermillion'],
|
||||||
|
blurb: [
|
||||||
|
'Lot 001 of the founding run — never traded below issue.',
|
||||||
|
'Hand-lacquered shell; only nine known in this grade.',
|
||||||
|
'Kiln-pale and flawless. A blue-chip hold.',
|
||||||
|
'24-duckmark gilt. Tracks the index, lot for lot.',
|
||||||
|
'Deep cobalt, tightly held by serious hands.',
|
||||||
|
'Vermillion. Ungraded twins are simply unheard of.',
|
||||||
|
],
|
||||||
|
tag: ['MINT 70', 'GEM 68', 'PROOF 69', 'MINT 71', 'GEM 66', 'MINT 70'],
|
||||||
|
};
|
||||||
14
src/data/overrides/crypto.ts
Normal file
14
src/data/overrides/crypto.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import type { StoreMeta } from './types';
|
||||||
|
|
||||||
|
export const crypto: StoreMeta = {
|
||||||
|
name: ['HODL Duck', 'Whale Duck', 'Degen Duck', 'Lambo Duck', 'Moon Duck', 'Diamond Hands'],
|
||||||
|
blurb: [
|
||||||
|
'Refuses to sell. Ever. Not even at -82%.',
|
||||||
|
'Moves the floor just by entering the pond.',
|
||||||
|
'Aped in at the top. Still grinning.',
|
||||||
|
'Gold chrome. Engine noises sold separately.',
|
||||||
|
'Eyes locked on the chart. wen moon.',
|
||||||
|
'Grips so tight the duck squeaks diamonds.',
|
||||||
|
],
|
||||||
|
tag: ['RARE', 'LEGENDARY', 'COMMON', 'MYTHIC', 'EPIC', 'LEGENDARY'],
|
||||||
|
};
|
||||||
14
src/data/overrides/executive.ts
Normal file
14
src/data/overrides/executive.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import type { StoreMeta } from './types';
|
||||||
|
|
||||||
|
export const executive: StoreMeta = {
|
||||||
|
name: ['The Keynote', 'The Chairman', 'The Closer', 'The Founder', 'The Negotiator', 'The Successor'],
|
||||||
|
blurb: [
|
||||||
|
'Hand-finished matte shell for the cold open.',
|
||||||
|
'Weighted base. Holds your gaze without blinking.',
|
||||||
|
'The one you rehearse the hard "no" in front of.',
|
||||||
|
'Carry it into the raise. It has seen bigger rounds.',
|
||||||
|
'Silent, patient, unimpressed — exactly the audience you need.',
|
||||||
|
'For the succession talk you keep postponing.',
|
||||||
|
],
|
||||||
|
tag: ['No. 001 / 250', 'No. 014 / 180', 'Atelier', 'Signature', 'No. 007 / 90', 'By request'],
|
||||||
|
};
|
||||||
16
src/data/overrides/index.ts
Normal file
16
src/data/overrides/index.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type { StoreMeta } from './types';
|
||||||
|
import { executive } from './executive';
|
||||||
|
import { crypto } from './crypto';
|
||||||
|
import { supper } from './supper';
|
||||||
|
import { collector } from './collector';
|
||||||
|
|
||||||
|
/** Per shop-system copy. Keyed by the shop system (store code up to the first
|
||||||
|
* `_`), the same rule the PostCSS plugin uses for styles. */
|
||||||
|
export const metaBySystem: Record<string, StoreMeta> = {
|
||||||
|
executive,
|
||||||
|
crypto,
|
||||||
|
supper,
|
||||||
|
collector,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type { StoreMeta } from './types';
|
||||||
14
src/data/overrides/supper.ts
Normal file
14
src/data/overrides/supper.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import type { StoreMeta } from './types';
|
||||||
|
|
||||||
|
export const supper: StoreMeta = {
|
||||||
|
name: ['Sunday Roast', 'Midnight Snack', 'Porridge Pal', 'Honey Butter', 'Blueberry', 'Tomato Soup'],
|
||||||
|
blurb: [
|
||||||
|
'Sits patiently while the gravy settles.',
|
||||||
|
'Keeps you company at the open fridge, 1am.',
|
||||||
|
'Slow mornings, warm bowls, no rush.',
|
||||||
|
'Best friends with toast and a long Sunday.',
|
||||||
|
'Small, cheerful, smells faintly of muffins.',
|
||||||
|
'Pairs with grilled cheese and bad weather.',
|
||||||
|
],
|
||||||
|
tag: ['Serves 1', 'Serves 1', 'Best with oats', 'Best with toast', 'Brunch', 'Soup season'],
|
||||||
|
};
|
||||||
7
src/data/overrides/types.ts
Normal file
7
src/data/overrides/types.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/** Per-store copy for the six shared ducks. Arrays are index-aligned with
|
||||||
|
* `ducks` in ../products.ts — same product, different voice per storefront. */
|
||||||
|
export type StoreMeta = {
|
||||||
|
name: string[];
|
||||||
|
blurb: string[];
|
||||||
|
tag: string[];
|
||||||
|
};
|
||||||
6
src/styles/fonts.css
Normal file
6
src/styles/fonts.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/*
|
||||||
|
* All storefronts load from the same stylesheet. Each store selects its display
|
||||||
|
* / ui / mono faces purely through the --font-* tokens set in its @store block
|
||||||
|
* in globals.css. Only the active store's block survives the build.
|
||||||
|
*/
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&family=Hanken+Grotesk:wght@400;500;600;700;800&family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;1,6..72,400;1,6..72,500&family=Spectral:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&display=swap');
|
||||||
127
src/styles/globals.css
Normal file
127
src/styles/globals.css
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
@import './fonts.css';
|
||||||
|
@import 'tailwindcss';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Semantic brand tokens. Every component styles itself from these — the same
|
||||||
|
* markup, the same classes, across all four storefronts. Each `@store` block
|
||||||
|
* below rewrites the tokens (and a few layout switches) for one storefront; the
|
||||||
|
* postcss-storestyles plugin keeps exactly one block at build time, driven by
|
||||||
|
* NEXT_PUBLIC_STORE_CODE. The shipped CSS carries a single brand, no branching.
|
||||||
|
*/
|
||||||
|
@theme {
|
||||||
|
--color-bg: #0c0d10;
|
||||||
|
--color-surface: #15161b;
|
||||||
|
--color-surface2: #1d1f26;
|
||||||
|
--color-fg: #efeae0;
|
||||||
|
--color-muted: #9a958a;
|
||||||
|
--color-line: rgba(220, 210, 190, 0.13);
|
||||||
|
--color-accent: #c9a86a;
|
||||||
|
--color-accent2: #8a7a55;
|
||||||
|
|
||||||
|
--font-display: 'Cormorant Garamond', Georgia, serif;
|
||||||
|
--font-ui: 'Hanken Grotesk', system-ui, sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', monospace;
|
||||||
|
|
||||||
|
--radius-card: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes floaty {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-9px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes tick {
|
||||||
|
from {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply bg-bg text-fg;
|
||||||
|
font-family: var(--font-ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── EXECUTIVE · Onyx — luxury, dark, gilt, Cormorant serif ── */
|
||||||
|
@store executive {
|
||||||
|
:root {
|
||||||
|
--color-bg: #0c0d10;
|
||||||
|
--color-surface: #15161b;
|
||||||
|
--color-surface2: #1d1f26;
|
||||||
|
--color-fg: #efeae0;
|
||||||
|
--color-muted: #9a958a;
|
||||||
|
--color-line: rgba(220, 210, 190, 0.13);
|
||||||
|
--color-accent: #c9a86a;
|
||||||
|
--color-accent2: #8a7a55;
|
||||||
|
--font-display: 'Cormorant Garamond', Georgia, serif;
|
||||||
|
--font-ui: 'Hanken Grotesk', system-ui, sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', monospace;
|
||||||
|
--radius-card: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── CRYPTO · DEGEN//DUCKS — neon, dark, glow, Space Grotesk ── */
|
||||||
|
@store crypto {
|
||||||
|
:root {
|
||||||
|
--color-bg: #070a0f;
|
||||||
|
--color-surface: #0d1219;
|
||||||
|
--color-surface2: #111927;
|
||||||
|
--color-fg: #e8f0ff;
|
||||||
|
--color-muted: #6f7d92;
|
||||||
|
--color-line: rgba(80, 255, 170, 0.2);
|
||||||
|
--color-accent: #33ff99;
|
||||||
|
--color-accent2: #b15bff;
|
||||||
|
--font-display: 'Space Grotesk', system-ui, sans-serif;
|
||||||
|
--font-ui: 'Space Grotesk', system-ui, sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', monospace;
|
||||||
|
--radius-card: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── SUPPER · Rubber Dubber — warm cream, cozy, Newsreader serif ── */
|
||||||
|
@store supper {
|
||||||
|
:root {
|
||||||
|
--color-bg: #f6efe3;
|
||||||
|
--color-surface: #fffaf2;
|
||||||
|
--color-surface2: #ece0cd;
|
||||||
|
--color-fg: #3a3128;
|
||||||
|
--color-muted: #8a7c6a;
|
||||||
|
--color-line: rgba(80, 60, 40, 0.14);
|
||||||
|
--color-accent: #cf7d4e;
|
||||||
|
--color-accent2: #7d8a5a;
|
||||||
|
--font-display: 'Newsreader', Georgia, serif;
|
||||||
|
--font-ui: 'Hanken Grotesk', system-ui, sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', monospace;
|
||||||
|
--radius-card: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── COLLECTOR · The Collector’s Den — vintage paper, maroon, Spectral ── */
|
||||||
|
@store collector {
|
||||||
|
:root {
|
||||||
|
--color-bg: #e7e3d6;
|
||||||
|
--color-surface: #f3f0e6;
|
||||||
|
--color-surface2: #dcd7c6;
|
||||||
|
--color-fg: #22231d;
|
||||||
|
--color-muted: #6f6c5d;
|
||||||
|
--color-line: rgba(40, 40, 28, 0.16);
|
||||||
|
--color-accent: #8a2f2f;
|
||||||
|
--color-accent2: #b08a3e;
|
||||||
|
--font-display: 'Spectral', Georgia, serif;
|
||||||
|
--font-ui: 'Hanken Grotesk', system-ui, sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', monospace;
|
||||||
|
--radius-card: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user