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:
Jakob Wennberg
2026-07-27 16:41:22 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent d47b19fd74
commit 1a7152a7af
8 changed files with 72 additions and 1 deletions
@@ -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
+29
View File
@@ -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 (