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>
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 | |