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
+2
View File
@@ -609,3 +609,5 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
[2026-07-27] POSTHOG_SECRET_API_KEY is server-only (no NEXT_PUBLIC_ prefix) and the HMAC is computed in the dashboard layout; only the derived per-user hash reaches the browser. The key signs identity hashes AND authenticates external API requests, so it is a real credential unlike the phc_ project token, which is public by design and embedded in the client bundle.
[2026-07-27] Support ticket free-text is declared as its own data category (user.content.support) in .compliance/ropa.yaml and named on the privacy page, separate from analytics. Analytics events deliberately carry no message body (the breadcrumb sends only the subject), but a ticket carries what the user wrote, because that is the point of writing to support. Keeping the two purposes distinct is what stops the privacy page drifting out of date the way the Recapt row did.
[2026-07-27] Removed the granular "BFNAR 2013:2 punkt 9.x" citations from public/docs/systemdokumentation-mall.md instead of renumbering them to kap 8: the swedish-accounting-compliance skill and our own route comments both place systemdokumentation in kap 8, the template asserted kapitel 9 with a full point-by-point mapping (9.1, 9.2, 9.3-9.5, 9.6-9.8, 9.9, 9.16), and neither could be verified against BFN's actual text from here. A downloadable statutory template is the wrong place to guess a legal citation, and a wrong precise cite is worse than a correct general one, so the doc now cites BFL 5 kap. 11 § and BFNAR 2013:2 without pinning points. Restore the points once someone confirms the chapter against the standard.
[2026-07-27] Abonnemang masthead uses the halftone Stockholm skyline, not key-adding-machine and no new asset: the wide engraving crops cleanly into a banner strip (waterline pinned to the bottom edge, same physics as the onboarding backdrop) while the machine vignette read noisy and lopsided at banner crop; screenshots of both compared before choosing.
[2026-07-27] The requested Claude/OpenAI logos went into the API tab's "Anslut MCP-klient" group as a quiet works-with strip, using the marketing site's monochrome halftone marks instead of the raw colored brand tiles: full-color trademark tiles would fight the editorial monochrome chrome, and the halftone versions already exist as shared brand assets.
@@ -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 (
+1
View File
@@ -2139,6 +2139,7 @@
"copy_aria": "Copy",
"copy_failed": "Could not copy. Select the text above and copy it manually.",
"connect_mcp_title": "Connect MCP client",
"works_with_ai": "Works with Claude, ChatGPT, and other AI assistants.",
"recommended_badge": "Recommended",
"claude_ai_instructions": "Go to <path>Settings → Connectors → Add custom connector</path> and paste the MCP server URL. You sign in via your {connectorName} account: no API key needed.",
"claude_code_cursor": "Claude Code / Cursor",
+1
View File
@@ -2139,6 +2139,7 @@
"copy_aria": "Kopiera",
"copy_failed": "Kunde inte kopiera. Markera texten ovan och kopiera manuellt.",
"connect_mcp_title": "Anslut MCP-klient",
"works_with_ai": "Fungerar med Claude, ChatGPT och andra AI-assistenter.",
"recommended_badge": "Rekommenderat",
"claude_ai_instructions": "Gå till <path>Settings → Connectors → Add custom connector</path> och klistra in MCP-serverns URL. Du loggas in via ditt {connectorName}-konto: ingen API-nyckel behövs.",
"claude_code_cursor": "Claude Code / Cursor",
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB