SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%

Brand: RareIndex mark (cut stone whose facets draw an index line), logo lockups, favicons (svg/png/ico/apple), PWA icons, site-wide Open Graph + Twitter share image, gold wordmark; asset and profile OG cards use the mark

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed 13 days ago (Sep 11, 2026) parent 5f1c89f

20 changed files +288 −10

modified apps/web/public/apple-touch-icon.png +0 −0

Binary file not shown.

added apps/web/public/brand/README.md +6 −0
@@ -0,0 +1,6 @@
1 +# RareIndex brand
2 +
3 +- `rareindex-mark.svg` — the mark (64×64 grid, 14 px radius): a cut stone (crown, girdle, pavilion) whose facets are drawn by an ascending index line. Ink `#0B0F19`, gold `#F1D08A → #C9963F`, mint `#2FB673 → #5FE3A1`.
4 +- `rareindex-logo.svg` / `rareindex-logo-dark.svg` — horizontal lockups (mark + wordmark + tagline) for light and dark backgrounds.
5 +- Favicons and PWA icons are generated from the mark by `apps/web/scripts/brand-assets.mjs` (`node scripts/brand-assets.mjs` from `apps/web`).
6 +- Minimum size for the mark: 16 px (favicon). Do not recolour the stone; the wordmark may be set in a single colour.
added apps/web/public/brand/rareindex-logo-dark.svg +26 −0
@@ -0,0 +1,26 @@
1 +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 80" width="500" height="80" role="img" aria-label="RareIndex — The Global Market for Collectibles">
2 + <title>RareIndex</title>
3 + <defs>
4 + <linearGradient id="ri-gold" x1="12" y1="22" x2="52" y2="54" gradientUnits="userSpaceOnUse">
5 + <stop offset="0" stop-color="#F1D08A"/>
6 + <stop offset="1" stop-color="#C9963F"/>
7 + </linearGradient>
8 + <linearGradient id="ri-mint" x1="16" y1="42" x2="46" y2="24" gradientUnits="userSpaceOnUse">
9 + <stop offset="0" stop-color="#2FB673"/>
10 + <stop offset="1" stop-color="#5FE3A1"/>
11 + </linearGradient>
12 + </defs>
13 + <g transform="translate(8 8)">
14 + <rect width="64" height="64" rx="14" fill="#0B0F19"/>
15 + <rect x="0.75" y="0.75" width="62.5" height="62.5" rx="13.25" fill="none" stroke="#FFFFFF" stroke-opacity="0.08" stroke-width="1.5"/>
16 + <!-- cut stone: crown, girdle, pavilion -->
17 + <path d="M19 19.5H45L52.5 29.5 32 54.5 11.5 29.5Z" fill="url(#ri-gold)" fill-opacity="0.12" stroke="url(#ri-gold)" stroke-width="3" stroke-linejoin="round"/>
18 + <path d="M11.5 29.5H52.5" stroke="url(#ri-gold)" stroke-width="2" stroke-opacity="0.55" stroke-linecap="round"/>
19 + <!-- the facets read as an index line -->
20 + <path d="M16.5 34.5 24 42.5 31 32.5 37.5 38 46 26.5" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
21 + <path d="M40.5 26.5H46V32" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
22 +
23 + </g>
24 + <text x="90" y="46" font-family="Geist, Inter, 'SF Pro Display', 'Helvetica Neue', Arial, sans-serif" font-size="40" font-weight="650" letter-spacing="-1.4" fill="#F4F4F2">Rare<tspan fill="#C9963F">Index</tspan></text>
25 + <text x="91" y="68" font-family="Geist, Inter, 'SF Pro Text', 'Helvetica Neue', Arial, sans-serif" font-size="13" font-weight="500" letter-spacing="2.2" fill="#9AA0AB">THE GLOBAL MARKET FOR COLLECTIBLES</text>
26 +</svg>
added apps/web/public/brand/rareindex-logo.svg +26 −0
@@ -0,0 +1,26 @@
1 +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 80" width="500" height="80" role="img" aria-label="RareIndex — The Global Market for Collectibles">
2 + <title>RareIndex</title>
3 + <defs>
4 + <linearGradient id="ri-gold" x1="12" y1="22" x2="52" y2="54" gradientUnits="userSpaceOnUse">
5 + <stop offset="0" stop-color="#F1D08A"/>
6 + <stop offset="1" stop-color="#C9963F"/>
7 + </linearGradient>
8 + <linearGradient id="ri-mint" x1="16" y1="42" x2="46" y2="24" gradientUnits="userSpaceOnUse">
9 + <stop offset="0" stop-color="#2FB673"/>
10 + <stop offset="1" stop-color="#5FE3A1"/>
11 + </linearGradient>
12 + </defs>
13 + <g transform="translate(8 8)">
14 + <rect width="64" height="64" rx="14" fill="#0B0F19"/>
15 + <rect x="0.75" y="0.75" width="62.5" height="62.5" rx="13.25" fill="none" stroke="#FFFFFF" stroke-opacity="0.08" stroke-width="1.5"/>
16 + <!-- cut stone: crown, girdle, pavilion -->
17 + <path d="M19 19.5H45L52.5 29.5 32 54.5 11.5 29.5Z" fill="url(#ri-gold)" fill-opacity="0.12" stroke="url(#ri-gold)" stroke-width="3" stroke-linejoin="round"/>
18 + <path d="M11.5 29.5H52.5" stroke="url(#ri-gold)" stroke-width="2" stroke-opacity="0.55" stroke-linecap="round"/>
19 + <!-- the facets read as an index line -->
20 + <path d="M16.5 34.5 24 42.5 31 32.5 37.5 38 46 26.5" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
21 + <path d="M40.5 26.5H46V32" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
22 +
23 + </g>
24 + <text x="90" y="46" font-family="Geist, Inter, 'SF Pro Display', 'Helvetica Neue', Arial, sans-serif" font-size="40" font-weight="650" letter-spacing="-1.4" fill="#0B0F19">Rare<tspan fill="#C9963F">Index</tspan></text>
25 + <text x="91" y="68" font-family="Geist, Inter, 'SF Pro Text', 'Helvetica Neue', Arial, sans-serif" font-size="13" font-weight="500" letter-spacing="2.2" fill="#6B7280">THE GLOBAL MARKET FOR COLLECTIBLES</text>
26 +</svg>
added apps/web/public/brand/rareindex-mark.svg +21 −0
@@ -0,0 +1,21 @@
1 +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="RareIndex">
2 + <title>RareIndex mark</title>
3 + <defs>
4 + <linearGradient id="ri-gold" x1="12" y1="22" x2="52" y2="54" gradientUnits="userSpaceOnUse">
5 + <stop offset="0" stop-color="#F1D08A"/>
6 + <stop offset="1" stop-color="#C9963F"/>
7 + </linearGradient>
8 + <linearGradient id="ri-mint" x1="16" y1="42" x2="46" y2="24" gradientUnits="userSpaceOnUse">
9 + <stop offset="0" stop-color="#2FB673"/>
10 + <stop offset="1" stop-color="#5FE3A1"/>
11 + </linearGradient>
12 + </defs>
13 + <rect width="64" height="64" rx="14" fill="#0B0F19"/>
14 + <rect x="0.75" y="0.75" width="62.5" height="62.5" rx="13.25" fill="none" stroke="#FFFFFF" stroke-opacity="0.08" stroke-width="1.5"/>
15 + <!-- cut stone: crown, girdle, pavilion -->
16 + <path d="M19 19.5H45L52.5 29.5 32 54.5 11.5 29.5Z" fill="url(#ri-gold)" fill-opacity="0.12" stroke="url(#ri-gold)" stroke-width="3" stroke-linejoin="round"/>
17 + <path d="M11.5 29.5H52.5" stroke="url(#ri-gold)" stroke-width="2" stroke-opacity="0.55" stroke-linecap="round"/>
18 + <!-- the facets read as an index line -->
19 + <path d="M16.5 34.5 24 42.5 31 32.5 37.5 38 46 26.5" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
20 + <path d="M40.5 26.5H46V32" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
21 +</svg>
modified apps/web/public/icon-192.png +0 −0

Binary file not shown.

modified apps/web/public/icon-512.png +0 −0

Binary file not shown.

modified apps/web/public/icon-maskable-512.png +0 −0

Binary file not shown.

modified apps/web/public/icon.svg +21 −1
@@ -1 +1,21 @@
1 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#0a0a0b"/><path d="M20 46V18h13.5c6.4 0 10.5 3.6 10.5 9.2 0 4.2-2.3 7.2-6.1 8.4L45 46h-7.3l-6.2-9.4H26.4V46H20zm6.4-14.6h6.5c3.2 0 5.1-1.6 5.1-4.2s-1.9-4.1-5.1-4.1h-6.5v8.3z" fill="#ffffff"/><path d="M12 52l9-9 6 5 11-12 8 7" stroke="#3ccb82" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
1 +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="RareIndex">
2 + <title>RareIndex mark</title>
3 + <defs>
4 + <linearGradient id="ri-gold" x1="12" y1="22" x2="52" y2="54" gradientUnits="userSpaceOnUse">
5 + <stop offset="0" stop-color="#F1D08A"/>
6 + <stop offset="1" stop-color="#C9963F"/>
7 + </linearGradient>
8 + <linearGradient id="ri-mint" x1="16" y1="42" x2="46" y2="24" gradientUnits="userSpaceOnUse">
9 + <stop offset="0" stop-color="#2FB673"/>
10 + <stop offset="1" stop-color="#5FE3A1"/>
11 + </linearGradient>
12 + </defs>
13 + <rect width="64" height="64" rx="14" fill="#0B0F19"/>
14 + <rect x="0.75" y="0.75" width="62.5" height="62.5" rx="13.25" fill="none" stroke="#FFFFFF" stroke-opacity="0.08" stroke-width="1.5"/>
15 + <!-- cut stone: crown, girdle, pavilion -->
16 + <path d="M19 19.5H45L52.5 29.5 32 54.5 11.5 29.5Z" fill="url(#ri-gold)" fill-opacity="0.12" stroke="url(#ri-gold)" stroke-width="3" stroke-linejoin="round"/>
17 + <path d="M11.5 29.5H52.5" stroke="url(#ri-gold)" stroke-width="2" stroke-opacity="0.55" stroke-linecap="round"/>
18 + <!-- the facets read as an index line -->
19 + <path d="M16.5 34.5 24 42.5 31 32.5 37.5 38 46 26.5" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
20 + <path d="M40.5 26.5H46V32" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
21 +</svg>
added apps/web/scripts/brand-assets.mjs +50 −0
@@ -0,0 +1,50 @@
1 +// Generates favicons / PWA icons / Apple touch icon / favicon.ico from public/brand/rareindex-mark.svg.
2 +// Run from apps/web: node scripts/brand-assets.mjs
3 +import { readFileSync, writeFileSync, copyFileSync } from 'node:fs';
4 +import sharp from 'sharp';
5 +
6 +const svg = readFileSync('public/brand/rareindex-mark.svg');
7 +const png = (size) => sharp(svg, { density: Math.ceil((size / 64) * 72) }).resize(size, size).png().toBuffer();
8 +
9 +const out = {
10 + 'src/app/icon.png': 32,
11 + 'src/app/apple-icon.png': 180,
12 + 'public/apple-touch-icon.png': 180,
13 + 'public/icon-192.png': 192,
14 + 'public/icon-512.png': 512,
15 +};
16 +for (const [path, size] of Object.entries(out)) writeFileSync(path, await png(size));
17 +
18 +// maskable: the mark on a full-bleed ink square with safe-zone padding (icon at 80 %)
19 +const inner = await png(410);
20 +const maskable = await sharp({ create: { width: 512, height: 512, channels: 4, background: '#0B0F19' } })
21 + .composite([{ input: inner, gravity: 'centre' }])
22 + .png()
23 + .toBuffer();
24 +writeFileSync('public/icon-maskable-512.png', maskable);
25 +
26 +// favicon.ico = PNG-in-ICO container with 16, 32 and 48 px images
27 +const sizes = [16, 32, 48];
28 +const images = await Promise.all(sizes.map((s) => png(s)));
29 +const header = Buffer.alloc(6);
30 +header.writeUInt16LE(0, 0); // reserved
31 +header.writeUInt16LE(1, 2); // type: icon
32 +header.writeUInt16LE(images.length, 4);
33 +const dir = Buffer.alloc(16 * images.length);
34 +let offset = 6 + dir.length;
35 +images.forEach((img, i) => {
36 + const s = sizes[i];
37 + dir.writeUInt8(s === 256 ? 0 : s, i * 16 + 0);
38 + dir.writeUInt8(s === 256 ? 0 : s, i * 16 + 1);
39 + dir.writeUInt8(0, i * 16 + 2); // palette
40 + dir.writeUInt8(0, i * 16 + 3); // reserved
41 + dir.writeUInt16LE(1, i * 16 + 4); // planes
42 + dir.writeUInt16LE(32, i * 16 + 6); // bpp
43 + dir.writeUInt32LE(img.length, i * 16 + 8);
44 + dir.writeUInt32LE(offset, i * 16 + 12);
45 + offset += img.length;
46 +});
47 +writeFileSync('src/app/favicon.ico', Buffer.concat([header, dir, ...images]));
48 +copyFileSync('public/brand/rareindex-mark.svg', 'src/app/icon.svg');
49 +copyFileSync('public/brand/rareindex-mark.svg', 'public/icon.svg');
50 +console.log('brand assets written');
added apps/web/src/app/apple-icon.png +0 −0

Binary file not shown.

modified apps/web/src/app/asset/[slug]/opengraph-image.tsx +6 −2
@@ -1,6 +1,7 @@
1 1 import { ImageResponse } from 'next/og';
2 2 import { getAssetBySlug } from '@/lib/queries/assets';
3 3 import { catName } from '@/lib/taxonomy';
4 +import { ogMark } from '@/components/layout/logo';
4 5
5 6 export const runtime = 'nodejs';
6 7 export const alt = 'RareIndex asset card';
@@ -23,8 +24,11 @@ export default async function Image({ params }: { params: Promise<{ slug: string
23 24 (
24 25 <div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', padding: 64, background: '#0b0c0e', color: '#f2f2f0', fontFamily: 'sans-serif' }}>
25 26 <div style={{ display: 'flex', alignItems: 'center', gap: 14, fontSize: 28 }}>
26 <div style={{ width: 40, height: 40, background: '#f2f2f0', color: '#0b0c0e', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, borderRadius: 6 }}>R</div>
27 <span style={{ fontWeight: 600 }}>RareIndex</span>
27 + {ogMark(44)}
28 + <span style={{ display: 'flex', fontWeight: 600 }}>
29 + <span>Rare</span>
30 + <span style={{ color: '#D9AE5F' }}>Index</span>
31 + </span>
28 32 <span style={{ color: '#8b8b93' }}>· {cat}</span>
29 33 </div>
30 34 <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
added apps/web/src/app/favicon.ico +0 −0

Binary file not shown.

modified apps/web/src/app/icon.png +0 −0

Binary file not shown.

added apps/web/src/app/icon.svg +21 −0
@@ -0,0 +1,21 @@
1 +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="RareIndex">
2 + <title>RareIndex mark</title>
3 + <defs>
4 + <linearGradient id="ri-gold" x1="12" y1="22" x2="52" y2="54" gradientUnits="userSpaceOnUse">
5 + <stop offset="0" stop-color="#F1D08A"/>
6 + <stop offset="1" stop-color="#C9963F"/>
7 + </linearGradient>
8 + <linearGradient id="ri-mint" x1="16" y1="42" x2="46" y2="24" gradientUnits="userSpaceOnUse">
9 + <stop offset="0" stop-color="#2FB673"/>
10 + <stop offset="1" stop-color="#5FE3A1"/>
11 + </linearGradient>
12 + </defs>
13 + <rect width="64" height="64" rx="14" fill="#0B0F19"/>
14 + <rect x="0.75" y="0.75" width="62.5" height="62.5" rx="13.25" fill="none" stroke="#FFFFFF" stroke-opacity="0.08" stroke-width="1.5"/>
15 + <!-- cut stone: crown, girdle, pavilion -->
16 + <path d="M19 19.5H45L52.5 29.5 32 54.5 11.5 29.5Z" fill="url(#ri-gold)" fill-opacity="0.12" stroke="url(#ri-gold)" stroke-width="3" stroke-linejoin="round"/>
17 + <path d="M11.5 29.5H52.5" stroke="url(#ri-gold)" stroke-width="2" stroke-opacity="0.55" stroke-linecap="round"/>
18 + <!-- the facets read as an index line -->
19 + <path d="M16.5 34.5 24 42.5 31 32.5 37.5 38 46 26.5" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
20 + <path d="M40.5 26.5H46V32" fill="none" stroke="url(#ri-mint)" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
21 +</svg>
added apps/web/src/app/opengraph-image.tsx +50 −0
@@ -0,0 +1,50 @@
1 +import { ImageResponse } from 'next/og';
2 +import { ogMark } from '@/components/layout/logo';
3 +
4 +export const runtime = 'nodejs';
5 +export const alt = 'RareIndex — The Global Market for Collectibles';
6 +export const size = { width: 1200, height: 630 };
7 +export const contentType = 'image/png';
8 +
9 +/** Site-wide share card (Open Graph / Twitter): mark, wordmark, tagline and an index line. Static — no database. */
10 +export default function Image() {
11 + return new ImageResponse(
12 + (
13 + <div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', padding: 72, background: 'linear-gradient(135deg, #0B0F19 0%, #10182A 60%, #0B0F19 100%)', color: '#F4F4F2', fontFamily: 'sans-serif', position: 'relative' }}>
14 + {/* faint index line across the card */}
15 + <svg width="1200" height="630" viewBox="0 0 1200 630" style={{ position: 'absolute', top: 0, left: 0 }}>
16 + <path d="M0 470 L140 430 L260 455 L380 380 L520 400 L640 320 L780 340 L900 250 L1040 270 L1200 170" fill="none" stroke="#3CCB82" strokeOpacity="0.28" strokeWidth="4" strokeLinejoin="round" strokeLinecap="round" />
17 + <path d="M0 470 L140 430 L260 455 L380 380 L520 400 L640 320 L780 340 L900 250 L1040 270 L1200 170 L1200 630 L0 630 Z" fill="#3CCB82" fillOpacity="0.05" />
18 + <g stroke="#FFFFFF" strokeOpacity="0.05">
19 + <path d="M0 158H1200M0 316H1200M0 474H1200" />
20 + </g>
21 + </svg>
22 + <div style={{ display: 'flex', alignItems: 'center', gap: 28 }}>
23 + {ogMark(120)}
24 + <div style={{ display: 'flex', flexDirection: 'column' }}>
25 + <div style={{ display: 'flex', fontSize: 84, fontWeight: 700, letterSpacing: -3, lineHeight: 1 }}>
26 + <span>Rare</span>
27 + <span style={{ color: '#D9AE5F' }}>Index</span>
28 + </div>
29 + <div style={{ marginTop: 10, fontSize: 26, letterSpacing: 6, color: '#9AA0AB', textTransform: 'uppercase' }}>The Global Market for Collectibles</div>
30 + </div>
31 + </div>
32 + <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
33 + <div style={{ fontSize: 34, lineHeight: 1.3, color: '#E6E6E2', maxWidth: 980 }}>Prices, verified sales, live asks, rarity, liquidity and the RARE indices — across marketplaces and auction houses, with confidence on every number.</div>
34 + <div style={{ display: 'flex', gap: 14, fontSize: 22, color: '#9AA0AB' }}>
35 + {['RareIndex Valuation', 'Liquidity Score', 'Market Depth', 'Auction Intelligence', 'Screener'].map((t) => (
36 + <span key={t} style={{ border: '1px solid rgba(255,255,255,0.14)', borderRadius: 8, padding: '6px 14px' }}>
37 + {t}
38 + </span>
39 + ))}
40 + </div>
41 + </div>
42 + <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 24, color: '#6F7580' }}>
43 + <span>www.rareindex.io</span>
44 + <span>Model estimates, not advice · Hosted on MacLustr</span>
45 + </div>
46 + </div>
47 + ),
48 + { ...size },
49 + );
50 +}
added apps/web/src/app/twitter-image.tsx +1 −0
@@ -0,0 +1 @@
1 +export { default, alt, size, contentType, runtime } from './opengraph-image';
modified apps/web/src/app/u/[handle]/opengraph-image.tsx +2 −1
@@ -1,4 +1,5 @@
1 1 import { ImageResponse } from 'next/og';
2 +import { ogMark } from '@/components/layout/logo';
2 3 import { publicProfile } from '@/lib/account/queries';
3 4 import { summarizePortfolio } from '@/lib/account/portfolio';
4 5 import { fmtMoney } from '@/lib/format';
@@ -18,7 +19,7 @@ export default async function Image({ params }: { params: Promise<{ handle: stri
18 19 (
19 20 <div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', padding: 64, background: '#fafaf9', color: '#0b0b0c', fontFamily: 'sans-serif' }}>
20 21 <div style={{ display: 'flex', alignItems: 'center', gap: 12, fontSize: 28, fontWeight: 600 }}>
21 <div style={{ width: 40, height: 40, background: '#0b0b0c', color: '#fff', borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 22 }}>R</div>
22 + {ogMark(40)}
22 23 RareIndex <span style={{ color: '#55555b', fontWeight: 400 }}>· collector profile</span>
23 24 </div>
24 25 <div style={{ display: 'flex', flexDirection: 'column' }}>
modified apps/web/src/components/layout/logo.tsx +57 −5
@@ -1,10 +1,62 @@
1 /** The RareIndex mark: a square with an "R" and a rising tick — used in the header, PWA icons and OG images. */
2 export function LogoMark({ className }: { className?: string }) {
1 +/**
2 + * The RareIndex mark: a cut stone (crown, girdle, pavilion) whose facets are drawn by an ascending
3 + * index line — rarity and market data in one shape. Source of truth: public/brand/rareindex-mark.svg
4 + * (favicons, PWA icons and share images are generated from it by scripts/brand-assets.mjs).
5 + */
6 +export function LogoMark({ className, id = 'ri' }: { className?: string; id?: string }) {
7 + const gold = `${id}-gold`;
8 + const mint = `${id}-mint`;
3 9 return (
4 10 <svg viewBox="0 0 64 64" className={className} aria-hidden focusable="false">
5 <rect width="64" height="64" rx="12" fill="var(--ri-accent, #0a0a0b)" />
6 <path d="M20 46V18h13.5c6.4 0 10.5 3.6 10.5 9.2 0 4.2-2.3 7.2-6.1 8.4L45 46h-7.3l-6.2-9.4H26.4V46H20zm6.4-14.6h6.5c3.2 0 5.1-1.6 5.1-4.2s-1.9-4.1-5.1-4.1h-6.5v8.3z" fill="var(--ri-accent-fg, #ffffff)" />
7 <path d="M12 52l9-9 6 5 11-12 8 7" stroke="var(--ri-gain, #3ccb82)" strokeWidth="3" fill="none" strokeLinecap="round" strokeLinejoin="round" opacity="0.95" />
11 + <defs>
12 + <linearGradient id={gold} x1="12" y1="22" x2="52" y2="54" gradientUnits="userSpaceOnUse">
13 + <stop offset="0" stopColor="#F1D08A" />
14 + <stop offset="1" stopColor="#C9963F" />
15 + </linearGradient>
16 + <linearGradient id={mint} x1="16" y1="42" x2="46" y2="24" gradientUnits="userSpaceOnUse">
17 + <stop offset="0" stopColor="#2FB673" />
18 + <stop offset="1" stopColor="#5FE3A1" />
19 + </linearGradient>
20 + </defs>
21 + <rect width="64" height="64" rx="14" fill="#0B0F19" />
22 + <rect x="0.75" y="0.75" width="62.5" height="62.5" rx="13.25" fill="none" stroke="#FFFFFF" strokeOpacity="0.08" strokeWidth="1.5" />
23 + <path d="M19 19.5H45L52.5 29.5 32 54.5 11.5 29.5Z" fill={`url(#${gold})`} fillOpacity="0.12" stroke={`url(#${gold})`} strokeWidth="3" strokeLinejoin="round" />
24 + <path d="M11.5 29.5H52.5" stroke={`url(#${gold})`} strokeWidth="2" strokeOpacity="0.55" strokeLinecap="round" />
25 + <path d="M16.5 34.5 24 42.5 31 32.5 37.5 38 46 26.5" fill="none" stroke={`url(#${mint})`} strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round" />
26 + <path d="M40.5 26.5H46V32" fill="none" stroke={`url(#${mint})`} strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round" />
27 + </svg>
28 + );
29 +}
30 +
31 +/** Wordmark: "Rare" in the foreground colour, "Index" in brand gold. */
32 +export function LogoWordmark({ className }: { className?: string }) {
33 + return (
34 + <span className={`font-semibold tracking-tight ${className ?? ''}`}>
35 + Rare<span className="text-[#C9963F]">Index</span>
36 + </span>
37 + );
38 +}
39 +
40 +/** Static markup for Satori (next/og): no CSS variables, plain attributes, unique gradient ids. */
41 +export function ogMark(size: number) {
42 + return (
43 + <svg viewBox="0 0 64 64" width={size} height={size}>
44 + <defs>
45 + <linearGradient id="og-gold" x1="12" y1="22" x2="52" y2="54" gradientUnits="userSpaceOnUse">
46 + <stop offset="0" stopColor="#F1D08A" />
47 + <stop offset="1" stopColor="#C9963F" />
48 + </linearGradient>
49 + <linearGradient id="og-mint" x1="16" y1="42" x2="46" y2="24" gradientUnits="userSpaceOnUse">
50 + <stop offset="0" stopColor="#2FB673" />
51 + <stop offset="1" stopColor="#5FE3A1" />
52 + </linearGradient>
53 + </defs>
54 + <rect width="64" height="64" rx="14" fill="#0B0F19" />
55 + <rect x="0.75" y="0.75" width="62.5" height="62.5" rx="13.25" fill="none" stroke="#FFFFFF" strokeOpacity="0.14" strokeWidth="1.5" />
56 + <path d="M19 19.5H45L52.5 29.5 32 54.5 11.5 29.5Z" fill="url(#og-gold)" fillOpacity="0.12" stroke="url(#og-gold)" strokeWidth="3" strokeLinejoin="round" />
57 + <path d="M11.5 29.5H52.5" stroke="url(#og-gold)" strokeWidth="2" strokeOpacity="0.55" strokeLinecap="round" />
58 + <path d="M16.5 34.5 24 42.5 31 32.5 37.5 38 46 26.5" fill="none" stroke="url(#og-mint)" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round" />
59 + <path d="M40.5 26.5H46V32" fill="none" stroke="url(#og-mint)" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round" />
8 60 </svg>
9 61 );
10 62 }
modified apps/web/src/components/layout/site-header.tsx +1 −1
@@ -12,7 +12,7 @@ export function Logo({ className, compact = false }: { className?: string; compa
12 12 <LogoMark className="h-6 w-6" />
13 13 {!compact ? (
14 14 <span className="text-[15px] font-semibold tracking-tight text-fg">
15 Rare<span className="text-muted">Index</span>
15 + Rare<span className="text-[#C9963F]">Index</span>
16 16 </span>
17 17 ) : null}
18 18 </Link>
19 19