100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
|
|
/**
|
|
* The single card system for the "For People" page.
|
|
* Wraps the shared `.fp-card*` classes (defined in VerticalScrollWrapper) so
|
|
* every section imports one component and the markup can't drift.
|
|
*/
|
|
|
|
type Theme = 'light' | 'dark' | 'fill';
|
|
|
|
const themeClass: Record<Theme, string> = {
|
|
light: 'fp-card--light',
|
|
dark: 'fp-card--dark',
|
|
fill: 'fp-card--fill',
|
|
};
|
|
|
|
const DEFAULT_ICON_BG: Record<Theme, string> = {
|
|
light: 'rgba(244,190,40,0.14)',
|
|
dark: 'rgba(244,190,40,0.14)',
|
|
fill: 'rgba(244,190,40,0.16)',
|
|
};
|
|
|
|
const sora = 'Sora, sans-serif';
|
|
const GOLD = '#f4be28';
|
|
|
|
interface TagCardProps {
|
|
theme?: Theme;
|
|
icon: React.ReactNode;
|
|
/** Icon-badge background; falls back to the theme's gold tint. */
|
|
iconBg?: string;
|
|
/** Icon tint — icons draw in `currentColor`. Falls back to the theme's gold. */
|
|
iconColor?: string;
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
style?: React.CSSProperties;
|
|
}
|
|
|
|
/** Compact icon + label pill (radius 12). */
|
|
export function FpTagCard({ theme = 'light', icon, iconBg, iconColor, children, className = '', style }: TagCardProps) {
|
|
return (
|
|
<div className={`fp-card ${themeClass[theme]} ${className}`.trim()} style={style}>
|
|
<span className="fp-card-icon" style={{ background: iconBg ?? DEFAULT_ICON_BG[theme], color: iconColor ?? GOLD }}>{icon}</span>
|
|
<span style={{
|
|
fontSize: 13, fontWeight: 700, fontFamily: sora, letterSpacing: '-0.01em',
|
|
whiteSpace: 'nowrap', color: theme === 'light' ? '#171512' : '#fff',
|
|
}}>
|
|
{children}
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface StatCardProps {
|
|
theme?: Theme;
|
|
layout?: 'row' | 'column';
|
|
icon: React.ReactNode;
|
|
iconBg?: string;
|
|
/** Icon tint — icons draw in `currentColor`. Falls back to the theme's gold. */
|
|
iconColor?: string;
|
|
/** May be a plain node or a count-up <span> ref. */
|
|
value: React.ReactNode;
|
|
label: string;
|
|
/** Value text color; defaults to theme-appropriate. */
|
|
color?: string;
|
|
className?: string;
|
|
style?: React.CSSProperties;
|
|
}
|
|
|
|
/** Larger icon + value + label card (radius 20). */
|
|
export function FpStatCard({
|
|
theme = 'light', layout = 'column', icon, iconBg, iconColor, value, label, color, className = '', style,
|
|
}: StatCardProps) {
|
|
const dark = theme !== 'light';
|
|
const column = layout === 'column';
|
|
return (
|
|
<div
|
|
className={`fp-card fp-card--stat ${themeClass[theme]} ${className}`.trim()}
|
|
style={{ flexDirection: column ? 'column' : 'row', alignItems: 'center', ...style }}
|
|
>
|
|
<span className="fp-card-icon fp-card-icon--lg" style={{ background: iconBg ?? DEFAULT_ICON_BG[theme], color: iconColor ?? GOLD }}>{icon}</span>
|
|
<div style={{ textAlign: column ? 'center' : 'left' }}>
|
|
<div style={{
|
|
fontSize: column ? 'clamp(20px, 2.4vw, 26px)' : 18, fontWeight: 900, fontFamily: sora,
|
|
letterSpacing: '-0.03em', lineHeight: 1, color: color ?? (dark ? '#fff' : '#171512'),
|
|
}}>
|
|
{value}
|
|
</div>
|
|
<div style={{
|
|
fontSize: column ? 11.5 : 10.5, fontWeight: 600, marginTop: 5, fontFamily: sora,
|
|
color: dark ? 'rgba(255,255,255,0.55)' : 'rgba(23,21,18,0.5)',
|
|
}}>
|
|
{label}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|