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
This commit is contained in:
Ronni Baslund
2026-06-06 15:55:35 +02:00
parent bc0697c3e8
commit d668b1b6a6
28 changed files with 721 additions and 142 deletions
+13 -2
View File
@@ -1,3 +1,14 @@
<style scoped>
.about-body-grid {
grid-template-columns: 1.2fr 1fr;
}
@media (max-width: 768px) {
.about-body-grid {
grid-template-columns: 1fr;
}
}
</style>
<script setup lang="ts">
import { useTheme, useCopy } from '~/composables/useLanding'
@@ -13,8 +24,8 @@ useHead({ title: () => `${copy.value.pages.about.label} · dezky` })
<template>
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
<LandingContainer pad="64px 64px 160px">
<div :style="{ display: 'grid', gridTemplateColumns: '1.2fr 1fr', gap: '80px', alignItems: 'start' }">
<LandingContainer pad="clamp(56px, 8vw, 64px) clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
<div class="about-body-grid" :style="{ display: 'grid', gap: '80px', alignItems: 'start' }">
<div :style="{ display: 'flex', flexDirection: 'column', gap: '24px' }">
<p
v-for="(para, i) in c.body" :key="i"
+108 -42
View File
@@ -24,10 +24,10 @@ function frame(bg: string, height?: number, fg?: string) {
}
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(48px, 6vw, 72px)', letterSpacing: '-0.035em', lineHeight: 0.95, margin: '24px 0 0', textWrap: 'balance' as const }
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: '96px 80px', borderBottom: `1px solid ${C.fog}` }
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 },
@@ -99,11 +99,11 @@ const doDont: DoDont[] = [
<template>
<div :style="{ background: C.paper, color: C.carbon }">
<!-- 00 · COVER -->
<section :style="{ background: C.carbon, color: C.bone, padding: '80px 80px', minHeight: '86vh', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', position: 'relative', overflow: 'hidden' }">
<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' }">
<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" />
@@ -117,12 +117,12 @@ const doDont: DoDont[] = [
</div>
<div :style="{ position: 'relative' }">
<div :style="eyebrow">The dezky brand</div>
<h1 :style="{ fontFamily: tight, fontWeight: 600, fontSize: 'clamp(64px, 11vw, 144px)', letterSpacing: '-0.045em', lineHeight: 0.9, margin: '32px 0 0', maxWidth: '90%' }">
<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' }">
<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>
@@ -133,14 +133,14 @@ const doDont: DoDont[] = [
<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 :style="{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: '24px', marginTop: '64px' }">
<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 :style="{ display: 'grid', gridTemplateColumns: '2fr 1fr 1fr', gap: '24px', marginTop: '8px' }">
<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>
@@ -151,9 +151,9 @@ const doDont: DoDont[] = [
<section :style="{ ...page, background: C.bone }">
<div :style="eyebrow">01.1 · Anatomy</div>
<h2 :style="h2">Constructed, not drawn.</h2>
<div :style="{ display: 'grid', gridTemplateColumns: '1.3fr 1fr', gap: '64px', alignItems: 'center' }">
<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">
<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" />
@@ -182,14 +182,14 @@ const doDont: DoDont[] = [
</div>
<div>
<h3 :style="h3Style(0)">Geometry</h3>
<table :style="{ width: '100%', borderCollapse: 'collapse', fontFamily: inter, fontSize: '14px' }">
<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>
</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>
@@ -199,7 +199,7 @@ const doDont: DoDont[] = [
<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: '1fr 1fr', gap: '32px' }">
<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' }">
@@ -212,7 +212,7 @@ const doDont: DoDont[] = [
</div>
<div>
<div :style="frame(C.fog, 360)">
<div :style="{ display: 'flex', alignItems: 'flex-end', gap: '48px' }">
<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>
@@ -230,15 +230,15 @@ const doDont: DoDont[] = [
<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: '140px', letterSpacing: '-0.04em', color: C.carbon, lineHeight: 1 }">dezky</div>
<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: '1fr 1fr', gap: '24px' }">
<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: '1fr 1fr', gap: '24px', marginTop: '8px' }">
<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>
@@ -249,7 +249,7 @@ const doDont: DoDont[] = [
<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(4, 1fr)', gap: '20px' }">
<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' }">
@@ -272,7 +272,7 @@ const doDont: DoDont[] = [
<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: '1fr 1fr', gap: '24px', marginTop: '80px' }">
<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>
@@ -285,7 +285,7 @@ const doDont: DoDont[] = [
</div>
</div>
<h3 :style="h3Style()">Surfaces & type</h3>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '16px' }">
<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' }">
@@ -296,7 +296,7 @@ const doDont: DoDont[] = [
</div>
</div>
<h3 :style="h3Style()">Semantic</h3>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px' }">
<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' }">
@@ -313,7 +313,7 @@ const doDont: DoDont[] = [
<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 :style="{ display: 'grid', gridTemplateColumns: '7fr 2fr 1fr', gap: '4px', height: '80px', marginTop: '32px' }">
<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>
@@ -325,9 +325,9 @@ const doDont: DoDont[] = [
<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: '1fr 1fr', gap: '48px', marginTop: '80px' }">
<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: '220px', lineHeight: 0.9, letterSpacing: '-0.04em', color: C.carbon }">Aa</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>
@@ -335,7 +335,7 @@ const doDont: DoDont[] = [
</div>
</div>
<div>
<div :style="{ background: C.fog, borderRadius: '4px', padding: '48px 32px', fontFamily: mono, fontWeight: 500, fontSize: '220px', lineHeight: 0.9, letterSpacing: '-0.04em', color: C.carbon }">Aa</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>
@@ -350,7 +350,7 @@ const doDont: DoDont[] = [
<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" :style="{ display: 'grid', gridTemplateColumns: '120px 80px 1fr 1fr', gap: '24px', alignItems: 'baseline', borderBottom: `1px solid ${C.fog}`, paddingBottom: '14px' }">
<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>
@@ -364,7 +364,7 @@ const doDont: DoDont[] = [
<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: '1fr 1fr', gap: '32px', marginTop: '80px' }">
<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>
@@ -385,7 +385,7 @@ const doDont: DoDont[] = [
</div>
</div>
<h3 :style="h3Style()">Tone shifts by surface</h3>
<table :style="{ width: '100%', borderCollapse: 'collapse', fontFamily: inter }">
<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>
@@ -398,7 +398,7 @@ const doDont: DoDont[] = [
<td :style="{ padding: '14px 0', fontFamily: inter, fontSize: '14px' }">{{ row[2] }}</td>
</tr>
</tbody>
</table>
</table></div>
</section>
<!-- 05 · APPLICATIONS -->
@@ -406,14 +406,14 @@ const doDont: DoDont[] = [
<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: '1fr 1fr', gap: '24px', marginTop: '64px' }">
<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: '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' }">
<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' }">
<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>
@@ -421,7 +421,7 @@ const doDont: DoDont[] = [
</div>
</div>
</div>
<div :style="{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '24px', marginTop: '8px' }">
<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>
@@ -431,16 +431,16 @@ const doDont: DoDont[] = [
<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: '64px 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' }">
<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)' }">
<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: '92px', letterSpacing: '-0.035em', lineHeight: 0.95, margin: '24px 0', maxWidth: '1000px' }">
<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>
@@ -452,16 +452,16 @@ const doDont: DoDont[] = [
<section :style="{ ...page, background: C.paper }">
<div :style="eyebrow">05.2 · Social</div>
<h2 :style="h2">Avatars & headers.</h2>
<div :style="{ display: 'grid', gridTemplateColumns: '180px 1fr', gap: '24px', alignItems: 'start' }">
<div class="social-grid" :style="{ display: 'grid', gap: '24px', alignItems: 'start' }">
<div>
<div :style="{ width: '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)' }">
<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: '88px', letterSpacing: '-0.035em', lineHeight: 0.95, color: C.carbon, maxWidth: '60%' }">your data<br>stays home.</div>
<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>
@@ -472,10 +472,10 @@ const doDont: DoDont[] = [
</section>
<!-- Closing -->
<section :style="{ background: C.carbon, color: C.bone, padding: '120px 80px', minHeight: '60vh', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }">
<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: '96px', letterSpacing: '-0.04em', lineHeight: 0.95, margin: '24px 0 0', maxWidth: '900px' }">
<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>
@@ -486,3 +486,69 @@ const doDont: DoDont[] = [
</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>
+15 -2
View File
@@ -13,11 +13,12 @@ useHead({ title: () => `${copy.value.pages.changelog.label} · dezky` })
<template>
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
<LandingContainer pad="56px 64px 160px">
<LandingContainer pad="clamp(56px, 8vw, 56px) clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
<div :style="{ maxWidth: '760px' }">
<div
v-for="(entry, i) in c.entries" :key="i"
:style="{ display: 'grid', gridTemplateColumns: '160px 1fr', gap: '32px', padding: '32px 0', borderTop: `1px solid ${t.border}`, alignItems: 'start' }"
class="changelog-entry"
:style="{ display: 'grid', gap: '32px', padding: '32px 0', borderTop: `1px solid ${t.border}`, alignItems: 'start' }"
>
<div>
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '20px', color: t.fg, letterSpacing: '-0.015em' }">{{ entry[0] }}</div>
@@ -36,3 +37,15 @@ useHead({ title: () => `${copy.value.pages.changelog.label} · dezky` })
</div>
</LandingContainer>
</template>
<style scoped>
.changelog-entry {
grid-template-columns: 160px 1fr;
}
@media (max-width: 768px) {
.changelog-entry {
grid-template-columns: 1fr;
}
}
</style>
+2 -2
View File
@@ -15,8 +15,8 @@ useHead({ title: () => `${copy.value.pages.contact.label} · dezky` })
<template>
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
<LandingContainer pad="56px 64px 160px">
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: '24px', maxWidth: '760px' }">
<LandingContainer pad="clamp(56px, 8vw, 56px) clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 320px), 1fr))', gap: '24px', maxWidth: '760px' }">
<a
:href="`mailto:${c.email}`"
:style="{ display: 'block', padding: '28px', border: `1px solid ${t.border}`, borderRadius: '4px', background: t.surface }"
+14 -2
View File
@@ -19,7 +19,7 @@ useHead({ title: () => `${copy.value.pages.dpa.title} · dezky` })
<template>
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
<LandingContainer pad="48px 64px 160px">
<LandingContainer pad="clamp(32px, 5vw, 48px) clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
<div :style="{ maxWidth: '760px' }">
<!-- Draft / legal-review banner -->
<div :style="{ padding: '18px 22px', borderRadius: '4px', border: `1px solid ${C.warn}55`, background: `${C.warn}14`, fontFamily: '\'Inter\', sans-serif', fontSize: '14px', lineHeight: 1.55, color: t.fg }">
@@ -41,7 +41,8 @@ useHead({ title: () => `${copy.value.pages.dpa.title} · dezky` })
<div :style="{ border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden', margin: '0 0 40px' }">
<div
v-for="(row, i) in c.subprocessors" :key="i"
:style="{ display: 'grid', gridTemplateColumns: '1.2fr 1.5fr 1fr', gap: '16px', padding: '16px 20px', borderTop: i === 0 ? 'none' : `1px solid ${t.border}`, background: t.surface, fontFamily: '\'Inter\', sans-serif', fontSize: '14px', alignItems: 'baseline' }"
class="subprocessor-row"
:style="{ display: 'grid', gap: '16px', padding: '16px 20px', borderTop: i === 0 ? 'none' : `1px solid ${t.border}`, background: t.surface, fontFamily: '\'Inter\', sans-serif', fontSize: '14px', alignItems: 'baseline' }"
>
<span :style="{ color: t.fg, fontWeight: 600 }">{{ row[0] }}</span>
<span :style="{ color: t.fgMuted }">{{ row[1] }}</span>
@@ -67,3 +68,14 @@ useHead({ title: () => `${copy.value.pages.dpa.title} · dezky` })
</div>
</LandingContainer>
</template>
<style scoped>
.subprocessor-row {
grid-template-columns: 1.2fr 1.5fr 1fr;
}
@media (max-width: 768px) {
.subprocessor-row {
grid-template-columns: 1fr;
}
}
</style>
+2 -2
View File
@@ -15,8 +15,8 @@ useHead({ title: () => `${copy.value.pages.migration.label} · dezky` })
<template>
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
<LandingContainer pad="56px 64px 80px">
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '40px' }">
<LandingContainer pad="clamp(56px, 8vw, 56px) clamp(20px, 5vw, 64px) clamp(56px, 8vw, 80px)">
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 240px), 1fr))', gap: '40px' }">
<div v-for="(step, i) in c.steps" :key="i">
<div :style="{ paddingTop: '20px', borderTop: `1px solid ${t.borderStrong}` }">
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', color: t.fgDim, letterSpacing: '0.06em' }">step {{ step[0] }}</div>
+78 -19
View File
@@ -22,9 +22,9 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
<!-- What you get -->
<LandingContainer pad="56px 64px 0">
<LandingContainer pad="clamp(40px, 6vw, 56px) clamp(20px, 5vw, 64px) 0">
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: '20px' }">{{ c.benefitsLabel }}</div>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
<div
v-for="(b, i) in c.benefits" :key="i"
:style="{
@@ -40,8 +40,8 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
</LandingContainer>
<!-- Margin calculator -->
<LandingContainer pad="72px 64px 0">
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '40px', alignItems: 'end', marginBottom: '32px' }">
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
<div class="partners-section-header">
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(30px, 3.6vw, 48px)', letterSpacing: '-0.03em', lineHeight: 1.0, margin: 0, color: t.fg }">{{ c.calc.heading }}</h2>
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.calc.label }}</div>
</div>
@@ -50,35 +50,40 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
</LandingContainer>
<!-- CSP vs Dezky comparison -->
<LandingContainer pad="72px 64px 0">
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '40px', alignItems: 'end', marginBottom: '32px' }">
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
<div class="partners-section-header">
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(30px, 3.6vw, 48px)', letterSpacing: '-0.03em', lineHeight: 1.0, margin: 0, color: t.fg }">{{ c.compare.heading }}</h2>
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.compare.label }}</div>
</div>
<div :style="{ border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
<div :style="{ display: 'grid', gridTemplateColumns: '1.3fr 1fr 1fr', background: t.surface, borderBottom: `1px solid ${t.borderStrong}` }">
<div class="partners-compare-grid partners-compare-header" :style="{ background: t.surface, borderBottom: `1px solid ${t.borderStrong}` }">
<div :style="{ padding: '18px 24px' }" />
<div :style="{ padding: '18px 24px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '13px', color: t.fgMuted }">{{ c.compare.cols[0] }}</div>
<div :style="{ padding: '18px 24px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '13px', fontWeight: 600, color: t.fg, background: `${t.signal}22` }">{{ c.compare.cols[1] }}</div>
</div>
<div
v-for="(row, i) in c.compare.rows" :key="i"
:style="{ display: 'grid', gridTemplateColumns: '1.3fr 1fr 1fr', borderTop: i === 0 ? 'none' : `1px solid ${t.border}`, fontFamily: '\'Inter\', sans-serif', fontSize: '15px' }"
class="partners-compare-grid"
:style="{ borderTop: i === 0 ? 'none' : `1px solid ${t.border}`, fontFamily: '\'Inter\', sans-serif', fontSize: '15px' }"
>
<div :style="{ padding: '18px 24px', color: t.fgMuted, fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', letterSpacing: '0.04em', textTransform: 'uppercase', alignSelf: 'center' }">{{ row[0] }}</div>
<div :style="{ padding: '18px 24px', color: t.fgMuted }">{{ row[1] }}</div>
<div :style="{ padding: '18px 24px', color: t.fg, fontWeight: 600, background: `${t.signal}11` }">{{ row[2] }}</div>
<div class="pc-cat" :style="{ padding: '18px 24px', color: t.fgMuted, fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', letterSpacing: '0.04em', textTransform: 'uppercase', alignSelf: 'center' }">{{ row[0] }}</div>
<div class="pc-val" :style="{ padding: '18px 24px', color: t.fgMuted }">
<span class="pc-key">{{ c.compare.cols[0] }}</span>{{ row[1] }}
</div>
<div class="pc-val" :style="{ padding: '18px 24px', color: t.fg, fontWeight: 600, background: `${t.signal}11` }">
<span class="pc-key">{{ c.compare.cols[1] }}</span>{{ row[2] }}
</div>
</div>
</div>
</LandingContainer>
<!-- Partner tiers -->
<LandingContainer pad="72px 64px 0">
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '40px', alignItems: 'end', marginBottom: '32px' }">
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
<div class="partners-section-header">
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(30px, 3.6vw, 48px)', letterSpacing: '-0.03em', lineHeight: 1.0, margin: 0, color: t.fg }">{{ c.tiers.heading }}</h2>
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.tiers.label }}</div>
</div>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 240px), 1fr))', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
<div
v-for="(tier, i) in c.tiers.items" :key="i"
:style="{ padding: '32px 28px', background: t.surface, borderLeft: i === 0 ? 'none' : `1px solid ${t.border}`, display: 'flex', flexDirection: 'column', gap: '20px' }"
@@ -106,9 +111,9 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
</LandingContainer>
<!-- How to get started -->
<LandingContainer pad="72px 64px 0">
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: '32px' }">{{ c.stepsLabel }}</div>
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '40px' }">
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 220px), 1fr))', gap: '40px' }">
<div v-for="(step, i) in c.steps" :key="i" :style="{ paddingTop: '20px', borderTop: `1px solid ${t.borderStrong}` }">
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', color: t.fgDim, letterSpacing: '0.06em' }">step {{ step[0] }}</div>
<div :style="{ marginTop: '20px', fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '24px', color: t.fg, letterSpacing: '-0.02em' }">{{ step[1] }}</div>
@@ -118,8 +123,8 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
</LandingContainer>
<!-- Partner FAQ -->
<LandingContainer pad="72px 64px 0">
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '40px', alignItems: 'end', marginBottom: '32px' }">
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
<div class="partners-section-header">
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(30px, 3.6vw, 48px)', letterSpacing: '-0.03em', lineHeight: 1.0, margin: 0, color: t.fg }">{{ c.faq.heading }}</h2>
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.faq.label }}</div>
</div>
@@ -141,7 +146,61 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
</LandingContainer>
<!-- CTA -->
<LandingContainer pad="72px 64px 160px">
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
<LandingBtn variant="primary" size="lg" @click="goToSection('#final-cta', route.path)">{{ c.cta }} </LandingBtn>
</LandingContainer>
</template>
<style scoped>
/* Asymmetric 2-col section headers: heading + label side-by-side on desktop, stacked on mobile */
.partners-section-header {
display: grid;
grid-template-columns: 1.1fr 1fr;
gap: 40px;
align-items: end;
margin-bottom: 32px;
}
/* Asymmetric 3-col compare table: label + csp-col + dezky-col */
.partners-compare-grid {
display: grid;
grid-template-columns: 1.3fr 1fr 1fr;
}
/* Per-cell column label only appears once the table stacks on mobile. */
.pc-key {
display: none;
}
@media (max-width: 768px) {
.partners-section-header {
grid-template-columns: 1fr;
}
.partners-compare-grid {
grid-template-columns: 1fr;
}
/* Header row redundant once each value is labelled inline. */
.partners-compare-header {
display: none;
}
.pc-cat {
padding-bottom: 4px !important;
}
.pc-val {
padding-top: 10px !important;
padding-bottom: 12px !important;
}
.pc-key {
display: block;
font-family: 'JetBrains Mono', monospace;
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: rgba(10, 10, 10, 0.5);
margin-bottom: 4px;
font-weight: 500;
}
}
</style>
+16 -2
View File
@@ -19,7 +19,7 @@ useHead({ title: () => `${copy.value.pages.privacy.title} · dezky` })
<template>
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
<LandingContainer pad="48px 64px 160px">
<LandingContainer pad="48px clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
<div :style="{ maxWidth: '760px' }">
<!-- Draft / legal-review banner -->
<div :style="{ padding: '18px 22px', borderRadius: '4px', border: `1px solid ${C.warn}55`, background: `${C.warn}14`, fontFamily: '\'Inter\', sans-serif', fontSize: '14px', lineHeight: 1.55, color: t.fg }">
@@ -41,7 +41,8 @@ useHead({ title: () => `${copy.value.pages.privacy.title} · dezky` })
<div :style="{ border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden', margin: '0 0 40px' }">
<div
v-for="(row, i) in c.recipients" :key="i"
:style="{ display: 'grid', gridTemplateColumns: '1.2fr 1.5fr 1fr', gap: '16px', padding: '16px 20px', borderTop: i === 0 ? 'none' : `1px solid ${t.border}`, background: t.surface, fontFamily: '\'Inter\', sans-serif', fontSize: '14px', alignItems: 'baseline' }"
class="recipients-row"
:style="{ gap: '16px', padding: '16px 20px', borderTop: i === 0 ? 'none' : `1px solid ${t.border}`, background: t.surface, fontFamily: '\'Inter\', sans-serif', fontSize: '14px', alignItems: 'baseline' }"
>
<span :style="{ color: t.fg, fontWeight: 600 }">{{ row[0] }}</span>
<span :style="{ color: t.fgMuted }">{{ row[1] }}</span>
@@ -67,3 +68,16 @@ useHead({ title: () => `${copy.value.pages.privacy.title} · dezky` })
</div>
</LandingContainer>
</template>
<style scoped>
.recipients-row {
display: grid;
grid-template-columns: 1.2fr 1.5fr 1fr;
}
@media (max-width: 768px) {
.recipients-row {
grid-template-columns: 1fr;
}
}
</style>
+24 -3
View File
@@ -13,11 +13,13 @@ useHead({ title: () => `${copy.value.pages.roadmap.label} · dezky` })
<template>
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
<LandingContainer pad="56px 64px 160px">
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
<LandingContainer pad="clamp(56px, 8vw, 56px) clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 240px), 1fr))', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
<div
v-for="(col, i) in c.columns" :key="i"
:style="{ padding: '32px 28px', borderLeft: i === 0 ? 'none' : `1px solid ${t.border}`, background: t.surface, minHeight: '260px' }"
class="roadmap-col"
:class="{ 'roadmap-col--first': i === 0 }"
:style="{ padding: '32px 28px', background: t.surface, minHeight: '260px', '--roadmap-border': t.border }"
>
<div :style="{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '24px' }">
<span :style="{ width: '6px', height: '6px', borderRadius: '999px', background: i === 0 ? t.signal : t.fgDim }" />
@@ -33,3 +35,22 @@ useHead({ title: () => `${copy.value.pages.roadmap.label} · dezky` })
</div>
</LandingContainer>
</template>
<style scoped>
.roadmap-col {
border-left: 1px solid var(--roadmap-border);
border-top: none;
}
.roadmap-col--first {
border-left: none;
}
@media (max-width: 768px) {
.roadmap-col {
border-left: none;
border-top: 1px solid var(--roadmap-border);
}
.roadmap-col--first {
border-top: none;
}
}
</style>