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:
@@ -13,7 +13,7 @@ const route = useRoute()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LandingPageHeader :label="copy.pages.comingSoonKicker" :title="title" :intro="body" />
|
<LandingPageHeader :label="copy.pages.comingSoonKicker" :title="title" :intro="body" />
|
||||||
<LandingContainer pad="48px 64px 160px">
|
<LandingContainer pad="clamp(32px, 5vw, 48px) clamp(20px, 5vw, 64px) clamp(56px, 8vw, 160px)">
|
||||||
<LandingBtn variant="primary" size="lg" @click="goToSection('#final-cta', route.path)">{{ copy.pages.ctaDemo }} →</LandingBtn>
|
<LandingBtn variant="primary" size="lg" @click="goToSection('#final-cta', route.path)">{{ copy.pages.ctaDemo }} →</LandingBtn>
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ const copy = useCopy()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section :style="{ background: t.bg, color: t.fg }">
|
<section :style="{ background: t.bg, color: t.fg }">
|
||||||
<LandingContainer pad="140px 64px">
|
<LandingContainer pad="clamp(56px, 8vw, 140px) clamp(20px, 5vw, 64px)">
|
||||||
<LandingSectionLabel :label="copy.compare.label" />
|
<LandingSectionLabel :label="copy.compare.label" />
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '80px', alignItems: 'end', marginBottom: '56px' }">
|
<div class="compare-intro-grid" :style="{ display: 'grid', gap: '80px', alignItems: 'end', marginBottom: '56px' }">
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.compare.heading }}</h2>
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.compare.heading }}</h2>
|
||||||
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '17px', lineHeight: 1.6, maxWidth: '460px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.compare.lede }}</p>
|
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '17px', lineHeight: 1.6, maxWidth: '460px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.compare.lede }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :style="{ border: `1px solid ${t.borderStrong}` }">
|
<div :style="{ border: `1px solid ${t.borderStrong}`, overflowX: 'auto' }">
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.2fr 1fr 1fr', background: t.fg, color: t.bg }">
|
<div class="compare-table-grid compare-header" :style="{ display: 'grid', background: t.fg, color: t.bg }">
|
||||||
<div :style="{ padding: '20px 28px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', letterSpacing: '0.12em', textTransform: 'uppercase', opacity: 0.6 }">kategori</div>
|
<div :style="{ padding: '20px 28px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', letterSpacing: '0.12em', textTransform: 'uppercase', opacity: 0.6 }">kategori</div>
|
||||||
<div :style="{ padding: '20px 28px', display: 'flex', alignItems: 'center', gap: '10px', borderLeft: `1px solid ${t.fgDim}` }">
|
<div :style="{ padding: '20px 28px', display: 'flex', alignItems: 'center', gap: '10px', borderLeft: `1px solid ${t.fgDim}` }">
|
||||||
<BrandNodeMark :size="18" :fg="t.bg" :accent="t.signal" />
|
<BrandNodeMark :size="18" :fg="t.bg" :accent="t.signal" />
|
||||||
@@ -26,21 +26,79 @@ const copy = useCopy()
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="(row, i) in copy.compare.rows" :key="i"
|
v-for="(row, i) in copy.compare.rows" :key="i"
|
||||||
|
class="compare-table-grid"
|
||||||
:style="{
|
:style="{
|
||||||
display: 'grid', gridTemplateColumns: '1.2fr 1fr 1fr',
|
display: 'grid',
|
||||||
borderTop: `1px solid ${t.border}`,
|
borderTop: `1px solid ${t.border}`,
|
||||||
background: i % 2 === 0 ? t.bg : t.bgAlt,
|
background: i % 2 === 0 ? t.bg : t.bgAlt,
|
||||||
fontFamily: '\'Inter\', sans-serif', fontSize: '15px',
|
fontFamily: '\'Inter\', sans-serif', fontSize: '15px',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div :style="{ padding: '22px 28px', color: t.fgMuted }">{{ row[0] }}</div>
|
<div class="cmp-cat" :style="{ padding: '22px 28px', color: t.fgMuted }">{{ row[0] }}</div>
|
||||||
<div :style="{ padding: '22px 28px', color: t.fg, fontWeight: 600, borderLeft: `1px solid ${t.border}`, display: 'flex', alignItems: 'center', gap: '10px' }">
|
<div class="cmp-val" :style="{ padding: '22px 28px', color: t.fg, fontWeight: 600, borderLeft: `1px solid ${t.border}` }">
|
||||||
|
<span class="cmp-key" :style="{ color: t.fgMuted }">{{ copy.compare.cols[0] }}</span>
|
||||||
|
<span :style="{ display: 'flex', alignItems: 'center', gap: '10px' }">
|
||||||
<span :style="{ width: '4px', height: '4px', background: t.signal, borderRadius: '999px', flexShrink: 0 }" />
|
<span :style="{ width: '4px', height: '4px', background: t.signal, borderRadius: '999px', flexShrink: 0 }" />
|
||||||
{{ row[1] }}
|
{{ row[1] }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="cmp-val" :style="{ padding: '22px 28px', color: t.fgMuted, borderLeft: `1px solid ${t.border}` }">
|
||||||
|
<span class="cmp-key" :style="{ color: t.fgMuted }">{{ copy.compare.cols[1] }}</span>
|
||||||
|
{{ row[2] }}
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ padding: '22px 28px', color: t.fgMuted, borderLeft: `1px solid ${t.border}` }">{{ row[2] }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.compare-intro-grid {
|
||||||
|
grid-template-columns: 1.1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compare-table-grid {
|
||||||
|
grid-template-columns: 1.2fr 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The per-cell column label only appears once the table stacks on mobile. */
|
||||||
|
.cmp-key {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.compare-intro-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compare-table-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header row is redundant once each value is labelled inline. */
|
||||||
|
.compare-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Category becomes the card title; values drop the column divider + tighten. */
|
||||||
|
.cmp-cat {
|
||||||
|
font-weight: 600;
|
||||||
|
padding-bottom: 2px !important;
|
||||||
|
}
|
||||||
|
.cmp-val {
|
||||||
|
border-left: none !important;
|
||||||
|
padding-top: 12px !important;
|
||||||
|
padding-bottom: 14px !important;
|
||||||
|
}
|
||||||
|
.cmp-key {
|
||||||
|
display: block;
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ withDefaults(defineProps<{
|
|||||||
pad?: string
|
pad?: string
|
||||||
max?: number
|
max?: number
|
||||||
}>(), {
|
}>(), {
|
||||||
pad: '120px 64px',
|
pad: 'clamp(56px, 8vw, 120px) clamp(20px, 5vw, 64px)',
|
||||||
max: 1280,
|
max: 1280,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const copy = useCopy()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section :style="{ background: t.bg, color: t.fg }">
|
<section :style="{ background: t.bg, color: t.fg }">
|
||||||
<LandingContainer pad="140px 64px" :max="960">
|
<LandingContainer pad="clamp(56px, 8vw, 140px) clamp(20px, 5vw, 64px)" :max="960">
|
||||||
<LandingSectionLabel :label="copy.faq.label" />
|
<LandingSectionLabel :label="copy.faq.label" />
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.faq.heading }}</h2>
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.faq.heading }}</h2>
|
||||||
<div :style="{ marginTop: '56px' }">
|
<div :style="{ marginTop: '56px' }">
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ const copy = useCopy()
|
|||||||
<div :style="{ position: 'absolute', right: '-180px', bottom: '-180px', opacity: 0.05 }">
|
<div :style="{ position: 'absolute', right: '-180px', bottom: '-180px', opacity: 0.05 }">
|
||||||
<BrandNodeMark :size="640" :fg="C.carbon" :accent="C.signal" />
|
<BrandNodeMark :size="640" :fg="C.carbon" :accent="C.signal" />
|
||||||
</div>
|
</div>
|
||||||
<LandingContainer pad="140px 64px">
|
<LandingContainer pad="clamp(56px, 8vw, 140px) clamp(20px, 5vw, 64px)">
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(48px, 6vw, 96px)', letterSpacing: '-0.04em', lineHeight: 0.98, margin: 0, color: C.bone, textWrap: 'balance', maxWidth: '900px' }">
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(32px, 6vw, 96px)', letterSpacing: '-0.04em', lineHeight: 0.98, margin: 0, color: C.bone, textWrap: 'balance', maxWidth: '900px' }">
|
||||||
<template v-for="(part, i) in copy.finalCta.heading" :key="i">
|
<template v-for="(part, i) in copy.finalCta.heading" :key="i">
|
||||||
<template v-if="typeof part === 'string'">{{ part }} </template>
|
<template v-if="typeof part === 'string'">{{ part }} </template>
|
||||||
<span v-else :style="{ color: C.signal }">{{ part.hl }}</span>
|
<span v-else :style="{ color: C.signal }">{{ part.hl }}</span>
|
||||||
</template>
|
</template>
|
||||||
</h2>
|
</h2>
|
||||||
<div :style="{ marginTop: '28px', maxWidth: '520px', fontFamily: '\'Inter\', sans-serif', fontSize: '19px', color: 'rgba(244,243,238,0.7)' }">{{ copy.finalCta.sub }}</div>
|
<div :style="{ marginTop: '28px', maxWidth: 'min(100%, 520px)', fontFamily: '\'Inter\', sans-serif', fontSize: '19px', color: 'rgba(244,243,238,0.7)' }">{{ copy.finalCta.sub }}</div>
|
||||||
<div :style="{ marginTop: '40px' }">
|
<div :style="{ marginTop: '40px' }">
|
||||||
<button :style="{
|
<button :style="{
|
||||||
background: C.signal, color: C.carbon, border: 'none',
|
background: C.signal, color: C.carbon, border: 'none',
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ function onLink(e: MouseEvent, href: string) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<footer :style="{ background: C.bone, color: C.carbon, borderTop: `1px solid ${t.border}` }">
|
<footer :style="{ background: C.bone, color: C.carbon, borderTop: `1px solid ${t.border}` }">
|
||||||
<LandingContainer pad="80px 64px 40px">
|
<LandingContainer pad="clamp(56px, 8vw, 80px) clamp(20px, 5vw, 64px) clamp(28px, 4vw, 40px)">
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.4fr repeat(4, 1fr)', gap: '48px' }">
|
<!-- Lockup + 4 link columns: desktop 1.4fr repeat(4,1fr), mobile stack -->
|
||||||
|
<div class="footer-main-grid">
|
||||||
<div>
|
<div>
|
||||||
<BrandNodeLockup :scale="0.75" :fg="C.carbon" :accent="C.signal" />
|
<BrandNodeLockup :scale="0.75" :fg="C.carbon" :accent="C.signal" />
|
||||||
<div :style="{ marginTop: '20px', fontFamily: '\'Inter\', sans-serif', fontSize: '14px', color: 'rgba(10,10,10,0.6)', maxWidth: '280px', lineHeight: 1.5 }">{{ copy.footer.tagline }}</div>
|
<div :style="{ marginTop: '20px', fontFamily: '\'Inter\', sans-serif', fontSize: '14px', color: 'rgba(10,10,10,0.6)', maxWidth: '280px', lineHeight: 1.5 }">{{ copy.footer.tagline }}</div>
|
||||||
@@ -34,6 +35,8 @@ function onLink(e: MouseEvent, href: string) {
|
|||||||
<div>{{ copy.footer.legal.addr }}</div>
|
<div>{{ copy.footer.legal.addr }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Four link columns reflow: 2 per row on tablet, 1 on small mobile -->
|
||||||
|
<div class="footer-link-columns">
|
||||||
<div v-for="(col, i) in copy.footer.cols" :key="i">
|
<div v-for="(col, i) in copy.footer.cols" :key="i">
|
||||||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: 'rgba(10,10,10,0.45)', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: '18px' }">{{ col[0] }}</div>
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: 'rgba(10,10,10,0.45)', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: '18px' }">{{ col[0] }}</div>
|
||||||
<div :style="{ display: 'flex', flexDirection: 'column', gap: '12px' }">
|
<div :style="{ display: 'flex', flexDirection: 'column', gap: '12px' }">
|
||||||
@@ -45,9 +48,11 @@ function onLink(e: MouseEvent, href: string) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div :style="{
|
<div :style="{
|
||||||
marginTop: '64px', paddingTop: '28px', borderTop: `1px solid ${t.border}`,
|
marginTop: '64px', paddingTop: '28px', borderTop: `1px solid ${t.border}`,
|
||||||
display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '24px',
|
display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '24px',
|
||||||
|
flexWrap: 'wrap',
|
||||||
fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: 'rgba(10,10,10,0.45)',
|
fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: 'rgba(10,10,10,0.45)',
|
||||||
}">
|
}">
|
||||||
<div>{{ copy.footer.copyright }}</div>
|
<div>{{ copy.footer.copyright }}</div>
|
||||||
@@ -62,3 +67,39 @@ function onLink(e: MouseEvent, href: string) {
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Desktop: lockup column (1.4fr) + one cell that spans the four link columns (4fr) */
|
||||||
|
.footer-main-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.4fr 4fr;
|
||||||
|
gap: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The four link columns always live in their own sub-grid so they can reflow
|
||||||
|
independently: 4 across on desktop, 2 on tablet, 1 on small mobile. */
|
||||||
|
.footer-link-columns {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
/* Stack lockup on top, link columns below */
|
||||||
|
.footer-main-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Two columns on tablet/large mobile, collapses to 1 on narrow phones */
|
||||||
|
.footer-link-columns {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr));
|
||||||
|
gap: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.footer-link-columns {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ const brush = {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section :style="{ background: t.bg, color: t.fg, paddingTop: '80px' }">
|
<section :style="{ background: t.bg, color: t.fg, paddingTop: 'clamp(40px, 6vw, 80px)' }">
|
||||||
<LandingContainer pad="60px 64px 0">
|
<LandingContainer pad="clamp(40px, 5vw, 60px) clamp(20px, 5vw, 64px) 0">
|
||||||
<div :style="{ display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '56px' }">
|
<div :style="{ display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '56px' }">
|
||||||
<span :style="{ width: '6px', height: '6px', borderRadius: '999px', background: t.signal, boxShadow: `0 0 0 4px ${t.signal}33` }" />
|
<span :style="{ width: '6px', height: '6px', borderRadius: '999px', background: t.signal, boxShadow: `0 0 0 4px ${t.signal}33` }" />
|
||||||
<span :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', color: t.fgMuted, letterSpacing: '0.04em', whiteSpace: 'nowrap' }">{{ copy.hero.eyebrow }}</span>
|
<span :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', color: t.fgMuted, letterSpacing: '0.04em', whiteSpace: 'nowrap' }">{{ copy.hero.eyebrow }}</span>
|
||||||
@@ -32,7 +32,7 @@ const brush = {
|
|||||||
|
|
||||||
<h1 :style="{
|
<h1 :style="{
|
||||||
fontFamily: '\'Inter Tight\', \'Inter\', sans-serif',
|
fontFamily: '\'Inter Tight\', \'Inter\', sans-serif',
|
||||||
fontWeight: 600, fontSize: 'clamp(56px, 7.2vw, 112px)', letterSpacing: '-0.04em',
|
fontWeight: 600, fontSize: 'clamp(38px, 7.2vw, 112px)', letterSpacing: '-0.04em',
|
||||||
lineHeight: 0.96, margin: 0, textWrap: 'balance', color: t.fg,
|
lineHeight: 0.96, margin: 0, textWrap: 'balance', color: t.fg,
|
||||||
}">
|
}">
|
||||||
<template v-for="(part, i) in headline" :key="i">
|
<template v-for="(part, i) in headline" :key="i">
|
||||||
@@ -61,7 +61,7 @@ const brush = {
|
|||||||
</div>
|
</div>
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
|
|
||||||
<LandingContainer pad="80px 64px 120px">
|
<LandingContainer pad="clamp(40px, 6vw, 80px) clamp(20px, 5vw, 64px) clamp(56px, 8vw, 120px)">
|
||||||
<LandingProductMockup />
|
<LandingProductMockup />
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ const copy = useCopy()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section :style="{ background: t.bg, color: t.fg }">
|
<section :style="{ background: t.bg, color: t.fg }">
|
||||||
<LandingContainer pad="120px 64px">
|
<LandingContainer pad="clamp(56px, 8vw, 120px) clamp(20px, 5vw, 64px)">
|
||||||
<LandingSectionLabel :label="copy.how.label" />
|
<LandingSectionLabel :label="copy.how.label" />
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.how.heading }}</h2>
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.how.heading }}</h2>
|
||||||
<div :style="{ marginTop: '80px', display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '0', position: 'relative' }">
|
<div class="how-steps" :style="{ marginTop: '80px', display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', position: 'relative' }">
|
||||||
<div
|
<div
|
||||||
v-for="(step, i) in copy.how.steps" :key="i"
|
v-for="(step, i) in copy.how.steps" :key="i"
|
||||||
:style="{ padding: '40px 36px 0 0', borderTop: `1px solid ${t.borderStrong}`, position: 'relative' }"
|
:style="{ padding: '40px 36px 0 0', borderTop: `1px solid ${t.borderStrong}`, position: 'relative' }"
|
||||||
@@ -29,3 +29,12 @@ const copy = useCopy()
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Timeline steps touch (gap 0) when side-by-side. When they stack on mobile,
|
||||||
|
add a row gap so each step's top node marker clears the previous step's text. */
|
||||||
|
.how-steps { gap: 0; }
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.how-steps { row-gap: 52px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// Sticky top nav with logo, anchor links, language toggle, login + demo CTA.
|
// Sticky top nav with logo, anchor links, language toggle, login + demo CTA.
|
||||||
// Ported from landing-sections.jsx Nav (light mode, production subset).
|
// Ported from landing-sections.jsx Nav (light mode, production subset).
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { APP_URL } from '~/utils/landingTokens'
|
import { APP_URL } from '~/utils/landingTokens'
|
||||||
import { useTheme, useCopy, useLang, toggleLang, scrollToAnchor, goToSection } from '~/composables/useLanding'
|
import { useTheme, useCopy, useLang, toggleLang, scrollToAnchor, goToSection } from '~/composables/useLanding'
|
||||||
@@ -11,6 +11,8 @@ const copy = useCopy()
|
|||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
const mobileOpen = ref(false)
|
||||||
|
|
||||||
const items = computed(() => [
|
const items = computed(() => [
|
||||||
{ label: copy.value.nav.product, href: '/#suite' },
|
{ label: copy.value.nav.product, href: '/#suite' },
|
||||||
{ label: copy.value.nav.security, href: '/#sovereignty' },
|
{ label: copy.value.nav.security, href: '/#sovereignty' },
|
||||||
@@ -32,6 +34,12 @@ function onNav(e: MouseEvent, href: string) {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
scrollToAnchor(href.slice(href.indexOf('#')))
|
scrollToAnchor(href.slice(href.indexOf('#')))
|
||||||
}
|
}
|
||||||
|
mobileOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMobileLink(e: MouseEvent, href: string) {
|
||||||
|
onNav(e, href)
|
||||||
|
mobileOpen.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -43,7 +51,8 @@ function onNav(e: MouseEvent, href: string) {
|
|||||||
borderBottom: `1px solid ${t.border}`,
|
borderBottom: `1px solid ${t.border}`,
|
||||||
}">
|
}">
|
||||||
<div :style="{
|
<div :style="{
|
||||||
maxWidth: '1280px', margin: '0 auto', padding: '18px 64px',
|
maxWidth: '1280px', margin: '0 auto',
|
||||||
|
padding: `18px clamp(20px, 5vw, 64px)`,
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||||
}">
|
}">
|
||||||
<NuxtLink to="/" :style="{ display: 'flex', alignItems: 'center', gap: '12px', cursor: 'pointer' }" @click="onLogo">
|
<NuxtLink to="/" :style="{ display: 'flex', alignItems: 'center', gap: '12px', cursor: 'pointer' }" @click="onLogo">
|
||||||
@@ -51,7 +60,8 @@ function onNav(e: MouseEvent, href: string) {
|
|||||||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontWeight: 600, fontSize: '16px', letterSpacing: '-0.02em', color: t.fg }">dezky</div>
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontWeight: 600, fontSize: '16px', letterSpacing: '-0.02em', color: t.fg }">dezky</div>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<nav :style="{ display: 'flex', alignItems: 'center', gap: '36px' }">
|
<!-- Desktop nav cluster — hidden on mobile via scoped CSS -->
|
||||||
|
<nav class="nav-desktop" :style="{ display: 'flex', alignItems: 'center', gap: '36px' }">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-for="(it, i) in items" :key="i" :to="it.href"
|
v-for="(it, i) in items" :key="i" :to="it.href"
|
||||||
:style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '14px', color: t.fgMuted, letterSpacing: '-0.005em' }"
|
:style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '14px', color: t.fgMuted, letterSpacing: '-0.005em' }"
|
||||||
@@ -59,7 +69,8 @@ function onNav(e: MouseEvent, href: string) {
|
|||||||
>{{ it.label }}</NuxtLink>
|
>{{ it.label }}</NuxtLink>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div :style="{ display: 'flex', alignItems: 'center', gap: '14px' }">
|
<!-- Desktop CTA cluster — hidden on mobile via scoped CSS -->
|
||||||
|
<div class="nav-desktop" :style="{ display: 'flex', alignItems: 'center', gap: '14px' }">
|
||||||
<button
|
<button
|
||||||
:style="{
|
:style="{
|
||||||
background: 'transparent', border: `1px solid ${t.border}`,
|
background: 'transparent', border: `1px solid ${t.border}`,
|
||||||
@@ -72,6 +83,110 @@ function onNav(e: MouseEvent, href: string) {
|
|||||||
<a :href="APP_URL" :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '14px', color: t.fgMuted }">{{ copy.nav.login }}</a>
|
<a :href="APP_URL" :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '14px', color: t.fgMuted }">{{ copy.nav.login }}</a>
|
||||||
<LandingBtn variant="primary" @click="goToSection('#final-cta', route.path)">{{ copy.nav.cta }} →</LandingBtn>
|
<LandingBtn variant="primary" @click="goToSection('#final-cta', route.path)">{{ copy.nav.cta }} →</LandingBtn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Hamburger — visible only on mobile via scoped CSS -->
|
||||||
|
<button
|
||||||
|
class="nav-hamburger"
|
||||||
|
:aria-label="mobileOpen ? 'Close menu' : 'Open menu'"
|
||||||
|
:aria-expanded="mobileOpen"
|
||||||
|
:style="{
|
||||||
|
flexDirection: 'column', justifyContent: 'center', alignItems: 'center',
|
||||||
|
gap: '5px', background: 'transparent', border: 'none', cursor: 'pointer',
|
||||||
|
padding: '6px', borderRadius: '4px',
|
||||||
|
}"
|
||||||
|
@click="mobileOpen = !mobileOpen"
|
||||||
|
>
|
||||||
|
<!-- Three bar lines; top/bottom rotate to X when open -->
|
||||||
|
<span :style="{
|
||||||
|
display: 'block', width: '22px', height: '2px',
|
||||||
|
background: t.fg, borderRadius: '2px',
|
||||||
|
transition: 'transform 0.2s, opacity 0.2s',
|
||||||
|
transform: mobileOpen ? 'translateY(7px) rotate(45deg)' : 'none',
|
||||||
|
}" />
|
||||||
|
<span :style="{
|
||||||
|
display: 'block', width: '22px', height: '2px',
|
||||||
|
background: t.fg, borderRadius: '2px',
|
||||||
|
transition: 'opacity 0.2s',
|
||||||
|
opacity: mobileOpen ? 0 : 1,
|
||||||
|
}" />
|
||||||
|
<span :style="{
|
||||||
|
display: 'block', width: '22px', height: '2px',
|
||||||
|
background: t.fg, borderRadius: '2px',
|
||||||
|
transition: 'transform 0.2s, opacity 0.2s',
|
||||||
|
transform: mobileOpen ? 'translateY(-7px) rotate(-45deg)' : 'none',
|
||||||
|
}" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile dropdown panel — JS-toggled, SSR-safe (visibility is CSS-controlled above 768px) -->
|
||||||
|
<div
|
||||||
|
v-if="mobileOpen"
|
||||||
|
class="nav-mobile-panel"
|
||||||
|
:style="{
|
||||||
|
background: 'rgba(250,250,247,0.97)',
|
||||||
|
borderTop: `1px solid ${t.border}`,
|
||||||
|
padding: '24px clamp(20px, 5vw, 64px) 32px',
|
||||||
|
display: 'flex', flexDirection: 'column', gap: '0',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<NuxtLink
|
||||||
|
v-for="(it, i) in items" :key="i" :to="it.href"
|
||||||
|
:style="{
|
||||||
|
fontFamily: '\'Inter\', sans-serif', fontSize: '16px', color: t.fgMuted,
|
||||||
|
letterSpacing: '-0.005em', padding: '14px 0',
|
||||||
|
borderBottom: `1px solid ${t.border}`,
|
||||||
|
display: 'block',
|
||||||
|
}"
|
||||||
|
@click="onMobileLink($event, it.href)"
|
||||||
|
>{{ it.label }}</NuxtLink>
|
||||||
|
|
||||||
|
<div :style="{ display: 'flex', alignItems: 'center', gap: '14px', paddingTop: '24px', flexWrap: 'wrap' }">
|
||||||
|
<button
|
||||||
|
:style="{
|
||||||
|
background: 'transparent', border: `1px solid ${t.border}`,
|
||||||
|
color: t.fgMuted, fontSize: '11px', padding: '6px 10px', borderRadius: '4px',
|
||||||
|
fontFamily: '\'JetBrains Mono\', monospace', letterSpacing: '0.06em', textTransform: 'uppercase',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
}"
|
||||||
|
@click="toggleLang()"
|
||||||
|
>{{ lang === 'da' ? 'da · en' : 'en · da' }}</button>
|
||||||
|
<a
|
||||||
|
:href="APP_URL"
|
||||||
|
:style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '14px', color: t.fgMuted }"
|
||||||
|
@click="mobileOpen = false"
|
||||||
|
>{{ copy.nav.login }}</a>
|
||||||
|
<LandingBtn variant="primary" @click="() => { goToSection('#final-cta', route.path); mobileOpen = false }">{{ copy.nav.cta }} →</LandingBtn>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Desktop clusters: visible by default, hidden on mobile */
|
||||||
|
.nav-desktop {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hamburger: hidden by default, shown on mobile */
|
||||||
|
.nav-hamburger {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile panel: always rendered when v-if is true, but constrained to mobile */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.nav-desktop {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-hamburger {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure the mobile panel is not shown on desktop even if somehow open */
|
||||||
|
@media (min-width: 769px) {
|
||||||
|
.nav-mobile-panel {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const copy = useCopy()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LandingContainer pad="120px 64px 0">
|
<LandingContainer pad="clamp(56px, 8vw, 120px) clamp(20px, 5vw, 64px) 0">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/"
|
to="/"
|
||||||
:style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', color: t.fgMuted, letterSpacing: '0.04em', display: 'inline-flex', alignItems: 'center', gap: '8px' }"
|
:style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', color: t.fgMuted, letterSpacing: '0.04em', display: 'inline-flex', alignItems: 'center', gap: '8px' }"
|
||||||
@@ -24,7 +24,7 @@ const copy = useCopy()
|
|||||||
|
|
||||||
<h1 :style="{
|
<h1 :style="{
|
||||||
fontFamily: '\'Inter Tight\', \'Inter\', sans-serif', fontWeight: 600,
|
fontFamily: '\'Inter Tight\', \'Inter\', sans-serif', fontWeight: 600,
|
||||||
fontSize: 'clamp(40px, 5.4vw, 76px)', letterSpacing: '-0.035em', lineHeight: 1.0,
|
fontSize: 'clamp(30px, 5.4vw, 76px)', letterSpacing: '-0.035em', lineHeight: 1.0,
|
||||||
margin: 0, textWrap: 'balance', color: t.fg, maxWidth: '900px',
|
margin: 0, textWrap: 'balance', color: t.fg, maxWidth: '900px',
|
||||||
}">{{ title }}</h1>
|
}">{{ title }}</h1>
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,15 @@ const annual = computed(() => monthly.value * 12)
|
|||||||
const dkk = new Intl.NumberFormat('da-DK', { style: 'currency', currency: 'DKK', maximumFractionDigits: 0 })
|
const dkk = new Intl.NumberFormat('da-DK', { style: 'currency', currency: 'DKK', maximumFractionDigits: 0 })
|
||||||
const fmtMonthly = computed(() => dkk.format(monthly.value))
|
const fmtMonthly = computed(() => dkk.format(monthly.value))
|
||||||
const fmtAnnual = computed(() => dkk.format(annual.value))
|
const fmtAnnual = computed(() => dkk.format(annual.value))
|
||||||
|
|
||||||
|
// Expose theme border color to scoped CSS via v-bind() so media-query-driven
|
||||||
|
// border direction changes (left on desktop, top on mobile) can still use the
|
||||||
|
// reactive theme value without window reads.
|
||||||
|
const borderColor = computed(() => t.value.border)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
|
<div class="calc-grid" :style="{ display: 'grid', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
|
||||||
<!-- Controls -->
|
<!-- Controls -->
|
||||||
<div :style="{ padding: '36px', background: t.surface, display: 'flex', flexDirection: 'column', gap: '28px', justifyContent: 'center' }">
|
<div :style="{ padding: '36px', background: t.surface, display: 'flex', flexDirection: 'column', gap: '28px', justifyContent: 'center' }">
|
||||||
<div>
|
<div>
|
||||||
@@ -74,7 +79,7 @@ const fmtAnnual = computed(() => dkk.format(annual.value))
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Output -->
|
<!-- Output -->
|
||||||
<div :style="{ padding: '36px', background: t.bgAlt, borderLeft: `1px solid ${t.border}`, display: 'flex', flexDirection: 'column', justifyContent: 'center' }">
|
<div class="calc-output" :style="{ padding: '36px', background: t.bgAlt, display: 'flex', flexDirection: 'column', justifyContent: 'center' }">
|
||||||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgMuted, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.monthlyLabel }}</div>
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgMuted, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.monthlyLabel }}</div>
|
||||||
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(40px, 5vw, 60px)', letterSpacing: '-0.03em', lineHeight: 1.0, color: t.fg, marginTop: '8px' }">{{ fmtMonthly }}</div>
|
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(40px, 5vw, 60px)', letterSpacing: '-0.03em', lineHeight: 1.0, color: t.fg, marginTop: '8px' }">{{ fmtMonthly }}</div>
|
||||||
<div :style="{ marginTop: '20px', paddingTop: '20px', borderTop: `1px solid ${t.border}`, fontFamily: '\'Inter\', sans-serif', fontSize: '15px', color: t.fgMuted }">
|
<div :style="{ marginTop: '20px', paddingTop: '20px', borderTop: `1px solid ${t.border}`, fontFamily: '\'Inter\', sans-serif', fontSize: '15px', color: t.fgMuted }">
|
||||||
@@ -83,3 +88,24 @@ const fmtAnnual = computed(() => dkk.format(annual.value))
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Desktop: two equal columns; output panel has a left divider. */
|
||||||
|
.calc-grid {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
.calc-output {
|
||||||
|
border-left: 1px solid v-bind(borderColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: stack to single column; swap left divider to a top divider. */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.calc-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.calc-output {
|
||||||
|
border-left: none;
|
||||||
|
border-top: 1px solid v-bind(borderColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const initial = computed(() => (props.name[0] ?? '').toUpperCase())
|
|||||||
<template>
|
<template>
|
||||||
<div :style="{
|
<div :style="{
|
||||||
background: bg, border: `1px solid ${border}`, borderRadius: '4px',
|
background: bg, border: `1px solid ${border}`, borderRadius: '4px',
|
||||||
padding: '20px 22px', display: 'flex', alignItems: 'center', gap: '16px',
|
padding: '20px 22px', display: 'flex', alignItems: 'center', gap: '16px', flexWrap: 'wrap',
|
||||||
opacity: placeholder ? 0.55 : 1,
|
opacity: placeholder ? 0.55 : 1,
|
||||||
borderStyle: placeholder ? 'dashed' : 'solid',
|
borderStyle: placeholder ? 'dashed' : 'solid',
|
||||||
}">
|
}">
|
||||||
@@ -28,7 +28,7 @@ const initial = computed(() => (props.name[0] ?? '').toUpperCase())
|
|||||||
fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 700,
|
fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 700,
|
||||||
fontSize: '22px', color: '#FFF', letterSpacing: '-0.02em', flexShrink: 0,
|
fontSize: '22px', color: '#FFF', letterSpacing: '-0.02em', flexShrink: 0,
|
||||||
}">{{ initial }}</div>
|
}">{{ initial }}</div>
|
||||||
<div :style="{ flex: 1 }">
|
<div :style="{ flex: 1, minWidth: 0 }">
|
||||||
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '17px', color: fg, letterSpacing: '-0.02em' }">{{ name }}</div>
|
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '17px', color: fg, letterSpacing: '-0.02em' }">{{ name }}</div>
|
||||||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: 'rgba(0,0,0,0.5)', marginTop: '2px' }">{{ subtitle }}</div>
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: 'rgba(0,0,0,0.5)', marginTop: '2px' }">{{ subtitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ const copy = useCopy()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section id="pricing" :style="{ background: t.bgAlt, color: t.fg, borderTop: `1px solid ${t.border}`, borderBottom: `1px solid ${t.border}`, scrollMarginTop: '72px' }">
|
<section id="pricing" :style="{ background: t.bgAlt, color: t.fg, borderTop: `1px solid ${t.border}`, borderBottom: `1px solid ${t.border}`, scrollMarginTop: '72px' }">
|
||||||
<LandingContainer pad="120px 64px">
|
<LandingContainer pad="clamp(56px, 8vw, 120px) clamp(20px, 5vw, 64px)">
|
||||||
<LandingSectionLabel :label="copy.pricing.label" />
|
<LandingSectionLabel :label="copy.pricing.label" />
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '80px', alignItems: 'center' }">
|
<div class="pricing-grid" :style="{ display: 'grid', gap: '80px', alignItems: 'center' }">
|
||||||
<div>
|
<div>
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.pricing.heading }}</h2>
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.pricing.heading }}</h2>
|
||||||
<div :style="{ marginTop: '28px', maxWidth: '520px' }">
|
<div :style="{ marginTop: '28px', maxWidth: '520px' }">
|
||||||
@@ -23,7 +23,7 @@ const copy = useCopy()
|
|||||||
<div :style="{ background: t.surface, border: `1px solid ${t.border}`, borderRadius: '6px', padding: '36px 36px' }">
|
<div :style="{ background: t.surface, border: `1px solid ${t.border}`, borderRadius: '6px', padding: '36px 36px' }">
|
||||||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.1em', textTransform: 'uppercase' }">{{ copy.pricing.teaser }}</div>
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.1em', textTransform: 'uppercase' }">{{ copy.pricing.teaser }}</div>
|
||||||
<div :style="{ display: 'flex', alignItems: 'baseline', gap: '12px', marginTop: '12px' }">
|
<div :style="{ display: 'flex', alignItems: 'baseline', gap: '12px', marginTop: '12px' }">
|
||||||
<span :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontSize: '96px', fontWeight: 600, letterSpacing: '-0.045em', lineHeight: 1, color: t.fg }">{{ copy.pricing.price }}</span>
|
<span :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontSize: 'clamp(56px, 10vw, 96px)', fontWeight: 600, letterSpacing: '-0.045em', lineHeight: 1, color: t.fg }">{{ copy.pricing.price }}</span>
|
||||||
<span :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '13px', color: t.fgMuted }">{{ copy.pricing.unit }}</span>
|
<span :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '13px', color: t.fgMuted }">{{ copy.pricing.unit }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ height: '1px', background: t.border, margin: '28px 0' }" />
|
<div :style="{ height: '1px', background: t.border, margin: '28px 0' }" />
|
||||||
@@ -33,3 +33,14 @@ const copy = useCopy()
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pricing-grid {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.pricing-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ const copy = useCopy()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section :style="{ background: t.bg, color: t.fg }">
|
<section :style="{ background: t.bg, color: t.fg }">
|
||||||
<LandingContainer pad="120px 64px">
|
<LandingContainer pad="clamp(56px, 8vw, 120px) clamp(20px, 5vw, 64px)">
|
||||||
<LandingSectionLabel :label="copy.problem.label" />
|
<LandingSectionLabel :label="copy.problem.label" />
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1fr 1.4fr', gap: '80px' }">
|
<div class="problem-grid" :style="{ display: 'grid', gap: '80px' }">
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.problem.heading }}</h2>
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.problem.heading }}</h2>
|
||||||
<div :style="{ display: 'flex', flexDirection: 'column', gap: '18px', paddingTop: '12px' }">
|
<div :style="{ display: 'flex', flexDirection: 'column', gap: '18px', paddingTop: '12px' }">
|
||||||
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '18px', lineHeight: 1.6, maxWidth: '620px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.problem.p1 }}</p>
|
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '18px', lineHeight: 1.6, maxWidth: '620px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.problem.p1 }}</p>
|
||||||
@@ -19,3 +19,14 @@ const copy = useCopy()
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.problem-grid {
|
||||||
|
grid-template-columns: 1fr 1.4fr;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.problem-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -2,13 +2,47 @@
|
|||||||
// Stylized customer-portal dashboard shown under the hero. Illustrative only —
|
// Stylized customer-portal dashboard shown under the hero. Illustrative only —
|
||||||
// labels are intentionally Danish in both languages. Ported from
|
// labels are intentionally Danish in both languages. Ported from
|
||||||
// landing-sections.jsx ProductMockup (light mode).
|
// landing-sections.jsx ProductMockup (light mode).
|
||||||
import { computed } from 'vue'
|
import { computed, ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import { C } from '~/utils/landingTokens'
|
import { C } from '~/utils/landingTokens'
|
||||||
import { useTheme, useDark } from '~/composables/useLanding'
|
import { useTheme, useDark } from '~/composables/useLanding'
|
||||||
|
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const dark = useDark()
|
const dark = useDark()
|
||||||
|
|
||||||
|
// The mockup is a fixed-layout dashboard. On desktop it runs fluid/full-width;
|
||||||
|
// when the available width drops below its comfortable design width we scale
|
||||||
|
// the WHOLE thing down (CSS zoom reflows, so no height hacks) instead of
|
||||||
|
// reflowing the internal dashboard — keeps it a recognisable mini-dashboard on
|
||||||
|
// phones. SSR-safe: renders at zoom 1 on the server, recomputes after mount.
|
||||||
|
const DESIGN_W = 760
|
||||||
|
const frame = ref<HTMLElement | null>(null)
|
||||||
|
const zoom = ref(1)
|
||||||
|
const frameWidth = ref('100%')
|
||||||
|
let ro: ResizeObserver | null = null
|
||||||
|
|
||||||
|
function recompute() {
|
||||||
|
const parent = frame.value?.parentElement
|
||||||
|
if (!parent) return
|
||||||
|
const w = parent.clientWidth
|
||||||
|
if (w >= DESIGN_W) {
|
||||||
|
zoom.value = 1
|
||||||
|
frameWidth.value = '100%'
|
||||||
|
} else {
|
||||||
|
frameWidth.value = `${DESIGN_W}px`
|
||||||
|
zoom.value = Math.max(0.4, w / DESIGN_W)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
recompute()
|
||||||
|
const parent = frame.value?.parentElement
|
||||||
|
if (parent && typeof ResizeObserver !== 'undefined') {
|
||||||
|
ro = new ResizeObserver(recompute)
|
||||||
|
ro.observe(parent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => ro?.disconnect())
|
||||||
|
|
||||||
const m = computed(() => ({
|
const m = computed(() => ({
|
||||||
bg: dark.value ? '#171715' : '#FFFFFF',
|
bg: dark.value ? '#171715' : '#FFFFFF',
|
||||||
border: dark.value ? 'rgba(255,255,255,0.08)' : 'rgba(10,10,10,0.08)',
|
border: dark.value ? 'rgba(255,255,255,0.08)' : 'rgba(10,10,10,0.08)',
|
||||||
@@ -39,10 +73,11 @@ const recent: [string, string, string][] = [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :style="{
|
<div ref="frame" :style="{
|
||||||
background: m.bg, border: `1px solid ${m.border}`, borderRadius: '8px',
|
background: m.bg, border: `1px solid ${m.border}`, borderRadius: '8px',
|
||||||
boxShadow: dark ? '0 40px 80px rgba(0,0,0,0.5)' : '0 30px 80px rgba(10,10,10,0.08)',
|
boxShadow: dark ? '0 40px 80px rgba(0,0,0,0.5)' : '0 30px 80px rgba(10,10,10,0.08)',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
width: frameWidth, zoom,
|
||||||
}">
|
}">
|
||||||
<!-- Window chrome -->
|
<!-- Window chrome -->
|
||||||
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', padding: '14px 18px', borderBottom: `1px solid ${m.border}` }">
|
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', padding: '14px 18px', borderBottom: `1px solid ${m.border}` }">
|
||||||
@@ -52,7 +87,7 @@ const recent: [string, string, string][] = [
|
|||||||
<div :style="{ marginLeft: '16px', padding: '4px 12px', background: m.subtle, borderRadius: '4px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: m.muted }">app.dezky.eu / dashboard</div>
|
<div :style="{ marginLeft: '16px', padding: '4px 12px', background: m.subtle, borderRadius: '4px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: m.muted }">app.dezky.eu / dashboard</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '220px 1fr', minHeight: '460px' }">
|
<div class="mockup-body" :style="{ display: 'grid', minHeight: '460px' }">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<div :style="{ borderRight: `1px solid ${m.border}`, padding: '20px 0' }">
|
<div :style="{ borderRight: `1px solid ${m.border}`, padding: '20px 0' }">
|
||||||
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', padding: '0 20px', marginBottom: '24px' }">
|
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', padding: '0 20px', marginBottom: '24px' }">
|
||||||
@@ -84,8 +119,8 @@ const recent: [string, string, string][] = [
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
<div :style="{ padding: '28px 32px' }">
|
<div :style="{ padding: 'clamp(16px, 4vw, 28px) clamp(16px, 4vw, 32px)' }">
|
||||||
<div :style="{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }">
|
<div :style="{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', flexWrap: 'wrap', gap: '8px' }">
|
||||||
<div>
|
<div>
|
||||||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase' }">indbakke</div>
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase' }">indbakke</div>
|
||||||
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '28px', color: m.fg, marginTop: '6px', letterSpacing: '-0.02em' }">god morgen, anne</div>
|
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '28px', color: m.fg, marginTop: '6px', letterSpacing: '-0.02em' }">god morgen, anne</div>
|
||||||
@@ -95,7 +130,7 @@ const recent: [string, string, string][] = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :style="{ marginTop: '24px', display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '12px' }">
|
<div :style="{ marginTop: '24px', display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 120px), 1fr))', gap: '12px' }">
|
||||||
<div v-for="(s, i) in stats" :key="i" :style="{ padding: '16px 18px', background: m.subtle, borderRadius: '4px' }">
|
<div v-for="(s, i) in stats" :key="i" :style="{ padding: '16px 18px', background: m.subtle, borderRadius: '4px' }">
|
||||||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '10px', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase' }">{{ s[2] }}</div>
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '10px', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase' }">{{ s[2] }}</div>
|
||||||
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontSize: '28px', fontWeight: 600, color: m.fg, marginTop: '4px' }">{{ s[1] }}</div>
|
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontSize: '28px', fontWeight: 600, color: m.fg, marginTop: '4px' }">{{ s[1] }}</div>
|
||||||
@@ -107,8 +142,8 @@ const recent: [string, string, string][] = [
|
|||||||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '10px', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: '12px' }">seneste · indbakke</div>
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '10px', color: m.muted, letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: '12px' }">seneste · indbakke</div>
|
||||||
<div
|
<div
|
||||||
v-for="(r, i) in recent" :key="i"
|
v-for="(r, i) in recent" :key="i"
|
||||||
|
class="mockup-mail-row"
|
||||||
:style="{
|
:style="{
|
||||||
display: 'grid', gridTemplateColumns: '200px 1fr 60px',
|
|
||||||
padding: '12px 0', borderTop: i === 0 ? `1px solid ${m.border}` : 'none',
|
padding: '12px 0', borderTop: i === 0 ? `1px solid ${m.border}` : 'none',
|
||||||
borderBottom: `1px solid ${m.border}`,
|
borderBottom: `1px solid ${m.border}`,
|
||||||
fontFamily: '\'Inter\', sans-serif', fontSize: '13px', alignItems: 'center',
|
fontFamily: '\'Inter\', sans-serif', fontSize: '13px', alignItems: 'center',
|
||||||
@@ -123,3 +158,15 @@ const recent: [string, string, string][] = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Fixed dashboard layout — the whole mockup is scaled to fit on small screens
|
||||||
|
(see the zoom logic in <script>), so the internal grid stays desktop-shaped. */
|
||||||
|
.mockup-body {
|
||||||
|
grid-template-columns: 220px 1fr;
|
||||||
|
}
|
||||||
|
.mockup-mail-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 200px 1fr 60px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const labelParts = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section id="sovereignty" :style="{ background: t.invert, color: t.invertFg, scrollMarginTop: '72px' }">
|
<section id="sovereignty" :style="{ background: t.invert, color: t.invertFg, scrollMarginTop: '72px' }">
|
||||||
<LandingContainer pad="140px 64px">
|
<LandingContainer pad="clamp(56px, 8vw, 140px) clamp(20px, 5vw, 64px)">
|
||||||
<div :style="{
|
<div :style="{
|
||||||
display: 'flex', alignItems: 'center', gap: '14px',
|
display: 'flex', alignItems: 'center', gap: '14px',
|
||||||
paddingBottom: '24px', marginBottom: '56px',
|
paddingBottom: '24px', marginBottom: '56px',
|
||||||
@@ -37,7 +37,7 @@ const labelParts = computed(() => {
|
|||||||
<span>—</span>
|
<span>—</span>
|
||||||
<span>{{ labelParts.text }}</span>
|
<span>{{ labelParts.text }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '96px' }">
|
<div class="sovereignty-main-grid" :style="{ display: 'grid', gap: '96px' }">
|
||||||
<div>
|
<div>
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.invertFg }">{{ copy.sovereignty.heading }}</h2>
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.invertFg }">{{ copy.sovereignty.heading }}</h2>
|
||||||
<div :style="{ marginTop: '36px', display: 'flex', flexDirection: 'column', gap: '18px', maxWidth: '540px' }">
|
<div :style="{ marginTop: '36px', display: 'flex', flexDirection: 'column', gap: '18px', maxWidth: '540px' }">
|
||||||
@@ -47,8 +47,9 @@ const labelParts = computed(() => {
|
|||||||
<div :style="{ paddingTop: '8px' }">
|
<div :style="{ paddingTop: '8px' }">
|
||||||
<div
|
<div
|
||||||
v-for="(row, i) in copy.sovereignty.checks" :key="i"
|
v-for="(row, i) in copy.sovereignty.checks" :key="i"
|
||||||
|
class="sovereignty-check-row"
|
||||||
:style="{
|
:style="{
|
||||||
display: 'grid', gridTemplateColumns: '1fr 1.4fr', padding: '20px 0',
|
display: 'grid', padding: '20px 0',
|
||||||
borderTop: i === 0 ? `1px solid ${rule18}` : 'none',
|
borderTop: i === 0 ? `1px solid ${rule18}` : 'none',
|
||||||
borderBottom: `1px solid ${rule10}`,
|
borderBottom: `1px solid ${rule10}`,
|
||||||
fontFamily: '\'Inter\', sans-serif', fontSize: '14px', alignItems: 'baseline',
|
fontFamily: '\'Inter\', sans-serif', fontSize: '14px', alignItems: 'baseline',
|
||||||
@@ -62,3 +63,23 @@ const labelParts = computed(() => {
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.sovereignty-main-grid {
|
||||||
|
grid-template-columns: 1.1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sovereignty-check-row {
|
||||||
|
grid-template-columns: 1fr 1.4fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sovereignty-main-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sovereignty-check-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -8,17 +8,18 @@ const copy = useCopy()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section id="stack" :style="{ background: t.bg, color: t.fg, scrollMarginTop: '72px' }">
|
<section id="stack" :style="{ background: t.bg, color: t.fg, scrollMarginTop: '72px' }">
|
||||||
<LandingContainer pad="140px 64px">
|
<LandingContainer pad="clamp(56px, 8vw, 140px) clamp(20px, 5vw, 64px)">
|
||||||
<LandingSectionLabel :label="copy.stack.label" />
|
<LandingSectionLabel :label="copy.stack.label" />
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '80px', alignItems: 'end', marginBottom: '56px' }">
|
<div class="stack-intro-grid" :style="{ display: 'grid', gap: '80px', alignItems: 'end', marginBottom: '56px' }">
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.stack.heading }}</h2>
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.stack.heading }}</h2>
|
||||||
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '20px', lineHeight: 1.5, maxWidth: '480px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.stack.lede }}</p>
|
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '20px', lineHeight: 1.5, maxWidth: '480px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.stack.lede }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
v-for="(row, i) in copy.stack.rows" :key="i"
|
v-for="(row, i) in copy.stack.rows" :key="i"
|
||||||
|
class="stack-row-grid"
|
||||||
:style="{
|
:style="{
|
||||||
display: 'grid', gridTemplateColumns: '1.1fr 1.6fr 1.3fr',
|
display: 'grid',
|
||||||
gap: '24px', padding: '24px 0',
|
gap: '24px', padding: '24px 0',
|
||||||
borderTop: i === 0 ? `1px solid ${t.borderStrong}` : 'none',
|
borderTop: i === 0 ? `1px solid ${t.borderStrong}` : 'none',
|
||||||
borderBottom: `1px solid ${t.border}`,
|
borderBottom: `1px solid ${t.border}`,
|
||||||
@@ -33,3 +34,23 @@ const copy = useCopy()
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.stack-intro-grid {
|
||||||
|
grid-template-columns: 1.1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stack-row-grid {
|
||||||
|
grid-template-columns: 1.1fr 1.6fr 1.3fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.stack-intro-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stack-row-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ const copy = useCopy()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section id="suite" :style="{ background: t.bgAlt, color: t.fg, borderTop: `1px solid ${t.border}`, borderBottom: `1px solid ${t.border}`, scrollMarginTop: '72px' }">
|
<section id="suite" :style="{ background: t.bgAlt, color: t.fg, borderTop: `1px solid ${t.border}`, borderBottom: `1px solid ${t.border}`, scrollMarginTop: '72px' }">
|
||||||
<LandingContainer pad="120px 64px">
|
<LandingContainer pad="clamp(56px, 8vw, 120px) clamp(20px, 5vw, 64px)">
|
||||||
<LandingSectionLabel :label="copy.suite.label" />
|
<LandingSectionLabel :label="copy.suite.label" />
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '80px', alignItems: 'end', marginBottom: '64px' }">
|
<div class="suite-intro-grid" :style="{ display: 'grid', gap: '80px', alignItems: 'end', marginBottom: '64px' }">
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.suite.heading }}</h2>
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.suite.heading }}</h2>
|
||||||
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '20px', lineHeight: 1.5, maxWidth: '520px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.suite.lede }}</p>
|
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '20px', lineHeight: 1.5, maxWidth: '520px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.suite.lede }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: '0', border: `1px solid ${t.border}` }">
|
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 220px), 1fr))', gap: '0', border: `1px solid ${t.border}` }">
|
||||||
<div
|
<div
|
||||||
v-for="(card, i) in copy.suite.cards" :key="i"
|
v-for="(card, i) in copy.suite.cards" :key="i"
|
||||||
:style="{
|
:style="{
|
||||||
@@ -49,3 +49,15 @@ const copy = useCopy()
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.suite-intro-grid {
|
||||||
|
grid-template-columns: 1.1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.suite-intro-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ const partnerCards = computed(() =>
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section id="whitelabel" :style="{ background: sectionBg, color: t.fg, borderTop: `1px solid ${t.border}`, borderBottom: `1px solid ${t.border}`, scrollMarginTop: '72px' }">
|
<section id="whitelabel" :style="{ background: sectionBg, color: t.fg, borderTop: `1px solid ${t.border}`, borderBottom: `1px solid ${t.border}`, scrollMarginTop: '72px' }">
|
||||||
<LandingContainer pad="120px 64px">
|
<LandingContainer pad="clamp(56px, 8vw, 120px) clamp(20px, 5vw, 64px)">
|
||||||
<LandingSectionLabel :label="copy.whitelabel.label" />
|
<LandingSectionLabel :label="copy.whitelabel.label" />
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.2fr 1fr', gap: '80px', alignItems: 'start' }">
|
<div class="whitelabel-grid" :style="{ display: 'grid', gap: 'clamp(40px, 5vw, 80px)', alignItems: 'start' }">
|
||||||
<div>
|
<div>
|
||||||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.whitelabel.heading }}</h2>
|
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(36px, 4.4vw, 64px)', letterSpacing: '-0.032em', lineHeight: 1.0, margin: 0, textWrap: 'balance', color: t.fg }">{{ copy.whitelabel.heading }}</h2>
|
||||||
<div :style="{ marginTop: '32px', maxWidth: '520px' }">
|
<div :style="{ marginTop: '32px', maxWidth: 'min(100%, 520px)' }">
|
||||||
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '20px', lineHeight: 1.5, maxWidth: '640px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.whitelabel.lede }}</p>
|
<p :style="{ fontFamily: '\'Inter\', sans-serif', fontSize: '20px', lineHeight: 1.5, maxWidth: '640px', color: t.fgMuted, margin: 0, textWrap: 'pretty' }">{{ copy.whitelabel.lede }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ marginTop: '32px', display: 'flex', flexDirection: 'column', gap: '12px' }">
|
<div :style="{ marginTop: '32px', display: 'flex', flexDirection: 'column', gap: '12px' }">
|
||||||
@@ -65,3 +65,14 @@ const partnerCards = computed(() =>
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.whitelabel-grid {
|
||||||
|
grid-template-columns: 1.2fr 1fr;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.whitelabel-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -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">
|
<script setup lang="ts">
|
||||||
import { useTheme, useCopy } from '~/composables/useLanding'
|
import { useTheme, useCopy } from '~/composables/useLanding'
|
||||||
|
|
||||||
@@ -13,8 +24,8 @@ useHead({ title: () => `${copy.value.pages.about.label} · dezky` })
|
|||||||
<template>
|
<template>
|
||||||
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
||||||
|
|
||||||
<LandingContainer pad="64px 64px 160px">
|
<LandingContainer pad="clamp(56px, 8vw, 64px) clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.2fr 1fr', gap: '80px', alignItems: 'start' }">
|
<div class="about-body-grid" :style="{ display: 'grid', gap: '80px', alignItems: 'start' }">
|
||||||
<div :style="{ display: 'flex', flexDirection: 'column', gap: '24px' }">
|
<div :style="{ display: 'flex', flexDirection: 'column', gap: '24px' }">
|
||||||
<p
|
<p
|
||||||
v-for="(para, i) in c.body" :key="i"
|
v-for="(para, i) in c.body" :key="i"
|
||||||
|
|||||||
+108
-42
@@ -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 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 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 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 = [
|
const bigSwatches = [
|
||||||
{ name: 'Carbon', hex: C.carbon, rgb: '10 10 10', role: 'Foreground · containers · type', dark: true },
|
{ name: 'Carbon', hex: C.carbon, rgb: '10 10 10', role: 'Foreground · containers · type', dark: true },
|
||||||
@@ -99,11 +99,11 @@ const doDont: DoDont[] = [
|
|||||||
<template>
|
<template>
|
||||||
<div :style="{ background: C.paper, color: C.carbon }">
|
<div :style="{ background: C.paper, color: C.carbon }">
|
||||||
<!-- 00 · COVER -->
|
<!-- 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 }">
|
<div :style="{ position: 'absolute', right: '-160px', bottom: '-160px', opacity: 0.06 }">
|
||||||
<BrandNodeMark :size="720" :fg="C.carbon" :accent="C.signal" />
|
<BrandNodeMark :size="720" :fg="C.carbon" :accent="C.signal" />
|
||||||
</div>
|
</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="{ display: 'flex', alignItems: 'center', gap: '14px' }">
|
||||||
<div :style="{ width: '56px', height: '56px', background: C.bone, borderRadius: '14px', display: 'flex', alignItems: 'center', justifyContent: 'center' }">
|
<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" />
|
<BrandNodeMark :size="44" :fg="C.carbon" :accent="C.signal" />
|
||||||
@@ -117,12 +117,12 @@ const doDont: DoDont[] = [
|
|||||||
</div>
|
</div>
|
||||||
<div :style="{ position: 'relative' }">
|
<div :style="{ position: 'relative' }">
|
||||||
<div :style="eyebrow">The dezky brand</div>
|
<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>
|
Quiet software,<br>
|
||||||
<span :style="{ color: C.signal }">sovereign data.</span>
|
<span :style="{ color: C.signal }">sovereign data.</span>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</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>dezky · brand system</div>
|
||||||
<div>00 / 06</div>
|
<div>00 / 06</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,14 +133,14 @@ const doDont: DoDont[] = [
|
|||||||
<div :style="eyebrow">01 · Logo</div>
|
<div :style="eyebrow">01 · Logo</div>
|
||||||
<h1 :style="h1">The mark</h1>
|
<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>
|
<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="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="{ 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.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 :style="frame(C.bone)"><BrandNodeMark :size="180" :fg="C.carbon" :accent="C.signal" /></div>
|
||||||
</div>
|
</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">Primary · on carbon</div>
|
||||||
<div :style="caption">Reversed · on signal</div>
|
<div :style="caption">Reversed · on signal</div>
|
||||||
<div :style="caption">Light · on bone</div>
|
<div :style="caption">Light · on bone</div>
|
||||||
@@ -151,9 +151,9 @@ const doDont: DoDont[] = [
|
|||||||
<section :style="{ ...page, background: C.bone }">
|
<section :style="{ ...page, background: C.bone }">
|
||||||
<div :style="eyebrow">01.1 · Anatomy</div>
|
<div :style="eyebrow">01.1 · Anatomy</div>
|
||||||
<h2 :style="h2">Constructed, not drawn.</h2>
|
<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)">
|
<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>
|
<defs>
|
||||||
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
|
<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" />
|
<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>
|
||||||
<div>
|
<div>
|
||||||
<h3 :style="h3Style(0)">Geometry</h3>
|
<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>
|
<tbody>
|
||||||
<tr v-for="([k, v], i) in specRows" :key="i" :style="{ borderBottom: `1px solid ${C.fog}` }">
|
<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', color: 'rgba(0,0,0,0.55)', width: '40%' }">{{ k }}</td>
|
||||||
<td :style="{ padding: '12px 0', fontFamily: mono, fontSize: '13px' }">{{ v }}</td>
|
<td :style="{ padding: '12px 0', fontFamily: mono, fontSize: '13px' }">{{ v }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
@@ -199,7 +199,7 @@ const doDont: DoDont[] = [
|
|||||||
<section :style="{ ...page, background: C.paper }">
|
<section :style="{ ...page, background: C.paper }">
|
||||||
<div :style="eyebrow">01.2 · Clear space · Minimum size</div>
|
<div :style="eyebrow">01.2 · Clear space · Minimum size</div>
|
||||||
<h2 :style="h2">Give it room.</h2>
|
<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>
|
||||||
<div :style="frame(C.fog, 360)">
|
<div :style="frame(C.fog, 360)">
|
||||||
<div :style="{ position: 'relative' }">
|
<div :style="{ position: 'relative' }">
|
||||||
@@ -212,7 +212,7 @@ const doDont: DoDont[] = [
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div :style="frame(C.fog, 360)">
|
<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' }">
|
<div v-for="s in [16, 24, 48, 96]" :key="s" :style="{ textAlign: 'center' }">
|
||||||
<BrandNodeMark :size="s" :fg="C.carbon" :accent="C.signal" />
|
<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 :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>
|
<div :style="eyebrow">01.3 · Wordmark · Lockup</div>
|
||||||
<h2 :style="h2">Letters set in JetBrains Mono.</h2>
|
<h2 :style="h2">Letters set in JetBrains Mono.</h2>
|
||||||
<div :style="frame(C.paper, 300)">
|
<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>
|
||||||
<div :style="caption">Wordmark · 100% scale</div>
|
<div :style="caption">Wordmark · 100% scale</div>
|
||||||
<h3 :style="h3Style()">Horizontal lockup</h3>
|
<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.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 :style="frame(C.carbon, 220)"><BrandNodeLockup :scale="1.4" :fg="C.carbon" :accent="C.signal" :wordmark="C.bone" /></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">Light surface</div>
|
<div :style="caption">Light surface</div>
|
||||||
<div :style="caption">Dark surface</div>
|
<div :style="caption">Dark surface</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -249,7 +249,7 @@ const doDont: DoDont[] = [
|
|||||||
<section :style="{ ...page, background: C.paper }">
|
<section :style="{ ...page, background: C.paper }">
|
||||||
<div :style="eyebrow">01.4 · Do · Don't</div>
|
<div :style="eyebrow">01.4 · Do · Don't</div>
|
||||||
<h2 :style="h2">How not to use it.</h2>
|
<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 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="{ background: row.bg, borderRadius: '4px', height: '180px', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative', overflow: 'hidden' }">
|
||||||
<div :style="{ transform: row.transform || 'none' }">
|
<div :style="{ transform: row.transform || 'none' }">
|
||||||
@@ -272,7 +272,7 @@ const doDont: DoDont[] = [
|
|||||||
<div :style="eyebrow">02 · Color</div>
|
<div :style="eyebrow">02 · Color</div>
|
||||||
<h1 :style="h1">Two colors do the work.</h1>
|
<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>
|
<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 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="{ 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 :style="{ fontFamily: tight, fontWeight: 600, fontSize: '36px', letterSpacing: '-0.02em' }">{{ c.name }}</div>
|
||||||
@@ -285,7 +285,7 @@ const doDont: DoDont[] = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 :style="h3Style()">Surfaces & type</h3>
|
<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 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="{ 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="{ marginTop: '12px' }">
|
||||||
@@ -296,7 +296,7 @@ const doDont: DoDont[] = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 :style="h3Style()">Semantic</h3>
|
<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 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="{ 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="{ marginTop: '12px' }">
|
||||||
@@ -313,7 +313,7 @@ const doDont: DoDont[] = [
|
|||||||
<div :style="eyebrow">02.1 · Color in use</div>
|
<div :style="eyebrow">02.1 · Color in use</div>
|
||||||
<h2 :style="h2">The 70 · 20 · 10 rule.</h2>
|
<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>
|
<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.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.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 :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>
|
<div :style="eyebrow">03 · Typography</div>
|
||||||
<h1 :style="h1">Inter Tight for voice. JetBrains Mono for evidence.</h1>
|
<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>
|
<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>
|
||||||
<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="{ marginTop: '24px' }">
|
||||||
<div :style="{ fontFamily: tight, fontWeight: 600, fontSize: '24px', letterSpacing: '-0.02em' }">Inter Tight</div>
|
<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: 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>
|
||||||
<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="{ marginTop: '24px' }">
|
||||||
<div :style="{ fontFamily: tight, fontWeight: 600, fontSize: '24px', letterSpacing: '-0.02em' }">JetBrains Mono</div>
|
<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: '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>
|
<div :style="eyebrow">03.1 · Scale</div>
|
||||||
<h2 :style="h2">One scale, ratio 1.25.</h2>
|
<h2 :style="h2">One scale, ratio 1.25.</h2>
|
||||||
<div :style="{ display: 'flex', flexDirection: 'column', gap: '18px' }">
|
<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.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: 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: 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>
|
<div :style="eyebrow">04 · Voice</div>
|
||||||
<h1 :style="h1">Direct. Lowercase. Earned.</h1>
|
<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>
|
<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>
|
||||||
<div :style="{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '20px' }">
|
<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="{ 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>
|
||||||
</div>
|
</div>
|
||||||
<h3 :style="h3Style()">Tone shifts by surface</h3>
|
<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>
|
<thead>
|
||||||
<tr :style="{ borderBottom: `1px solid ${C.carbon}` }">
|
<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>
|
<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>
|
<td :style="{ padding: '14px 0', fontFamily: inter, fontSize: '14px' }">{{ row[2] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- 05 · APPLICATIONS -->
|
<!-- 05 · APPLICATIONS -->
|
||||||
@@ -406,14 +406,14 @@ const doDont: DoDont[] = [
|
|||||||
<div :style="eyebrow">05 · Applications</div>
|
<div :style="eyebrow">05 · Applications</div>
|
||||||
<h1 :style="h1">In the world.</h1>
|
<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>
|
<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="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" />
|
<BrandNodeMark :size="170" :fg="C.carbon" :accent="C.signal" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :style="frame(C.paper, 420)">
|
<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' }">
|
<div v-for="s in [64, 32, 16]" :key="s" :style="{ textAlign: 'center' }">
|
||||||
<BrandNodeMark :size="s" :fg="C.carbon" :accent="C.signal" />
|
<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 :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>
|
||||||
</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">iOS app icon · 1024 master</div>
|
||||||
<div :style="caption">Favicon set</div>
|
<div :style="caption">Favicon set</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -431,16 +431,16 @@ const doDont: DoDont[] = [
|
|||||||
<section :style="{ ...page, background: C.bone }">
|
<section :style="{ ...page, background: C.bone }">
|
||||||
<div :style="eyebrow">05.1 · Web</div>
|
<div :style="eyebrow">05.1 · Web</div>
|
||||||
<h2 :style="h2">Marketing hero.</h2>
|
<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="{ 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' }">
|
<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" />
|
<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>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>
|
<span :style="{ background: C.signal, color: C.carbon, padding: '6px 14px', borderRadius: '4px', fontWeight: 600 }">book a demo</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ fontFamily: mono, fontSize: '12px', color: C.signal, letterSpacing: '0.08em' }">// sovereign productivity · v1.0</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>
|
Your digital workplace.<br>
|
||||||
<span :style="{ color: C.signal }">Data that stays in the EU.</span>
|
<span :style="{ color: C.signal }">Data that stays in the EU.</span>
|
||||||
</h1>
|
</h1>
|
||||||
@@ -452,16 +452,16 @@ const doDont: DoDont[] = [
|
|||||||
<section :style="{ ...page, background: C.paper }">
|
<section :style="{ ...page, background: C.paper }">
|
||||||
<div :style="eyebrow">05.2 · Social</div>
|
<div :style="eyebrow">05.2 · Social</div>
|
||||||
<h2 :style="h2">Avatars & headers.</h2>
|
<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>
|
||||||
<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" />
|
<BrandNodeMark :size="130" :fg="C.carbon" :accent="C.signal" />
|
||||||
</div>
|
</div>
|
||||||
<div :style="caption">Avatar · circular crop</div>
|
<div :style="caption">Avatar · circular crop</div>
|
||||||
</div>
|
</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="{ 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%)' }">
|
<div :style="{ position: 'absolute', right: '64px', top: '50%', transform: 'translateY(-50%)' }">
|
||||||
<BrandNodeMark :size="220" :fg="C.signal" :accent="C.carbon" />
|
<BrandNodeMark :size="220" :fg="C.signal" :accent="C.carbon" />
|
||||||
</div>
|
</div>
|
||||||
@@ -472,10 +472,10 @@ const doDont: DoDont[] = [
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Closing -->
|
<!-- 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>
|
||||||
<div :style="eyebrow">End</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>
|
Use it well.<br>
|
||||||
<span :style="{ color: C.signal }">Don't redraw it.</span>
|
<span :style="{ color: C.signal }">Don't redraw it.</span>
|
||||||
</h1>
|
</h1>
|
||||||
@@ -486,3 +486,69 @@ const doDont: DoDont[] = [
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ useHead({ title: () => `${copy.value.pages.changelog.label} · dezky` })
|
|||||||
<template>
|
<template>
|
||||||
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
<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 :style="{ maxWidth: '760px' }">
|
||||||
<div
|
<div
|
||||||
v-for="(entry, i) in c.entries" :key="i"
|
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>
|
||||||
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '20px', color: t.fg, letterSpacing: '-0.015em' }">{{ entry[0] }}</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>
|
</div>
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.changelog-entry {
|
||||||
|
grid-template-columns: 160px 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.changelog-entry {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ useHead({ title: () => `${copy.value.pages.contact.label} · dezky` })
|
|||||||
<template>
|
<template>
|
||||||
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
<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="{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: '24px', maxWidth: '760px' }">
|
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 320px), 1fr))', gap: '24px', maxWidth: '760px' }">
|
||||||
<a
|
<a
|
||||||
:href="`mailto:${c.email}`"
|
:href="`mailto:${c.email}`"
|
||||||
:style="{ display: 'block', padding: '28px', border: `1px solid ${t.border}`, borderRadius: '4px', background: t.surface }"
|
:style="{ display: 'block', padding: '28px', border: `1px solid ${t.border}`, borderRadius: '4px', background: t.surface }"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ useHead({ title: () => `${copy.value.pages.dpa.title} · dezky` })
|
|||||||
<template>
|
<template>
|
||||||
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
<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' }">
|
<div :style="{ maxWidth: '760px' }">
|
||||||
<!-- Draft / legal-review banner -->
|
<!-- 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 }">
|
<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 :style="{ border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden', margin: '0 0 40px' }">
|
||||||
<div
|
<div
|
||||||
v-for="(row, i) in c.subprocessors" :key="i"
|
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.fg, fontWeight: 600 }">{{ row[0] }}</span>
|
||||||
<span :style="{ color: t.fgMuted }">{{ row[1] }}</span>
|
<span :style="{ color: t.fgMuted }">{{ row[1] }}</span>
|
||||||
@@ -67,3 +68,14 @@ useHead({ title: () => `${copy.value.pages.dpa.title} · dezky` })
|
|||||||
</div>
|
</div>
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.subprocessor-row {
|
||||||
|
grid-template-columns: 1.2fr 1.5fr 1fr;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.subprocessor-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ useHead({ title: () => `${copy.value.pages.migration.label} · dezky` })
|
|||||||
<template>
|
<template>
|
||||||
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
||||||
|
|
||||||
<LandingContainer pad="56px 64px 80px">
|
<LandingContainer pad="clamp(56px, 8vw, 56px) clamp(20px, 5vw, 64px) clamp(56px, 8vw, 80px)">
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '40px' }">
|
<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 v-for="(step, i) in c.steps" :key="i">
|
||||||
<div :style="{ paddingTop: '20px', borderTop: `1px solid ${t.borderStrong}` }">
|
<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>
|
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', color: t.fgDim, letterSpacing: '0.06em' }">step {{ step[0] }}</div>
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
|
|||||||
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
||||||
|
|
||||||
<!-- What you get -->
|
<!-- 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="{ 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
|
<div
|
||||||
v-for="(b, i) in c.benefits" :key="i"
|
v-for="(b, i) in c.benefits" :key="i"
|
||||||
:style="{
|
:style="{
|
||||||
@@ -40,8 +40,8 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
|
|
||||||
<!-- Margin calculator -->
|
<!-- Margin calculator -->
|
||||||
<LandingContainer pad="72px 64px 0">
|
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '40px', alignItems: 'end', marginBottom: '32px' }">
|
<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>
|
<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 :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.calc.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,35 +50,40 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
|
|
||||||
<!-- CSP vs Dezky comparison -->
|
<!-- CSP vs Dezky comparison -->
|
||||||
<LandingContainer pad="72px 64px 0">
|
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '40px', alignItems: 'end', marginBottom: '32px' }">
|
<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>
|
<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 :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.compare.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
|
<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' }" />
|
||||||
<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', 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 :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>
|
||||||
<div
|
<div
|
||||||
v-for="(row, i) in c.compare.rows" :key="i"
|
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 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 :style="{ padding: '18px 24px', color: t.fgMuted }">{{ row[1] }}</div>
|
<div class="pc-val" :style="{ padding: '18px 24px', color: t.fgMuted }">
|
||||||
<div :style="{ padding: '18px 24px', color: t.fg, fontWeight: 600, background: `${t.signal}11` }">{{ row[2] }}</div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
|
|
||||||
<!-- Partner tiers -->
|
<!-- Partner tiers -->
|
||||||
<LandingContainer pad="72px 64px 0">
|
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '40px', alignItems: 'end', marginBottom: '32px' }">
|
<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>
|
<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 :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.tiers.label }}</div>
|
||||||
</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
|
<div
|
||||||
v-for="(tier, i) in c.tiers.items" :key="i"
|
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' }"
|
: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>
|
</LandingContainer>
|
||||||
|
|
||||||
<!-- How to get started -->
|
<!-- 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="{ 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 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="{ 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>
|
<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>
|
</LandingContainer>
|
||||||
|
|
||||||
<!-- Partner FAQ -->
|
<!-- Partner FAQ -->
|
||||||
<LandingContainer pad="72px 64px 0">
|
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
|
||||||
<div :style="{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: '40px', alignItems: 'end', marginBottom: '32px' }">
|
<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>
|
<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 :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.faq.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,7 +146,61 @@ useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
|
|||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
|
|
||||||
<!-- CTA -->
|
<!-- 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>
|
<LandingBtn variant="primary" size="lg" @click="goToSection('#final-cta', route.path)">{{ c.cta }} →</LandingBtn>
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</template>
|
</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>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ useHead({ title: () => `${copy.value.pages.privacy.title} · dezky` })
|
|||||||
<template>
|
<template>
|
||||||
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
<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' }">
|
<div :style="{ maxWidth: '760px' }">
|
||||||
<!-- Draft / legal-review banner -->
|
<!-- 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 }">
|
<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 :style="{ border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden', margin: '0 0 40px' }">
|
||||||
<div
|
<div
|
||||||
v-for="(row, i) in c.recipients" :key="i"
|
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.fg, fontWeight: 600 }">{{ row[0] }}</span>
|
||||||
<span :style="{ color: t.fgMuted }">{{ row[1] }}</span>
|
<span :style="{ color: t.fgMuted }">{{ row[1] }}</span>
|
||||||
@@ -67,3 +68,16 @@ useHead({ title: () => `${copy.value.pages.privacy.title} · dezky` })
|
|||||||
</div>
|
</div>
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</template>
|
</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>
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ useHead({ title: () => `${copy.value.pages.roadmap.label} · dezky` })
|
|||||||
<template>
|
<template>
|
||||||
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
<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="{ 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
|
<div
|
||||||
v-for="(col, i) in c.columns" :key="i"
|
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' }">
|
<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 }" />
|
<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>
|
</div>
|
||||||
</LandingContainer>
|
</LandingContainer>
|
||||||
</template>
|
</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>
|
||||||
|
|||||||
Reference in New Issue
Block a user