Files
dezky/apps/website/pages/brand.vue
T
Ronni Baslund d668b1b6a6 feat(website): responsive / mobile layouts
Make the marketing site mobile-friendly across every page and section.
Desktop appearance is unchanged; all breakpoint logic targets <=768px.

- Fluid section padding via clamp(); equal grids use auto-fit/minmax,
  asymmetric grids stack to one column via scoped-CSS media queries
- Nav: real hamburger menu on mobile (links, lang toggle, login, CTA)
- ProductMockup: scales the whole dashboard to fit (zoom) instead of
  reflowing its internals into a tall stack
- Lower oversized heading clamp() minimums so titles no longer overflow
  at ~390px (hero, page headers, final CTA, brand cover/chapter)
- HowItWorks: row-gap when steps stack so node markers clear the text
- Compare + partners tables: stacked rows now label each value with its
  column (Dezky vs hyperscaler / CSP) instead of an ambiguous header
- Footer columns, tiers, calculator and tables stack cleanly on mobile
2026-06-06 15:55:35 +02:00

555 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
// Dezky Brand Guide — ported from the Claude Design handoff (guide.jsx).
// Long-scroll document: Cover → Logo → Color → Typography → Voice →
// Applications. English-only by design; rendered inside the site layout (Nav +
// Footer). Illustrative copy adapted to Dezky's real EU-sovereign positioning.
import { C } from '~/utils/landingTokens'
definePageMeta({ layout: 'page' })
useHead({ title: 'dezky · brand guide v1.0' })
const mono = '"JetBrains Mono", ui-monospace, monospace'
const tight = '"Inter Tight", "Inter", sans-serif'
const inter = '"Inter", sans-serif'
function pStyle(max = 640, dim = false) {
return { fontFamily: inter, fontSize: '16px', lineHeight: 1.6, maxWidth: `${max}px`, color: dim ? 'rgba(0,0,0,0.65)' : 'currentColor', margin: '0 0 16px', textWrap: 'pretty' as const }
}
function h3Style(mt = 64) {
return { fontFamily: tight, fontWeight: 600, fontSize: '24px', letterSpacing: '-0.02em', lineHeight: 1.15, margin: `${mt}px 0 16px` }
}
function frame(bg: string, height?: number, fg?: string) {
return { background: bg, color: fg, height: height ? `${height}px` : '100%', borderRadius: '4px', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative' as const, overflow: 'hidden' }
}
const eyebrow = { fontFamily: mono, fontSize: '11px', letterSpacing: '0.18em', textTransform: 'uppercase' as const, fontWeight: 500, opacity: 0.7 }
const h1 = { fontFamily: tight, fontWeight: 600, fontSize: 'clamp(32px, 6vw, 72px)', letterSpacing: '-0.035em', lineHeight: 0.95, margin: '24px 0 0', textWrap: 'balance' as const }
const h2 = { fontFamily: tight, fontWeight: 600, fontSize: 'clamp(36px, 4.5vw, 52px)', letterSpacing: '-0.03em', lineHeight: 1, margin: '20px 0 56px', textWrap: 'balance' as const }
const caption = { fontFamily: mono, fontSize: '10px', letterSpacing: '0.12em', textTransform: 'uppercase' as const, color: 'rgba(0,0,0,0.5)', marginTop: '10px' }
const page = { padding: 'clamp(56px, 8vw, 96px) clamp(20px, 5vw, 80px)', borderBottom: `1px solid ${C.fog}` }
const bigSwatches = [
{ name: 'Carbon', hex: C.carbon, rgb: '10 10 10', role: 'Foreground · containers · type', dark: true },
{ name: 'Signal', hex: C.signal, rgb: '212 255 58', role: 'Brand accent · CTAs · the node-dot', dark: false },
]
const surfaces = [
{ name: 'Bone', hex: C.bone, role: 'Primary surface' },
{ name: 'Paper', hex: C.paper, role: 'Elevated surface' },
{ name: 'Fog', hex: C.fog, role: 'Dividers · inputs' },
{ name: 'Slate', hex: C.slate, role: 'Secondary type' },
]
const semantic = [
{ name: 'Success', hex: C.ok, role: 'Positive states' },
{ name: 'Warning', hex: C.warn, role: 'Caution' },
{ name: 'Error', hex: C.bad, role: 'Errors · destructive' },
]
const specRows: [string, string][] = [
['Squircle', '84 × 84 unit · radius 22'],
['Bowl', 'full circle · r 14'],
['Stem', '7 wide · rounded ascender cap'],
['Counter', 'r 6 · centered in bowl'],
['Node-dot', 'r 4 · upper-right corner'],
['Clear space', '= squircle corner radius'],
]
const typeScale = [
{ token: 'display-xl', px: 96, weight: 600, label: 'Page hero · landings only' },
{ token: 'display', px: 64, weight: 600, label: 'Section heroes' },
{ token: 'h1', px: 40, weight: 600, label: 'Page titles' },
{ token: 'h2', px: 28, weight: 600, label: 'Section titles' },
{ token: 'h3', px: 20, weight: 600, label: 'Subsections' },
{ token: 'body-lg', px: 18, weight: 400, label: 'Intro paragraphs' },
{ token: 'body', px: 16, weight: 400, label: 'Default body' },
{ token: 'caption', px: 13, weight: 500, label: 'Metadata · labels' },
]
const soundLike = [
'Your data stays in the EU.',
'Mail, files, video, chat — one login.',
'No lock-in. Export everything, anytime.',
'Built in Denmark, under European law.',
'Switch from Microsoft 365 with zero downtime.',
]
const dontSound = [
'Revolutionize your workflow!',
'AI-powered, blazingly-fast platform.',
'We empower teams to unlock potential.',
'Synergy. Holistic. Solutions.',
'Let\'s make magic together ✨',
]
const toneRows: [string, string, string][] = [
['Marketing site', 'Confident, direct', 'Your digital workplace. Data that stays in the EU.'],
['Product UI', 'Plain, instructional', 'Add a user to get started.'],
['Errors', 'Honest, useful', 'Couldn\'t reach the server. We\'re retrying.'],
['Empty states', 'Helpful, slightly dry', 'No files yet. Drag one in or invite your team.'],
['Docs', 'Direct, practical', 'Point your domain\'s MX record at Dezky.'],
]
type DoDont = { ok: boolean, label: string, bg: string, fg: string, accent: string, variant?: 'donut' | 'solid' | 'outline', transform?: string }
const doDont: DoDont[] = [
{ ok: true, label: 'On carbon', bg: C.carbon, fg: C.carbon, accent: C.signal },
{ ok: true, label: 'On signal', bg: C.signal, fg: C.signal, accent: C.carbon },
{ ok: true, label: 'On bone', bg: C.bone, fg: C.carbon, accent: C.signal },
{ ok: true, label: 'Monochrome', bg: C.bone, fg: C.carbon, accent: C.bone },
{ ok: false, label: 'No gradients', bg: 'linear-gradient(135deg,#ff4dcb,#7a5aff)', fg: C.carbon, accent: C.signal },
{ ok: false, label: 'No rotation', bg: C.bone, fg: C.carbon, accent: C.signal, transform: 'rotate(-14deg)' },
{ ok: false, label: 'No stretching', bg: C.bone, fg: C.carbon, accent: C.signal, transform: 'scaleX(1.4)' },
{ ok: false, label: 'No off-brand color', bg: C.bone, fg: C.carbon, accent: '#ff4dcb' },
]
</script>
<template>
<div :style="{ background: C.paper, color: C.carbon }">
<!-- 00 · COVER -->
<section :style="{ background: C.carbon, color: C.bone, padding: 'clamp(56px, 8vw, 80px) clamp(20px, 5vw, 80px)', minHeight: '86vh', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', position: 'relative', overflow: 'hidden' }">
<div :style="{ position: 'absolute', right: '-160px', bottom: '-160px', opacity: 0.06 }">
<BrandNodeMark :size="720" :fg="C.carbon" :accent="C.signal" />
</div>
<div :style="{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', position: 'relative', flexWrap: 'wrap', gap: '16px' }">
<div :style="{ display: 'flex', alignItems: 'center', gap: '14px' }">
<div :style="{ width: '56px', height: '56px', background: C.bone, borderRadius: '14px', display: 'flex', alignItems: 'center', justifyContent: 'center' }">
<BrandNodeMark :size="44" :fg="C.carbon" :accent="C.signal" />
</div>
<div :style="{ fontFamily: mono, fontWeight: 600, fontSize: '18px', letterSpacing: '-0.02em', color: C.bone }">dezky</div>
</div>
<div :style="{ textAlign: 'right' }">
<div :style="eyebrow">Brand Guide · v1.0</div>
<div :style="{ fontFamily: mono, fontSize: '11px', marginTop: '8px', color: 'rgba(255,255,255,0.5)' }">June 2026</div>
</div>
</div>
<div :style="{ position: 'relative' }">
<div :style="eyebrow">The dezky brand</div>
<h1 :style="{ fontFamily: tight, fontWeight: 600, fontSize: 'clamp(34px, 11vw, 144px)', letterSpacing: '-0.045em', lineHeight: 0.9, margin: '32px 0 0', maxWidth: '90%' }">
Quiet software,<br>
<span :style="{ color: C.signal }">sovereign data.</span>
</h1>
</div>
<div :style="{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', color: 'rgba(255,255,255,0.55)', fontFamily: mono, fontSize: '11px', position: 'relative', flexWrap: 'wrap', gap: '8px' }">
<div>dezky · brand system</div>
<div>00 / 06</div>
</div>
</section>
<!-- 01 · LOGO -->
<section :style="{ ...page, background: C.paper }">
<div :style="eyebrow">01 · Logo</div>
<h1 :style="h1">The mark</h1>
<p :style="pStyle(560, true)">A lowercase <i>d</i> built as a unified letterform full bowl, rounded ascender, contained in a squircle. The node-dot in the upper-right is the one moving piece of the brand: a signal, a presence.</p>
<div class="logo-main-grid" :style="{ display: 'grid', gap: '24px', marginTop: '64px' }">
<div :style="frame(C.carbon, 520)"><BrandNodeMark :size="360" :fg="C.carbon" :accent="C.signal" /></div>
<div :style="{ display: 'grid', gridTemplateRows: '1fr 1fr', gap: '24px', height: '520px' }">
<div :style="frame(C.signal)"><BrandNodeMark :size="180" :fg="C.signal" :accent="C.carbon" /></div>
<div :style="frame(C.bone)"><BrandNodeMark :size="180" :fg="C.carbon" :accent="C.signal" /></div>
</div>
</div>
<div class="logo-caption-row" :style="{ display: 'grid', gap: '24px', marginTop: '8px' }">
<div :style="caption">Primary · on carbon</div>
<div :style="caption">Reversed · on signal</div>
<div :style="caption">Light · on bone</div>
</div>
</section>
<!-- 01.1 Anatomy -->
<section :style="{ ...page, background: C.bone }">
<div :style="eyebrow">01.1 · Anatomy</div>
<h2 :style="h2">Constructed, not drawn.</h2>
<div class="anatomy-grid" :style="{ display: 'grid', gap: '64px', alignItems: 'center' }">
<div :style="frame(C.paper, 520)">
<svg viewBox="0 0 360 360" width="380" height="380" style="max-width:100%;height:auto;">
<defs>
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="rgba(0,0,0,0.05)" stroke-width="0.5" />
</pattern>
</defs>
<rect width="360" height="360" fill="url(#grid)" />
<g transform="translate(40, 40) scale(2.8)">
<rect x="8" y="8" width="84" height="84" rx="22" :fill="C.carbon" />
<g :fill="C.signal">
<path d="M 34.425 52 a 14 14 0 1 0 28 0 a 14 14 0 1 0 -28 0 Z M 41.925 52 a 6.5 6.5 0 1 0 13 0 a 6.5 6.5 0 1 0 -13 0 Z" fill-rule="evenodd" />
<path d="M 58.575 29.5 a 3.5 3.5 0 0 1 7 0 L 65.575 66 L 58.575 66 Z" />
<circle cx="74" cy="26" r="4" />
</g>
</g>
<g font-family="'JetBrains Mono', monospace" font-size="9" fill="rgba(0,0,0,0.6)">
<line x1="40" y1="240" x2="320" y2="240" stroke="rgba(0,0,0,0.25)" stroke-dasharray="3,3" />
<text x="324" y="244">baseline</text>
<line x1="40" y1="162" x2="320" y2="162" stroke="rgba(0,0,0,0.25)" stroke-dasharray="3,3" />
<text x="324" y="166">x-height</text>
<line x1="40" y1="106" x2="320" y2="106" stroke="rgba(0,0,0,0.25)" stroke-dasharray="3,3" />
<text x="324" y="110">ascender</text>
<text x="100" y="290" :fill="C.carbon" font-weight="500">r 14 · bowl</text>
<text x="100" y="304" fill="rgba(0,0,0,0.5)">7w · stem</text>
</g>
</svg>
</div>
<div>
<h3 :style="h3Style(0)">Geometry</h3>
<div :style="{ overflowX: 'auto' }"><table :style="{ width: '100%', borderCollapse: 'collapse', fontFamily: inter, fontSize: '14px' }">
<tbody>
<tr v-for="([k, v], i) in specRows" :key="i" :style="{ borderBottom: `1px solid ${C.fog}` }">
<td :style="{ padding: '12px 0', color: 'rgba(0,0,0,0.55)', width: '40%' }">{{ k }}</td>
<td :style="{ padding: '12px 0', fontFamily: mono, fontSize: '13px' }">{{ v }}</td>
</tr>
</tbody>
</table></div>
<p :style="pStyle(520, true)">Every measurement is a multiple of the stem weight (7u). Don't redraw the mark — use the master SVG.</p>
</div>
</div>
</section>
<!-- 01.2 Clear space / min size -->
<section :style="{ ...page, background: C.paper }">
<div :style="eyebrow">01.2 · Clear space · Minimum size</div>
<h2 :style="h2">Give it room.</h2>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '32px' }">
<div>
<div :style="frame(C.fog, 360)">
<div :style="{ position: 'relative' }">
<div :style="{ position: 'absolute', inset: '-44px', border: '1px dashed rgba(0,0,0,0.3)', borderRadius: '12px' }" />
<BrandNodeMark :size="140" :fg="C.carbon" :accent="C.signal" />
</div>
</div>
<div :style="caption">Clear space</div>
<p :style="pStyle(520, true)">Maintain clear space equal to <b>half the squircle dimension</b> on every side. No type, edges, or other marks intrude.</p>
</div>
<div>
<div :style="frame(C.fog, 360)">
<div :style="{ display: 'flex', alignItems: 'flex-end', gap: '48px', flexWrap: 'wrap', justifyContent: 'center' }">
<div v-for="s in [16, 24, 48, 96]" :key="s" :style="{ textAlign: 'center' }">
<BrandNodeMark :size="s" :fg="C.carbon" :accent="C.signal" />
<div :style="{ fontFamily: mono, fontSize: '10px', color: 'rgba(0,0,0,0.5)', marginTop: '12px' }">{{ s }}px</div>
</div>
</div>
</div>
<div :style="caption">Minimum sizes</div>
<p :style="pStyle(520, true)"><b>24 px</b> minimum for digital · <b>16 px</b> only as a favicon (drop the node-dot if it disappears). <b>12 mm</b> minimum for print.</p>
</div>
</div>
</section>
<!-- 01.3 Wordmark / lockup -->
<section :style="{ ...page, background: C.bone }">
<div :style="eyebrow">01.3 · Wordmark · Lockup</div>
<h2 :style="h2">Letters set in JetBrains Mono.</h2>
<div :style="frame(C.paper, 300)">
<div :style="{ fontFamily: mono, fontWeight: 600, fontSize: 'clamp(48px, 15vw, 140px)', letterSpacing: '-0.04em', color: C.carbon, lineHeight: 1 }">dezky</div>
</div>
<div :style="caption">Wordmark · 100% scale</div>
<h3 :style="h3Style()">Horizontal lockup</h3>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '24px' }">
<div :style="frame(C.paper, 220)"><BrandNodeLockup :scale="1.4" :fg="C.carbon" :accent="C.signal" /></div>
<div :style="frame(C.carbon, 220)"><BrandNodeLockup :scale="1.4" :fg="C.carbon" :accent="C.signal" :wordmark="C.bone" /></div>
</div>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '24px', marginTop: '8px' }">
<div :style="caption">Light surface</div>
<div :style="caption">Dark surface</div>
</div>
<p :style="pStyle(520, true)">Gap between mark and wordmark is fixed at <b>0.25× the mark height</b>. Wordmark cap-height aligns with the squircle height — never larger.</p>
</section>
<!-- 01.4 Do / Don't -->
<section :style="{ ...page, background: C.paper }">
<div :style="eyebrow">01.4 · Do · Don't</div>
<h2 :style="h2">How not to use it.</h2>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 200px), 1fr))', gap: '20px' }">
<div v-for="(row, i) in doDont" :key="i">
<div :style="{ background: row.bg, borderRadius: '4px', height: '180px', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative', overflow: 'hidden' }">
<div :style="{ transform: row.transform || 'none' }">
<BrandNodeMark :size="96" :fg="row.fg" :accent="row.accent" :variant="row.variant || 'donut'" />
</div>
<svg v-if="!row.ok" viewBox="0 0 100 100" preserveAspectRatio="none" :style="{ position: 'absolute', inset: 0, width: '100%', height: '100%' }">
<line x1="0" y1="100" x2="100" y2="0" :stroke="C.bad" stroke-width="1.5" />
</svg>
</div>
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '10px' }">
<div :style="{ width: '16px', height: '16px', borderRadius: '999px', background: row.ok ? C.ok : C.bad, color: '#fff', fontSize: '10px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700 }">{{ row.ok ? '' : '' }}</div>
<div :style="{ fontSize: '12px', fontWeight: 500 }">{{ row.label }}</div>
</div>
</div>
</div>
</section>
<!-- 02 · COLOR -->
<section :style="{ ...page, background: C.paper }">
<div :style="eyebrow">02 · Color</div>
<h1 :style="h1">Two colors do the work.</h1>
<p :style="pStyle(560, true)">Carbon and Signal carry the brand. Everything else is supporting cast. Signal is loud — reserve it for the mark, the node-dot, and primary calls to action. Never tint, never gradient.</p>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '24px', marginTop: '80px' }">
<div v-for="c in bigSwatches" :key="c.name">
<div :style="{ background: c.hex, height: '280px', borderRadius: '4px', display: 'flex', alignItems: 'flex-end', padding: '24px', color: c.dark ? C.bone : C.carbon, boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.05)' }">
<div :style="{ fontFamily: tight, fontWeight: 600, fontSize: '36px', letterSpacing: '-0.02em' }">{{ c.name }}</div>
</div>
<div :style="{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', marginTop: '16px', fontFamily: mono, fontSize: '11px', color: 'rgba(0,0,0,0.65)' }">
<div><div :style="{ color: 'rgba(0,0,0,0.4)' }">HEX</div>{{ c.hex }}</div>
<div><div :style="{ color: 'rgba(0,0,0,0.4)' }">RGB</div>{{ c.rgb }}</div>
<div><div :style="{ color: 'rgba(0,0,0,0.4)' }">ROLE</div>{{ c.role }}</div>
</div>
</div>
</div>
<h3 :style="h3Style()">Surfaces & type</h3>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 200px), 1fr))', gap: '16px' }">
<div v-for="c in surfaces" :key="c.name">
<div :style="{ background: c.hex, height: '140px', borderRadius: '4px', boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.05)' }" />
<div :style="{ marginTop: '12px' }">
<div :style="{ fontFamily: inter, fontWeight: 600, fontSize: '14px' }">{{ c.name }}</div>
<div :style="{ fontFamily: mono, fontSize: '11px', color: 'rgba(0,0,0,0.5)' }">{{ c.hex }}</div>
<div :style="{ fontFamily: inter, fontSize: '12px', color: 'rgba(0,0,0,0.55)', marginTop: '4px', lineHeight: 1.45 }">{{ c.role }}</div>
</div>
</div>
</div>
<h3 :style="h3Style()">Semantic</h3>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 200px), 1fr))', gap: '16px' }">
<div v-for="c in semantic" :key="c.name">
<div :style="{ background: c.hex, height: '140px', borderRadius: '4px', boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.05)' }" />
<div :style="{ marginTop: '12px' }">
<div :style="{ fontFamily: inter, fontWeight: 600, fontSize: '14px' }">{{ c.name }}</div>
<div :style="{ fontFamily: mono, fontSize: '11px', color: 'rgba(0,0,0,0.5)' }">{{ c.hex }}</div>
<div :style="{ fontFamily: inter, fontSize: '12px', color: 'rgba(0,0,0,0.55)', marginTop: '4px', lineHeight: 1.45 }">{{ c.role }}</div>
</div>
</div>
</div>
</section>
<!-- 02.1 Color in use -->
<section :style="{ ...page, background: C.bone }">
<div :style="eyebrow">02.1 · Color in use</div>
<h2 :style="h2">The 70 · 20 · 10 rule.</h2>
<p :style="pStyle(560, true)"><b>70%</b> Bone / Paper · <b>20%</b> Carbon · <b>10%</b> Signal. Surface stays calm; the brand interrupts only at moments of consequence.</p>
<div class="color-ratio-bar" :style="{ display: 'grid', gap: '4px', height: '80px', marginTop: '32px' }">
<div :style="{ background: C.paper, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: mono, fontSize: '11px', color: 'rgba(0,0,0,0.5)' }">70 · paper / bone</div>
<div :style="{ background: C.carbon, color: C.bone, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: mono, fontSize: '11px' }">20 · carbon</div>
<div :style="{ background: C.signal, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: mono, fontSize: '11px', color: C.carbon }">10</div>
</div>
</section>
<!-- 03 · TYPOGRAPHY -->
<section :style="{ ...page, background: C.paper }">
<div :style="eyebrow">03 · Typography</div>
<h1 :style="h1">Inter Tight for voice. JetBrains Mono for evidence.</h1>
<p :style="pStyle(620, true)">Inter Tight carries the brand's confident, modern register used for everything from hero copy to subheadings. JetBrains Mono is reserved for the wordmark, labels, code, data, and quantitative details anywhere the brand wants to feel exact.</p>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '48px', marginTop: '80px' }">
<div>
<div :style="{ background: C.fog, borderRadius: '4px', padding: '48px 32px', fontFamily: tight, fontWeight: 500, fontSize: 'clamp(80px, 18vw, 220px)', lineHeight: 0.9, letterSpacing: '-0.04em', color: C.carbon }">Aa</div>
<div :style="{ marginTop: '24px' }">
<div :style="{ fontFamily: tight, fontWeight: 600, fontSize: '24px', letterSpacing: '-0.02em' }">Inter Tight</div>
<div :style="{ fontFamily: mono, fontSize: '11px', color: 'rgba(0,0,0,0.5)', marginTop: '4px' }">Display · UI · prose · 400 · 500 · 600 · 700</div>
<div :style="{ fontFamily: tight, fontSize: '15px', marginTop: '16px', color: 'rgba(0,0,0,0.75)', lineHeight: 1.5 }">The quick brown fox jumps over the lazy dog. 0123456789</div>
</div>
</div>
<div>
<div :style="{ background: C.fog, borderRadius: '4px', padding: '48px 32px', fontFamily: mono, fontWeight: 500, fontSize: 'clamp(80px, 18vw, 220px)', lineHeight: 0.9, letterSpacing: '-0.04em', color: C.carbon }">Aa</div>
<div :style="{ marginTop: '24px' }">
<div :style="{ fontFamily: tight, fontWeight: 600, fontSize: '24px', letterSpacing: '-0.02em' }">JetBrains Mono</div>
<div :style="{ fontFamily: mono, fontSize: '11px', color: 'rgba(0,0,0,0.5)', marginTop: '4px' }">Wordmark · code · labels · 400 · 500 · 600</div>
<div :style="{ fontFamily: mono, fontSize: '15px', marginTop: '16px', color: 'rgba(0,0,0,0.75)', lineHeight: 1.5 }">data.stays_in_eu() // 0123456789</div>
</div>
</div>
</div>
</section>
<!-- 03.1 Scale -->
<section :style="{ ...page, background: C.bone }">
<div :style="eyebrow">03.1 · Scale</div>
<h2 :style="h2">One scale, ratio 1.25.</h2>
<div :style="{ display: 'flex', flexDirection: 'column', gap: '18px' }">
<div v-for="r in typeScale" :key="r.token" class="type-scale-row" :style="{ display: 'grid', gap: '24px', alignItems: 'baseline', borderBottom: `1px solid ${C.fog}`, paddingBottom: '14px' }">
<div :style="{ fontFamily: mono, fontSize: '11px', color: 'rgba(0,0,0,0.5)' }">{{ r.token }}</div>
<div :style="{ fontFamily: mono, fontSize: '11px', color: 'rgba(0,0,0,0.5)' }">{{ r.px }} / {{ Math.round(r.px * 1.25) }}</div>
<div :style="{ fontFamily: tight, fontWeight: r.weight, fontSize: `${Math.min(r.px, 40)}px`, letterSpacing: r.px > 40 ? '-0.025em' : '-0.01em', lineHeight: 1 }">dezky</div>
<div :style="{ fontFamily: inter, fontSize: '13px', color: 'rgba(0,0,0,0.55)' }">{{ r.label }}</div>
</div>
</div>
</section>
<!-- 04 · VOICE -->
<section :style="{ ...page, background: C.paper }">
<div :style="eyebrow">04 · Voice</div>
<h1 :style="h1">Direct. Lowercase. Earned.</h1>
<p :style="pStyle(620, true)">dezky doesn't shout — it ships. Sentences are short, verbs do the work, and we talk about sovereignty in plain terms. No exclamation points, no emoji, no AI metaphors, no fear-mongering.</p>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '32px', marginTop: '80px' }">
<div>
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '20px' }">
<div :style="{ width: '18px', height: '18px', borderRadius: '999px', background: C.ok, color: '#fff', fontSize: '11px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700 }">✓</div>
<div :style="{ fontFamily: tight, fontWeight: 600, fontSize: '16px' }">We sound like</div>
</div>
<div :style="{ display: 'flex', flexDirection: 'column', gap: '14px' }">
<div v-for="(s, i) in soundLike" :key="i" :style="{ padding: '14px 18px', background: 'rgba(31,138,91,0.06)', borderLeft: `2px solid ${C.ok}`, fontFamily: tight, fontSize: '16px', color: C.carbon, lineHeight: 1.4 }">{{ s }}</div>
</div>
</div>
<div>
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '20px' }">
<div :style="{ width: '18px', height: '18px', borderRadius: '999px', background: C.bad, color: '#fff', fontSize: '11px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700 }">✕</div>
<div :style="{ fontFamily: tight, fontWeight: 600, fontSize: '16px' }">We don't sound like</div>
</div>
<div :style="{ display: 'flex', flexDirection: 'column', gap: '14px' }">
<div v-for="(s, i) in dontSound" :key="i" :style="{ padding: '14px 18px', background: 'rgba(226,48,48,0.05)', borderLeft: `2px solid ${C.bad}`, fontFamily: tight, fontSize: '16px', color: C.carbon, lineHeight: 1.4 }">{{ s }}</div>
</div>
</div>
</div>
<h3 :style="h3Style()">Tone shifts by surface</h3>
<div :style="{ overflowX: 'auto' }"><table :style="{ width: '100%', borderCollapse: 'collapse', fontFamily: inter }">
<thead>
<tr :style="{ borderBottom: `1px solid ${C.carbon}` }">
<th v-for="hh in ['Surface', 'Tone', 'Example']" :key="hh" :style="{ textAlign: 'left', padding: '12px 0', fontSize: '11px', fontFamily: mono, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'rgba(0,0,0,0.55)' }">{{ hh }}</th>
</tr>
</thead>
<tbody :style="{ fontSize: '14px', color: 'rgba(0,0,0,0.75)' }">
<tr v-for="(row, i) in toneRows" :key="i" :style="{ borderBottom: `1px solid ${C.fog}` }">
<td :style="{ padding: '14px 0', width: '20%' }">{{ row[0] }}</td>
<td :style="{ padding: '14px 0', width: '30%' }">{{ row[1] }}</td>
<td :style="{ padding: '14px 0', fontFamily: inter, fontSize: '14px' }">{{ row[2] }}</td>
</tr>
</tbody>
</table></div>
</section>
<!-- 05 · APPLICATIONS -->
<section :style="{ ...page, background: C.bone }">
<div :style="eyebrow">05 · Applications</div>
<h1 :style="h1">In the world.</h1>
<p :style="pStyle(560, true)">Reference renders across the surfaces dezky lives on. Treat them as the canonical reductions of the system.</p>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '24px', marginTop: '64px' }">
<div :style="frame(C.carbon, 420)">
<div :style="{ width: 'min(100%, 220px)', height: '220px', background: C.carbon, borderRadius: '50px', boxShadow: '0 24px 80px rgba(0,0,0,0.5), inset 0 0 0 1px rgba(255,255,255,0.04)', display: 'flex', alignItems: 'center', justifyContent: 'center' }">
<BrandNodeMark :size="170" :fg="C.carbon" :accent="C.signal" />
</div>
</div>
<div :style="frame(C.paper, 420)">
<div :style="{ display: 'flex', alignItems: 'center', gap: '48px', flexWrap: 'wrap', justifyContent: 'center' }">
<div v-for="s in [64, 32, 16]" :key="s" :style="{ textAlign: 'center' }">
<BrandNodeMark :size="s" :fg="C.carbon" :accent="C.signal" />
<div :style="{ fontFamily: mono, fontSize: '10px', color: 'rgba(0,0,0,0.5)', marginTop: '14px' }">{{ s }} × {{ s }}</div>
</div>
</div>
</div>
</div>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '24px', marginTop: '8px' }">
<div :style="caption">iOS app icon · 1024 master</div>
<div :style="caption">Favicon set</div>
</div>
</section>
<!-- 05.1 Web -->
<section :style="{ ...page, background: C.bone }">
<div :style="eyebrow">05.1 · Web</div>
<h2 :style="h2">Marketing hero.</h2>
<div :style="{ background: C.carbon, borderRadius: '8px', padding: 'clamp(32px, 5vw, 64px) clamp(20px, 4vw, 56px)', color: C.bone, boxShadow: '0 24px 80px rgba(0,0,0,0.2)' }">
<div :style="{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '80px', flexWrap: 'wrap', gap: '16px' }">
<BrandNodeLockup :scale="0.7" :fg="C.carbon" :accent="C.signal" :wordmark="C.bone" />
<div :style="{ display: 'flex', gap: '28px', alignItems: 'center', fontFamily: mono, fontSize: '12px', color: 'rgba(255,255,255,0.65)', flexWrap: 'wrap' }">
<span>product</span><span>security</span><span>pricing</span><span>log in</span>
<span :style="{ background: C.signal, color: C.carbon, padding: '6px 14px', borderRadius: '4px', fontWeight: 600 }">book a demo</span>
</div>
</div>
<div :style="{ fontFamily: mono, fontSize: '12px', color: C.signal, letterSpacing: '0.08em' }">// sovereign productivity · v1.0</div>
<h1 :style="{ fontFamily: tight, fontWeight: 600, fontSize: 'clamp(36px, 8vw, 92px)', letterSpacing: '-0.035em', lineHeight: 0.95, margin: '24px 0', maxWidth: '1000px' }">
Your digital workplace.<br>
<span :style="{ color: C.signal }">Data that stays in the EU.</span>
</h1>
<p :style="{ fontFamily: inter, fontSize: '20px', color: 'rgba(255,255,255,0.7)', maxWidth: '620px', lineHeight: 1.5 }">Mail, files, video, chat and SSO fully integrated, EU-hosted, no lock-in.</p>
</div>
</section>
<!-- 05.3 Social -->
<section :style="{ ...page, background: C.paper }">
<div :style="eyebrow">05.2 · Social</div>
<h2 :style="h2">Avatars & headers.</h2>
<div class="social-grid" :style="{ display: 'grid', gap: '24px', alignItems: 'start' }">
<div>
<div :style="{ width: 'min(100%, 180px)', height: '180px', borderRadius: '50%', background: C.carbon, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.05)' }">
<BrandNodeMark :size="130" :fg="C.carbon" :accent="C.signal" />
</div>
<div :style="caption">Avatar · circular crop</div>
</div>
<div>
<div :style="{ height: '320px', background: C.signal, borderRadius: '8px', position: 'relative', overflow: 'hidden', display: 'flex', alignItems: 'center', padding: '0 64px' }">
<div :style="{ fontFamily: tight, fontWeight: 600, fontSize: 'clamp(36px, 8vw, 88px)', letterSpacing: '-0.035em', lineHeight: 0.95, color: C.carbon, maxWidth: '60%' }">your data<br>stays home.</div>
<div :style="{ position: 'absolute', right: '64px', top: '50%', transform: 'translateY(-50%)' }">
<BrandNodeMark :size="220" :fg="C.signal" :accent="C.carbon" />
</div>
</div>
<div :style="caption">Header · 1500 × 500</div>
</div>
</div>
</section>
<!-- Closing -->
<section :style="{ background: C.carbon, color: C.bone, padding: 'clamp(56px, 8vw, 120px) clamp(20px, 5vw, 80px)', minHeight: '60vh', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }">
<div>
<div :style="eyebrow">End</div>
<h1 :style="{ fontFamily: tight, fontWeight: 600, fontSize: 'clamp(28px, 10vw, 96px)', letterSpacing: '-0.04em', lineHeight: 0.95, margin: '24px 0 0', maxWidth: '900px' }">
Use it well.<br>
<span :style="{ color: C.signal }">Don't redraw it.</span>
</h1>
</div>
<div :style="{ display: 'flex', justifyContent: 'flex-end', alignItems: 'flex-end', fontFamily: mono, fontSize: '11px', color: 'rgba(255,255,255,0.55)', marginTop: '64px' }">
<div>dezky · brand system · v1.0</div>
</div>
</section>
</div>
</template>
<style scoped>
/* 01 Logo — 2fr 1fr main grid */
.logo-main-grid {
grid-template-columns: 2fr 1fr;
}
@media (max-width: 768px) {
.logo-main-grid {
grid-template-columns: 1fr;
}
}
/* 01 Logo — 2fr 1fr 1fr caption row */
.logo-caption-row {
grid-template-columns: 2fr 1fr 1fr;
}
@media (max-width: 768px) {
.logo-caption-row {
grid-template-columns: 1fr;
}
}
/* 01.1 Anatomy — 1.3fr 1fr grid */
.anatomy-grid {
grid-template-columns: 1.3fr 1fr;
}
@media (max-width: 768px) {
.anatomy-grid {
grid-template-columns: 1fr;
}
}
/* 02.1 Color ratio bar — 7fr 2fr 1fr */
.color-ratio-bar {
grid-template-columns: 7fr 2fr 1fr;
}
@media (max-width: 768px) {
.color-ratio-bar {
grid-template-columns: 1fr;
height: auto;
}
.color-ratio-bar > div {
min-height: 48px;
}
}
/* 03.1 Type scale rows — 120px 80px 1fr 1fr */
.type-scale-row {
grid-template-columns: 120px 80px 1fr 1fr;
}
@media (max-width: 768px) {
.type-scale-row {
grid-template-columns: 1fr;
}
}
/* 05.2 Social — 180px 1fr */
.social-grid {
grid-template-columns: 180px 1fr;
}
@media (max-width: 768px) {
.social-grid {
grid-template-columns: 1fr;
}
}
</style>