feat(settings): skyline masthead on Abonnemang + AI works-with marks on API tab (#1241)
The Abonnemang tab gets a quiet decorative masthead: the marketing site's halftone Stockholm skyline as a wide banner strip on the frame tint, waterline pinned to the strip's bottom edge (same physics as the onboarding backdrop). Shown in every billing state; purely decorative. The API tab's "Anslut MCP-klient" group gets a works-with strip using the site's monochrome halftone Claude and OpenAI marks (copied into public/illustrations and registered in the shared manifest), with a bilingual caption. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
d47b19fd74
commit
1a7152a7af
@@ -6,6 +6,8 @@ export const ILLUSTRATIONS = {
|
||||
'about-stockholm': { w: 2648, h: 1318 },
|
||||
calculator: { w: 745, h: 525 },
|
||||
'key-adding-machine': { w: 1650, h: 1318 },
|
||||
'logo-claude': { w: 524, h: 525 },
|
||||
'logo-openai': { w: 518, h: 525 },
|
||||
notebook: { w: 636, h: 525 },
|
||||
pencil: { w: 542, h: 525 },
|
||||
} as const
|
||||
|
||||
@@ -30,6 +30,7 @@ import { Loader2, Plus, Copy, Check, Trash2, Key, ChevronDown, AlertTriangle } f
|
||||
import { cn, formatDateLong } from '@/lib/utils'
|
||||
import { copyToClipboard } from '@/lib/browser/copy-to-clipboard'
|
||||
import { getBranding } from '@/lib/branding/service'
|
||||
import { ILLUSTRATIONS, illustrationSrc } from '@/components/onboarding/onboarding-illustrations'
|
||||
import { STAGING_SCOPES } from '@/lib/auth/api-keys'
|
||||
import type { ApiKeyScope } from '@/lib/auth/api-keys'
|
||||
|
||||
@@ -474,6 +475,34 @@ export function ApiKeysPanel() {
|
||||
</SettingsGroup>
|
||||
|
||||
<SettingsGroup label={t('connect_mcp_title')}>
|
||||
{/* The marketing site's halftone AI marks (Claude, OpenAI): a quiet
|
||||
"works with" cue, not chrome. Text carries the meaning; the marks
|
||||
are decorative. */}
|
||||
<div className="flex items-center gap-3 px-1 pb-1 pt-3">
|
||||
<div aria-hidden className="flex shrink-0 items-center gap-2">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={illustrationSrc('logo-claude')}
|
||||
width={ILLUSTRATIONS['logo-claude'].w}
|
||||
height={ILLUSTRATIONS['logo-claude'].h}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
className="h-5 w-auto"
|
||||
/>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={illustrationSrc('logo-openai')}
|
||||
width={ILLUSTRATIONS['logo-openai'].w}
|
||||
height={ILLUSTRATIONS['logo-openai'].h}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
className="h-5 w-auto"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">{t('works_with_ai')}</p>
|
||||
</div>
|
||||
<SettingsRow
|
||||
label="Claude.ai"
|
||||
align="baseline"
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from '@/components/settings/SettingsRows'
|
||||
import { formatDateLong } from '@/lib/utils'
|
||||
import { BillingActions } from '@/components/settings/BillingActions'
|
||||
import { ILLUSTRATIONS, illustrationSrc } from '@/components/onboarding/onboarding-illustrations'
|
||||
|
||||
// What the paid tier unlocks (mirrors lib/entitlements PAID_CAPABILITIES).
|
||||
const INCLUDED = [
|
||||
@@ -49,6 +50,36 @@ interface BillingView {
|
||||
isDemo: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorative masthead: the marketing site's halftone Stockholm skyline as a
|
||||
* quiet wide banner, echoing the onboarding backdrop so the paid surface
|
||||
* feels like the same product. Purely decorative (aria-hidden, empty alt),
|
||||
* no text, one gesture.
|
||||
*/
|
||||
function BillingHero() {
|
||||
const art = ILLUSTRATIONS['about-stockholm']
|
||||
return (
|
||||
<div
|
||||
aria-hidden
|
||||
className="relative mt-6 h-40 overflow-hidden rounded-lg border border-border bg-frame"
|
||||
>
|
||||
{/* Anchored so the skyline's waterline sits on the strip's bottom edge
|
||||
(the water reflection falls below it), spires reaching up into the
|
||||
strip: same physics as the onboarding backdrop. */}
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={illustrationSrc('about-stockholm')}
|
||||
width={art.w}
|
||||
height={art.h}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
className="absolute bottom-0 left-1/2 w-full min-w-[640px] -translate-x-1/2 translate-y-[51%] opacity-70 dark:opacity-40 dark:invert"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FeatureList({ heading, items }: { heading: string; items: string[] }) {
|
||||
return (
|
||||
<div>
|
||||
@@ -149,7 +180,12 @@ export function BillingSettingsContent() {
|
||||
return () => { active = false }
|
||||
}, [reloadKey, errorLocale])
|
||||
|
||||
const header = <SettingsSectionHeader title={tNav('billing')} intro={tIntro('billing')} />
|
||||
const header = (
|
||||
<>
|
||||
<SettingsSectionHeader title={tNav('billing')} intro={tIntro('billing')} />
|
||||
<BillingHero />
|
||||
</>
|
||||
)
|
||||
|
||||
if (!view) {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user