feat: adapt dashboard and subscription page for light theme

- Add glassTheme utility with theme-aware color tokens
- Replace hardcoded rgba(255,255,255,...) with dynamic values
- Replace text-white with text-dark-50 for auto theme remapping
- Restyle subscription page block to match dashboard glassmorphic design
- Light mode: white semi-opaque cards with subtle shadows
- Dark mode: unchanged visual appearance
This commit is contained in:
Fringg
2026-02-25 11:00:14 +03:00
parent a819f309c8
commit f474067efb
7 changed files with 921 additions and 478 deletions

View File

@@ -3,7 +3,9 @@ import { useTranslation } from 'react-i18next';
import { Link } from 'react-router';
import type { Subscription } from '../../types';
import { useCurrency } from '../../hooks/useCurrency';
import { useTheme } from '../../hooks/useTheme';
import { getTrafficZone } from '../../utils/trafficZone';
import { getGlassColors } from '../../utils/glassTheme';
interface StatsGridProps {
balanceRubles: number;
@@ -14,18 +16,18 @@ interface StatsGridProps {
refLoading: boolean;
}
const ChevronIcon = () => (
const ChevronIcon = ({ color }: { color: string }) => (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
style={{ opacity: 0.25, flexShrink: 0 }}
style={{ flexShrink: 0 }}
aria-hidden="true"
>
<path
d="M6 4l4 4-4 4"
stroke="#fff"
stroke={color}
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
@@ -43,6 +45,8 @@ export default function StatsGrid({
}: StatsGridProps) {
const { t } = useTranslation();
const { formatAmount, currencySymbol, formatPositive } = useCurrency();
const { isDark } = useTheme();
const g = getGlassColors(isDark);
const zone = useMemo(
() => getTrafficZone(subscription?.traffic_used_percent ?? 0),
@@ -81,7 +85,7 @@ export default function StatsGrid({
label: t('dashboard.stats.subscription'),
value: subscription ? `${subscription.days_left}` : '—',
valueSuffix: subscription ? ` ${t('subscription.daysShort')}` : '',
valueColor: '#fff',
valueColor: g.text,
to: '/subscription',
icon: (color: string) => (
<svg
@@ -99,15 +103,15 @@ export default function StatsGrid({
<circle cx="7" cy="7" r="1" />
</svg>
),
iconBg: 'rgba(255,255,255,0.06)',
iconColor: 'rgba(255,255,255,0.45)',
iconBg: g.trackBg,
iconColor: g.textSecondary,
loading: subLoading,
onboarding: 'subscription-status',
},
{
label: t('dashboard.stats.referrals'),
value: `${referralCount}`,
valueColor: '#fff',
valueColor: g.text,
to: '/referral',
icon: (color: string) => (
<svg
@@ -126,8 +130,8 @@ export default function StatsGrid({
<path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" />
</svg>
),
iconBg: 'rgba(255,255,255,0.06)',
iconColor: 'rgba(255,255,255,0.45)',
iconBg: g.trackBg,
iconColor: g.textSecondary,
loading: refLoading,
},
{
@@ -165,9 +169,9 @@ export default function StatsGrid({
to={card.to}
className="group relative overflow-hidden rounded-[18px] transition-all duration-200"
style={{
background:
'linear-gradient(145deg, rgba(255,255,255,0.045) 0%, rgba(255,255,255,0.02) 100%)',
border: '1px solid rgba(255,255,255,0.06)',
background: g.cardBg,
border: `1px solid ${g.cardBorder}`,
boxShadow: g.shadow,
padding: '18px 20px 20px',
}}
data-onboarding={card.onboarding}
@@ -181,9 +185,9 @@ export default function StatsGrid({
>
{card.icon(card.iconColor)}
</div>
<span className="text-[13px] font-medium text-white/45">{card.label}</span>
<span className="text-[13px] font-medium text-dark-50/45">{card.label}</span>
</div>
<ChevronIcon />
<ChevronIcon color={g.textFaint} />
</div>
{/* Value */}
@@ -196,7 +200,7 @@ export default function StatsGrid({
>
{card.value}
{card.valueSuffix && (
<span className="ml-0.5 text-base font-medium text-white/35">
<span className="ml-0.5 text-base font-medium text-dark-50/35">
{card.valueSuffix}
</span>
)}

View File

@@ -6,8 +6,10 @@ import { UseMutationResult } from '@tanstack/react-query';
import TrafficProgressBar from './TrafficProgressBar';
import Sparkline from './Sparkline';
import { useAnimatedNumber } from '../../hooks/useAnimatedNumber';
import { useTheme } from '../../hooks/useTheme';
import { getTrafficZone } from '../../utils/trafficZone';
import { formatTraffic } from '../../utils/formatTraffic';
import { getGlassColors } from '../../utils/glassTheme';
import type { Subscription } from '../../types';
interface SubscriptionCardActiveProps {
@@ -48,6 +50,8 @@ export default function SubscriptionCardActive({
}: SubscriptionCardActiveProps) {
const { t } = useTranslation();
const navigate = useNavigate();
const { isDark } = useTheme();
const g = getGlassColors(isDark);
const usedPercent = trafficData?.traffic_used_percent ?? subscription.traffic_used_percent;
const usedGb = trafficData?.traffic_used_gb ?? subscription.traffic_used_gb;
@@ -65,12 +69,12 @@ export default function SubscriptionCardActive({
<div
className="relative overflow-hidden rounded-3xl backdrop-blur-xl"
style={{
background:
'linear-gradient(145deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)',
background: g.cardBg,
border: subscription.is_trial
? '1px solid rgba(62,219,176,0.15)'
: '1px solid rgba(255,255,255,0.07)',
: `1px solid ${g.cardBorder}`,
padding: '28px 28px 24px',
boxShadow: g.shadow,
}}
>
{/* Trial shimmer border */}
@@ -90,7 +94,7 @@ export default function SubscriptionCardActive({
width: 200,
height: 200,
borderRadius: '50%',
background: `radial-gradient(circle, ${zone.mainHex}15 0%, transparent 70%)`,
background: `radial-gradient(circle, ${zone.mainHex}${g.glowAlpha} 0%, transparent 70%)`,
transition: 'background 0.8s ease',
}}
aria-hidden="true"
@@ -147,7 +151,7 @@ export default function SubscriptionCardActive({
</div>
{/* Title */}
<h2 className="text-lg font-bold tracking-tight text-white">
<h2 className="text-lg font-bold tracking-tight text-dark-50">
{t('dashboard.trafficUsageTitle')}
</h2>
</div>
@@ -162,17 +166,17 @@ export default function SubscriptionCardActive({
>
&#8734;
</div>
<div className="mt-1 font-mono text-[11px] text-white/30">
<div className="mt-1 font-mono text-[11px] text-dark-50/30">
{formatTraffic(usedGb)} {t('dashboard.usedSuffix')}
</div>
</>
) : (
<>
<div className="font-display text-[38px] font-extrabold leading-none tracking-tight text-white">
<div className="font-display text-[38px] font-extrabold leading-none tracking-tight text-dark-50">
{animatedPercent.toFixed(0)}
<span className="ml-px text-lg font-medium text-white/35">%</span>
<span className="ml-px text-lg font-medium text-dark-50/35">%</span>
</div>
<div className="mt-0.5 font-mono text-[11px] text-white/30">
<div className="mt-0.5 font-mono text-[11px] text-dark-50/30">
{formatTraffic(usedGb)} / {formatTraffic(subscription.traffic_limit_gb)}
</div>
</>
@@ -194,9 +198,13 @@ export default function SubscriptionCardActive({
{subscription.subscription_url && (
<button
onClick={() => navigate('/connection')}
className="mb-2.5 flex w-full items-center gap-3.5 rounded-[14px] border border-white/[0.04] bg-white/[0.03] p-3.5 text-left transition-all duration-300 hover:border-white/[0.08] hover:bg-white/[0.05]"
className="mb-2.5 flex w-full items-center gap-3.5 rounded-[14px] p-3.5 text-left transition-all duration-300"
data-onboarding="connect-devices"
style={{ fontFamily: 'inherit' }}
style={{
fontFamily: 'inherit',
background: g.innerBg,
border: `1px solid ${g.innerBorder}`,
}}
>
{/* Monitor icon */}
<div
@@ -222,10 +230,10 @@ export default function SubscriptionCardActive({
{/* Text */}
<div className="min-w-0 flex-1">
<div className="text-sm font-semibold tracking-tight text-white">
<div className="text-sm font-semibold tracking-tight text-dark-50">
{t('dashboard.connectDevice')}
</div>
<div className="mt-0.5 text-[11px] text-white/30">
<div className="mt-0.5 text-[11px] text-dark-50/30">
{t('dashboard.devicesOfMax', {
used: connectedDevices,
max: subscription.device_limit,
@@ -240,7 +248,7 @@ export default function SubscriptionCardActive({
key={i}
className="h-[7px] w-[7px] rounded-full transition-all duration-300"
style={{
background: i < connectedDevices ? zone.mainHex : 'rgba(255,255,255,0.08)',
background: i < connectedDevices ? zone.mainHex : g.textGhost,
boxShadow: i < connectedDevices ? `0 0 6px ${zone.mainHex}50` : 'none',
}}
/>
@@ -266,27 +274,27 @@ export default function SubscriptionCardActive({
>
{t('dashboard.tariff')}
</div>
<div className="text-base font-bold leading-tight tracking-tight text-white">
<div className="text-base font-bold leading-tight tracking-tight text-dark-50">
{subscription.tariff_name || t('subscription.currentPlan')}
</div>
<div className="mt-0.5 font-mono text-[10px] text-white/30">
<div className="mt-0.5 font-mono text-[10px] text-dark-50/30">
{t('dashboard.validUntil', { date: formattedDate })}
</div>
</Link>
{/* Days remaining */}
<div
className="flex-1 rounded-[14px] bg-white/[0.03] p-3.5 transition-colors duration-300"
className="flex-1 rounded-[14px] p-3.5 transition-colors duration-300"
style={{
border:
daysLeft <= 3 ? '1px solid rgba(255,184,0,0.2)' : '1px solid rgba(255,255,255,0.04)',
background: g.innerBg,
border: daysLeft <= 3 ? '1px solid rgba(255,184,0,0.2)' : `1px solid ${g.innerBorder}`,
}}
>
<div className="mb-1 flex items-center gap-1.5 text-[10px] font-medium uppercase tracking-wider text-white/35">
<div className="mb-1 flex items-center gap-1.5 text-[10px] font-medium uppercase tracking-wider text-dark-50/35">
<div
className="flex h-6 w-6 items-center justify-center rounded-[7px] transition-colors duration-300"
style={{
background: daysLeft <= 3 ? 'rgba(255,184,0,0.1)' : 'rgba(255,255,255,0.05)',
background: daysLeft <= 3 ? 'rgba(255,184,0,0.1)' : g.hoverBg,
}}
>
<svg
@@ -294,7 +302,7 @@ export default function SubscriptionCardActive({
height="13"
viewBox="0 0 24 24"
fill="none"
stroke={daysLeft <= 3 ? '#FFB800' : 'rgba(255,255,255,0.4)'}
stroke={daysLeft <= 3 ? '#FFB800' : g.textSecondary}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
@@ -309,11 +317,13 @@ export default function SubscriptionCardActive({
<div className="flex items-baseline gap-1">
<span
className="text-[22px] font-bold tracking-tight transition-colors duration-300"
style={{ color: daysLeft <= 3 ? '#FFB800' : '#fff' }}
style={{ color: daysLeft <= 3 ? '#FFB800' : g.text }}
>
{daysLeft}
</span>
<span className="text-xs font-medium text-white/25">{t('subscription.daysShort')}</span>
<span className="text-xs font-medium text-dark-50/25">
{t('subscription.daysShort')}
</span>
</div>
</div>
</div>
@@ -323,7 +333,7 @@ export default function SubscriptionCardActive({
<button
onClick={() => refreshTrafficMutation.mutate()}
disabled={refreshTrafficMutation.isPending || trafficRefreshCooldown > 0}
className="flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium text-white/35 transition-colors hover:bg-white/[0.05] hover:text-white/50 disabled:cursor-not-allowed disabled:opacity-50"
className="flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium text-dark-50/35 transition-colors hover:bg-dark-50/[0.05] hover:text-dark-50/50 disabled:cursor-not-allowed disabled:opacity-50"
aria-label={t('common.refresh')}
>
<RefreshIcon
@@ -333,7 +343,7 @@ export default function SubscriptionCardActive({
</button>
<Link
to="/subscription"
className="text-[11px] font-medium text-white/25 transition-colors hover:text-white/40"
className="text-[11px] font-medium text-dark-50/25 transition-colors hover:text-dark-50/40"
>
{t('dashboard.viewSubscription')} &rarr;
</Link>
@@ -341,12 +351,15 @@ export default function SubscriptionCardActive({
{/* ─── Sparkline ─── */}
{dailyUsage.length >= 2 && (
<div className="rounded-[14px] border border-white/[0.04] bg-white/[0.02] p-3.5 pb-3">
<div
className="rounded-[14px] p-3.5 pb-3"
style={{ background: g.innerBg, border: `1px solid ${g.innerBorder}` }}
>
<div className="mb-2.5 flex items-center justify-between">
<span className="text-[11px] font-medium uppercase tracking-wider text-white/40">
<span className="text-[11px] font-medium uppercase tracking-wider text-dark-50/40">
{t('dashboard.usageLast14Days')}
</span>
<span className="font-mono text-[11px] text-white/25">
<span className="font-mono text-[11px] text-dark-50/25">
{t('dashboard.maxUsage', { amount: formatTraffic(Math.max(...dailyUsage)) })}
</span>
</div>

View File

@@ -1,6 +1,8 @@
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router';
import type { Subscription } from '../../types';
import { useTheme } from '../../hooks/useTheme';
import { getGlassColors } from '../../utils/glassTheme';
interface SubscriptionCardExpiredProps {
subscription: Subscription;
@@ -8,6 +10,8 @@ interface SubscriptionCardExpiredProps {
export default function SubscriptionCardExpired({ subscription }: SubscriptionCardExpiredProps) {
const { t } = useTranslation();
const { isDark } = useTheme();
const g = getGlassColors(isDark);
const formattedDate = new Date(subscription.end_date).toLocaleDateString();
@@ -15,9 +19,9 @@ export default function SubscriptionCardExpired({ subscription }: SubscriptionCa
<div
className="relative overflow-hidden rounded-3xl"
style={{
background:
'linear-gradient(145deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)',
background: g.cardBg,
border: '1px solid rgba(255,70,70,0.12)',
boxShadow: g.shadow,
padding: '28px 28px 24px',
}}
>
@@ -72,10 +76,10 @@ export default function SubscriptionCardExpired({ subscription }: SubscriptionCa
</svg>
</div>
<div>
<h2 className="text-lg font-bold tracking-tight text-white">
<h2 className="text-lg font-bold tracking-tight text-dark-50">
{t('dashboard.expired.title')}
</h2>
<span className="text-xs text-white/35">
<span className="text-xs text-dark-50/35">
{subscription.is_trial
? t('dashboard.expired.trialSubtitle')
: t('dashboard.expired.paidSubtitle')}
@@ -128,10 +132,10 @@ export default function SubscriptionCardExpired({ subscription }: SubscriptionCa
{ label: t('dashboard.expired.expiredDate'), value: formattedDate },
].map((item, i) => (
<div key={i}>
<div className="mb-1 font-mono text-[10px] font-medium uppercase tracking-wider text-white/30">
<div className="mb-1 font-mono text-[10px] font-medium uppercase tracking-wider text-dark-50/30">
{item.label}
</div>
<div className="text-base font-bold tracking-tight text-white/50">{item.value}</div>
<div className="text-base font-bold tracking-tight text-dark-50/50">{item.value}</div>
</div>
))}
</div>
@@ -151,7 +155,11 @@ export default function SubscriptionCardExpired({ subscription }: SubscriptionCa
</Link>
<Link
to="/subscription"
className="flex items-center justify-center rounded-[14px] border border-white/[0.08] bg-white/[0.03] px-5 py-3.5 text-[15px] font-semibold tracking-tight text-white/50 transition-colors duration-200 hover:bg-white/[0.05]"
className="flex items-center justify-center rounded-[14px] px-5 py-3.5 text-[15px] font-semibold tracking-tight text-dark-50/50 transition-colors duration-200"
style={{
background: g.innerBg,
border: `1px solid ${g.innerBorder}`,
}}
>
{t('dashboard.expired.tariffs')}
</Link>

View File

@@ -2,6 +2,8 @@ import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { getTrafficZone } from '../../utils/trafficZone';
import { formatTraffic } from '../../utils/formatTraffic';
import { useTheme } from '../../hooks/useTheme';
import { getGlassColors } from '../../utils/glassTheme';
interface TrafficProgressBarProps {
usedGb: number;
@@ -21,6 +23,8 @@ export default function TrafficProgressBar({
compact = false,
}: TrafficProgressBarProps) {
const { t } = useTranslation();
const { isDark } = useTheme();
const g = getGlassColors(isDark);
const zone = useMemo(() => getTrafficZone(percent), [percent]);
const clampedPercent = Math.min(percent, 100);
const barHeight = compact ? 8 : 14;
@@ -42,7 +46,7 @@ export default function TrafficProgressBar({
style={{
height: barHeight,
borderRadius: 10,
background: 'rgba(255,255,255,0.06)',
background: g.trackBg,
border: `1px solid ${zone.mainHex}20`,
}}
>
@@ -90,7 +94,7 @@ export default function TrafficProgressBar({
/>
{t('dashboard.unlimitedTraffic')}
</span>
<span className="font-mono text-[11px] text-white/30">
<span className="font-mono text-[11px] text-dark-50/30">
{t('dashboard.usedTraffic', { amount: formatTraffic(usedGb) })}
</span>
</div>
@@ -113,8 +117,8 @@ export default function TrafficProgressBar({
style={{
height: barHeight,
borderRadius: 10,
background: 'rgba(255,255,255,0.06)',
border: '1px solid rgba(255,255,255,0.04)',
background: g.trackBg,
border: `1px solid ${g.trackBorder}`,
}}
>
{/* Warning zone tint backgrounds */}
@@ -171,7 +175,7 @@ export default function TrafficProgressBar({
style={{
left: `${threshold}%`,
width: 1,
background: 'rgba(255,255,255,0.08)',
background: g.textGhost,
}}
aria-hidden="true"
/>
@@ -199,7 +203,7 @@ export default function TrafficProgressBar({
{/* Scale labels */}
{!compact && limitGb > 0 && (
<div
className="mt-1.5 flex justify-between px-0.5 font-mono text-[9px] font-medium text-white/20"
className="mt-1.5 flex justify-between px-0.5 font-mono text-[9px] font-medium text-dark-50/20"
aria-hidden="true"
>
{[0, 25, 50, 75, 100].map((v) => (

View File

@@ -3,6 +3,8 @@ import { Link } from 'react-router';
import { UseMutationResult } from '@tanstack/react-query';
import type { TrialInfo } from '../../types';
import { useCurrency } from '../../hooks/useCurrency';
import { useTheme } from '../../hooks/useTheme';
import { getGlassColors } from '../../utils/glassTheme';
interface TrialOfferCardProps {
trialInfo: TrialInfo;
@@ -21,6 +23,8 @@ export default function TrialOfferCard({
}: TrialOfferCardProps) {
const { t } = useTranslation();
const { formatAmount, currencySymbol } = useCurrency();
const { isDark } = useTheme();
const g = getGlassColors(isDark);
const isFree = !trialInfo.requires_payment;
const canAfford = balanceKopeks >= trialInfo.price_kopeks;
@@ -28,9 +32,9 @@ export default function TrialOfferCard({
<div
className="relative overflow-hidden rounded-3xl text-center"
style={{
background:
'linear-gradient(145deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)',
border: '1px solid rgba(255,255,255,0.07)',
background: g.cardBg,
border: `1px solid ${g.cardBorder}`,
boxShadow: g.shadow,
padding: '32px 28px 28px',
}}
>
@@ -115,10 +119,10 @@ export default function TrialOfferCard({
</div>
{/* Title */}
<h2 className="mb-1.5 text-[22px] font-bold tracking-tight text-white">
<h2 className="mb-1.5 text-[22px] font-bold tracking-tight text-dark-50">
{isFree ? t('dashboard.trialOffer.freeTitle') : t('dashboard.trialOffer.paidTitle')}
</h2>
<p className="mb-5 text-sm text-white/40">
<p className="mb-5 text-sm text-dark-50/40">
{isFree ? t('dashboard.trialOffer.freeDesc') : t('dashboard.trialOffer.paidDesc')}
</p>
@@ -154,10 +158,10 @@ export default function TrialOfferCard({
{ value: String(trialInfo.device_limit), label: t('subscription.trial.devices') },
].map((stat, i) => (
<div key={i} className="text-center">
<div className="text-4xl font-extrabold leading-none tracking-tight text-white">
<div className="text-4xl font-extrabold leading-none tracking-tight text-dark-50">
{stat.value}
</div>
<div className="mt-1 text-xs font-medium text-white/30">{stat.label}</div>
<div className="mt-1 text-xs font-medium text-dark-50/30">{stat.label}</div>
</div>
))}
</div>
@@ -165,11 +169,11 @@ export default function TrialOfferCard({
{/* Balance info for paid trial */}
{!isFree && trialInfo.price_rubles > 0 && (
<div
className="mb-4 space-y-2 rounded-xl bg-white/[0.03] p-4 text-left"
style={{ border: '1px solid rgba(255,255,255,0.04)' }}
className="mb-4 space-y-2 rounded-xl p-4 text-left"
style={{ background: g.innerBg, border: `1px solid ${g.innerBorder}` }}
>
<div className="flex items-center justify-between">
<span className="text-sm text-white/40">{t('balance.currentBalance')}</span>
<span className="text-sm text-dark-50/40">{t('balance.currentBalance')}</span>
<span
className={`font-display text-sm font-semibold ${canAfford ? 'text-success-400' : 'text-warning-400'}`}
>

File diff suppressed because it is too large Load Diff

43
src/utils/glassTheme.ts Normal file
View File

@@ -0,0 +1,43 @@
/**
* Theme-aware glass morphism color tokens.
* Provides consistent colors for the glassmorphic card components
* that work on both dark and light backgrounds.
*/
export function getGlassColors(isDark: boolean) {
return {
// Card container
cardBg: isDark
? 'linear-gradient(145deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)'
: 'linear-gradient(145deg, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0.6) 100%)',
cardBorder: isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.08)',
// Inner sections (cards within cards)
innerBg: isDark ? 'rgba(255,255,255,0.03)' : 'rgba(0,0,0,0.03)',
innerBorder: isDark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.06)',
// Hover states
hoverBg: isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)',
hoverBorder: isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.1)',
// Text
text: isDark ? '#fff' : '#1a1a2e',
textSecondary: isDark ? 'rgba(255,255,255,0.4)' : 'rgba(0,0,0,0.5)',
textMuted: isDark ? 'rgba(255,255,255,0.3)' : 'rgba(0,0,0,0.35)',
textFaint: isDark ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.25)',
textGhost: isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)',
// Progress bar track
trackBg: isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)',
trackBorder: isDark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.06)',
// Code blocks
codeBg: isDark ? 'rgba(255,255,255,0.03)' : 'rgba(0,0,0,0.04)',
codeBorder: isDark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.06)',
// Glow effects — reduced in light mode
glowAlpha: isDark ? '15' : '08',
// Shadows for light mode depth
shadow: isDark ? 'none' : '0 2px 12px rgba(0,0,0,0.06)',
};
}