Optimize academy
This commit is contained in:
@@ -25,7 +25,7 @@ export default function PlanCard({ product, selectedPlan, currentTier, isSubscri
|
||||
const isActivePlan = selectedPlan?.key === activePlanKey
|
||||
// Pro subscribers already have creator access — don't show a separate "switch" CTA for creator card
|
||||
const isHigherTierCovered = activeTier === 'pro' && product.tier === 'creator'
|
||||
const isPlanReady = Boolean(selectedPlan?.configured && selectedPlan?.price_id_valid)
|
||||
const isPlanReady = Boolean(selectedPlan?.configured && selectedPlan?.price_id_valid && selectedPlan?.remote_price_exists !== false)
|
||||
// User has a different active subscription (not this plan)
|
||||
const isSubscribedElsewhere = isSubscribed && !isActivePlan
|
||||
|
||||
@@ -96,10 +96,10 @@ export default function PlanCard({ product, selectedPlan, currentTier, isSubscri
|
||||
onClick={() => onCheckout(selectedPlan)}
|
||||
tone="primary"
|
||||
>
|
||||
{!billingEnabled ? 'Coming soon' : isPlanReady ? `Get ${product.name} — ${selectedPlan?.price_display || ''}` : 'Not available yet'}
|
||||
{!billingEnabled ? 'Coming soon' : isPlanReady ? `Get ${product.name} — ${selectedPlan?.price_display || ''}` : 'Not available yet'}
|
||||
</ActionButton>
|
||||
) : null}
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,16 +386,6 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
|
||||
</a>
|
||||
) : null}
|
||||
|
||||
{enhanceUrl ? (
|
||||
<a
|
||||
href={enhanceUrl}
|
||||
className="inline-flex items-center gap-2 rounded-full border border-violet-300/25 bg-violet-400/12 px-5 py-2.5 text-sm font-medium text-violet-50 transition-all duration-200 hover:border-violet-200/40 hover:bg-violet-400/18 hover:text-white"
|
||||
>
|
||||
<EnhanceIcon />
|
||||
Enhance image
|
||||
</a>
|
||||
) : null}
|
||||
|
||||
{/* Report pill */}
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -279,7 +279,7 @@ function ActionLink({ href, label, children, onClick }) {
|
||||
href={href || '#'}
|
||||
aria-label={label}
|
||||
onClick={onClick}
|
||||
className="inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80"
|
||||
className="artwork-card__action-btn inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
@@ -292,7 +292,7 @@ function ActionButton({ label, children, onClick }) {
|
||||
type="button"
|
||||
aria-label={label}
|
||||
onClick={onClick}
|
||||
className="inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80"
|
||||
className="artwork-card__action-btn inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80"
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
@@ -832,7 +832,7 @@ export default function ArtworkCard({
|
||||
style={articleStyle}
|
||||
{...articleData}
|
||||
>
|
||||
<div className={cx('relative overflow-hidden rounded-[1.6rem] border border-white/8 bg-slate-950/80 shadow-[0_18px_60px_rgba(2,6,23,0.45)] transition duration-300 ease-out group-hover:-translate-y-1 group-hover:scale-[1.02] group-hover:border-white/14 group-hover:shadow-[0_24px_80px_rgba(8,47,73,0.5)] group-focus-within:-translate-y-1 group-focus-within:scale-[1.02] group-focus-within:border-sky-200/40', frameClassName)}>
|
||||
<div className={cx('artwork-card__frame relative overflow-hidden rounded-[1.6rem] border border-white/8 bg-slate-950/80 shadow-[0_18px_60px_rgba(2,6,23,0.45)] transition duration-300 ease-out group-hover:-translate-y-1 group-hover:scale-[1.02] group-hover:border-white/14 group-hover:shadow-[0_24px_80px_rgba(8,47,73,0.5)] group-focus-within:-translate-y-1 group-focus-within:scale-[1.02] group-focus-within:border-sky-200/40', frameClassName)}>
|
||||
<a
|
||||
href={href}
|
||||
aria-label={`Open artwork: ${cardLabel}`}
|
||||
@@ -842,8 +842,8 @@ export default function ArtworkCard({
|
||||
<span className="sr-only">{cardLabel}</span>
|
||||
</a>
|
||||
|
||||
<div className={cx('relative overflow-hidden bg-slate-900', aspectClass, mediaClassName)} style={mediaStyle}>
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(148,163,184,0.28),_transparent_52%),linear-gradient(180deg,rgba(15,23,42,0.08),rgba(15,23,42,0.42))]" />
|
||||
<div className={cx('artwork-card__media relative overflow-hidden bg-slate-900', aspectClass, mediaClassName)} style={mediaStyle}>
|
||||
<div className="artwork-card__media-glow absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(148,163,184,0.28),_transparent_52%),linear-gradient(180deg,rgba(15,23,42,0.08),rgba(15,23,42,0.42))]" />
|
||||
|
||||
<img
|
||||
src={image}
|
||||
@@ -861,7 +861,7 @@ export default function ArtworkCard({
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-black/85 via-black/38 to-transparent opacity-90 transition duration-300 md:opacity-45 md:group-hover:opacity-100 md:group-focus-within:opacity-100" />
|
||||
<div className="artwork-card__media-shade pointer-events-none absolute inset-0 bg-gradient-to-t from-black/85 via-black/38 to-transparent opacity-90 transition duration-300 md:opacity-45 md:group-hover:opacity-100 md:group-focus-within:opacity-100" />
|
||||
|
||||
{(resolvedMetricBadge?.label || relativePublishedAt) ? (
|
||||
<div className="pointer-events-none absolute inset-x-0 top-0 z-20 flex items-start justify-between gap-3 p-3">
|
||||
@@ -888,7 +888,7 @@ export default function ArtworkCard({
|
||||
|
||||
{showActions && (
|
||||
<div className={cx(
|
||||
'absolute right-3 z-20 flex max-w-[14rem] flex-wrap justify-end translate-y-2 gap-2 opacity-100 transition duration-200 md:opacity-0 md:group-hover:translate-y-0 md:group-hover:opacity-100 md:group-focus-within:translate-y-0 md:group-focus-within:opacity-100',
|
||||
'artwork-card__actions absolute right-3 z-20 flex max-w-[14rem] flex-wrap justify-end translate-y-2 gap-2 opacity-100 transition duration-200 md:opacity-0 md:group-hover:translate-y-0 md:group-hover:opacity-100 md:group-focus-within:translate-y-0 md:group-focus-within:opacity-100',
|
||||
relativePublishedAt ? 'top-12' : 'top-3'
|
||||
)}>
|
||||
<ActionButton label={liked ? 'Unlike artwork' : 'Like artwork'} onClick={handleLike}>
|
||||
@@ -917,7 +917,7 @@ export default function ArtworkCard({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-20 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-3 backdrop-blur-[2px] opacity-100 transition-opacity duration-200 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100">
|
||||
<div className="artwork-card__meta pointer-events-none absolute inset-x-0 bottom-0 z-20 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-3 backdrop-blur-[2px] opacity-100 transition-opacity duration-200 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100">
|
||||
{shouldBlurMature ? <div className="mb-2 inline-flex rounded-full border border-amber-300/20 bg-black/55 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-amber-100">Blurred by your content settings</div> : null}
|
||||
<h3 className={cx('truncate font-semibold text-white', titleClass)}>
|
||||
{title}
|
||||
|
||||
@@ -162,7 +162,7 @@ export default function ArtworkHero({ artwork, presentMd, presentLg, presentXl,
|
||||
event.currentTarget.onerror = null
|
||||
|
||||
if (mainImageMode === 'primary') {
|
||||
setMainImageMode('fallback')
|
||||
setMainImageMode(hasRealArtworkImage ? 'hidden' : 'fallback')
|
||||
setIsLoaded(false)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function GroupDiscoveryCard({ group, className = '', compact = fa
|
||||
<a
|
||||
href={group.urls?.public || '/groups'}
|
||||
className={cx(
|
||||
'group block overflow-hidden rounded-[30px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-5 shadow-[0_24px_70px_rgba(2,6,23,0.34)] transition duration-200 hover:-translate-y-1 hover:border-white/20',
|
||||
'group-discovery-card group block overflow-hidden rounded-[30px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-5 shadow-[0_24px_70px_rgba(2,6,23,0.34)] transition duration-200 hover:-translate-y-1 hover:border-white/20',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function GroupLeaderboardCard({ item }) {
|
||||
const entity = item.entity
|
||||
|
||||
return (
|
||||
<article className="rounded-[26px] border border-white/10 bg-white/[0.03] p-4 shadow-[0_18px_50px_rgba(2,6,23,0.3)]">
|
||||
<article className="group-leaderboard-card rounded-[26px] border border-white/10 bg-white/[0.03] p-4 shadow-[0_18px_50px_rgba(2,6,23,0.3)]">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-white/10 bg-slate-950/70 text-lg font-black text-white">
|
||||
#{item.rank}
|
||||
|
||||
@@ -5,7 +5,7 @@ export default function GroupPromoCard({ group, eyebrow = 'Groups spotlight', ti
|
||||
if (!group) return null
|
||||
|
||||
return (
|
||||
<section className="overflow-hidden rounded-[34px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.18),transparent_28%),radial-gradient(circle_at_80%_20%,rgba(16,185,129,0.12),transparent_26%),linear-gradient(180deg,rgba(7,16,29,0.98),rgba(2,6,23,0.94))] shadow-[0_30px_90px_rgba(2,6,23,0.45)]">
|
||||
<section className="group-promo-card overflow-hidden rounded-[34px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.18),transparent_28%),radial-gradient(circle_at_80%_20%,rgba(16,185,129,0.12),transparent_26%),linear-gradient(180deg,rgba(7,16,29,0.98),rgba(2,6,23,0.94))] shadow-[0_30px_90px_rgba(2,6,23,0.45)]">
|
||||
<div className="grid gap-6 p-6 lg:grid-cols-[minmax(0,1.3fr)_320px] lg:p-8">
|
||||
<div>
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/80">{eyebrow}</p>
|
||||
@@ -30,7 +30,7 @@ export default function GroupPromoCard({ group, eyebrow = 'Groups spotlight', ti
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-[28px] border border-white/10 bg-black/25 p-5 backdrop-blur-sm">
|
||||
<div className="group-promo-card__summary rounded-[28px] border border-white/10 bg-black/25 p-5 backdrop-blur-sm">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex h-16 w-16 shrink-0 items-center justify-center overflow-hidden rounded-2xl border border-white/10 bg-white/[0.04]">
|
||||
{group.avatar_url ? <img src={group.avatar_url} alt="" className="h-full w-full object-cover" loading="lazy" /> : <i className="fa-solid fa-people-group text-slate-300" />}
|
||||
|
||||
@@ -24,9 +24,9 @@ export default function LeaderboardItem({ item, type, highlight = false }) {
|
||||
const groupSignals = Array.isArray(entity.trust_signals) ? entity.trust_signals.slice(0, 2) : []
|
||||
|
||||
return (
|
||||
<article className={cx('rounded-3xl border p-4 shadow-lg transition', tone)}>
|
||||
<article className={cx('leaderboard-item rounded-3xl border p-4 shadow-lg transition', tone)}>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className={cx('flex shrink-0 items-center justify-center rounded-2xl border font-black', highlight ? 'h-14 w-14 text-xl' : 'h-11 w-11 text-base', 'border-white/10 bg-slate-950/70 text-white')}>
|
||||
<div className={cx('leaderboard-item__rank flex shrink-0 items-center justify-center rounded-2xl border font-black', highlight ? 'h-14 w-14 text-xl' : 'h-11 w-11 text-base', 'border-white/10 bg-slate-950/70 text-white')}>
|
||||
#{rank}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ function cx(...parts) {
|
||||
|
||||
export default function LeaderboardTabs({ items, active, onChange, sticky = false, label }) {
|
||||
return (
|
||||
<div className={cx(sticky ? 'sticky top-16 z-20' : '', 'rounded-2xl border border-white/10 bg-slate-950/85 p-2 backdrop-blur') }>
|
||||
<div className={cx(sticky ? 'sticky top-16 z-20' : '', 'leaderboard-tabs rounded-2xl border border-white/10 bg-slate-950/85 p-2 backdrop-blur') }>
|
||||
<div className="flex flex-wrap items-center gap-2" role="tablist" aria-label={label || 'Leaderboard tabs'}>
|
||||
{items.map((item) => {
|
||||
const isActive = item.value === active
|
||||
@@ -19,7 +19,7 @@ export default function LeaderboardTabs({ items, active, onChange, sticky = fals
|
||||
aria-selected={isActive}
|
||||
onClick={() => onChange(item.value)}
|
||||
className={cx(
|
||||
'rounded-full px-4 py-2 text-sm font-semibold transition',
|
||||
'leaderboard-tabs__tab rounded-full px-4 py-2 text-sm font-semibold transition',
|
||||
isActive
|
||||
? 'bg-sky-400 text-slate-950 shadow-[0_12px_30px_rgba(56,189,248,0.28)]'
|
||||
: 'bg-white/5 text-slate-300 hover:bg-white/10 hover:text-white',
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function ProfileHero({ user, profile, isOwner, viewerIsFollowing,
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative mx-auto max-w-7xl px-4 pt-4 md:pt-6">
|
||||
<div className="profile-hero relative mx-auto max-w-7xl px-4 pt-4 md:pt-6">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-x-10 top-8 -z-10 h-44 rounded-full blur-3xl"
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function ProfileTabs({ activeTab, onTabChange }) {
|
||||
}, [activeTab])
|
||||
|
||||
return (
|
||||
<div className="sticky top-0 z-30 border-b border-white/10 bg-[#08111f]/80 backdrop-blur-2xl">
|
||||
<div className="profile-tabs-shell sticky top-0 z-30 border-b border-white/10 bg-[#08111f]/80 backdrop-blur-2xl">
|
||||
<nav
|
||||
ref={navRef}
|
||||
className="profile-tabs-sticky overflow-x-auto scrollbar-hide"
|
||||
|
||||
Reference in New Issue
Block a user