554cb99f2c
Add the Umami tracker (cookieless, no consent banner) in the document head, limited to the production hostnames via data-domains so dev traffic doesn't pollute the stats. Pageviews are auto-tracked per page and locale. Custom events on the key funnel: - demo-request (demo form submit, with teamSize) - partner-application (partner form submit, with type) - book-demo (every "Book a demo" CTA click) via data-umami-event - login (clicks through to the app) Also fix the mobile nav menu links, which weren't localized (would drop Danish visitors back to English).
312 lines
16 KiB
Vue
312 lines
16 KiB
Vue
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
import { useTheme, useCopy, track } from '~/composables/useLanding'
|
||
|
||
definePageMeta({ layout: 'page' })
|
||
|
||
const t = useTheme()
|
||
const copy = useCopy()
|
||
const localePath = useLocalePath()
|
||
const c = computed(() => copy.value.pages.partners)
|
||
|
||
const openFaq = ref<number | null>(0)
|
||
function toggleFaq(i: number) {
|
||
openFaq.value = openFaq.value === i ? null : i
|
||
}
|
||
|
||
// Partner application form — interim: composes a prefilled email to
|
||
// info@dezky.eu (no backend). Swap for a real intake/CRM later.
|
||
const pName = ref('')
|
||
const pCompany = ref('')
|
||
const pEmail = ref('')
|
||
const pWebsite = ref('')
|
||
const pType = ref('')
|
||
const pMessage = ref('')
|
||
|
||
function submitPartner() {
|
||
const f = c.value.form
|
||
const subject = `Partner application — ${(pCompany.value || pName.value).trim()}`
|
||
const body = [
|
||
`${f.nameLabel}: ${pName.value}`,
|
||
`${f.companyLabel}: ${pCompany.value}`,
|
||
`${f.emailLabel}: ${pEmail.value}`,
|
||
`${f.websiteLabel}: ${pWebsite.value}`,
|
||
`${f.typeLabel}: ${pType.value}`,
|
||
'',
|
||
pMessage.value,
|
||
].join('\n')
|
||
track('partner-application', { type: pType.value })
|
||
window.location.href = `mailto:info@dezky.eu?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`
|
||
}
|
||
|
||
const formEyebrow = computed(() => ({
|
||
fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.value.fgDim,
|
||
letterSpacing: '0.12em', textTransform: 'uppercase' as const,
|
||
}))
|
||
const formH2 = computed(() => ({
|
||
fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(28px, 3.6vw, 44px)',
|
||
letterSpacing: '-0.03em', lineHeight: 1.05, margin: '14px 0 0', color: t.value.fg, textWrap: 'balance' as const,
|
||
}))
|
||
const fieldLabel = computed(() => ({
|
||
display: 'block', marginBottom: '8px', fontFamily: '\'JetBrains Mono\', monospace',
|
||
fontSize: '11px', color: t.value.fgMuted, letterSpacing: '0.06em', textTransform: 'uppercase' as const,
|
||
}))
|
||
const inputStyle = computed(() => ({
|
||
width: '100%', padding: '12px 14px', border: `1px solid ${t.value.border}`, borderRadius: '4px',
|
||
background: t.value.bg, color: t.value.fg, fontFamily: '\'Inter\', sans-serif', fontSize: '15px',
|
||
boxSizing: 'border-box' as const,
|
||
}))
|
||
|
||
useHead({ title: () => `${copy.value.pages.partners.label} · dezky` })
|
||
</script>
|
||
|
||
<template>
|
||
<LandingPageHeader :label="c.label" :title="c.title" :intro="c.intro" />
|
||
|
||
<!-- What you get -->
|
||
<LandingContainer pad="clamp(40px, 6vw, 56px) clamp(20px, 5vw, 64px) 0">
|
||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: '20px' }">{{ c.benefitsLabel }}</div>
|
||
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
|
||
<div
|
||
v-for="(b, i) in c.benefits" :key="i"
|
||
:style="{
|
||
padding: '28px', background: t.surface,
|
||
borderTop: i > 1 ? `1px solid ${t.border}` : 'none',
|
||
borderLeft: i % 2 === 1 ? `1px solid ${t.border}` : 'none',
|
||
}"
|
||
>
|
||
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '20px', color: t.fg, letterSpacing: '-0.015em' }">{{ b[0] }}</div>
|
||
<p :style="{ marginTop: '10px', fontFamily: '\'Inter\', sans-serif', fontSize: '15px', lineHeight: 1.6, color: t.fgMuted, margin: '10px 0 0', textWrap: 'pretty' }">{{ b[1] }}</p>
|
||
</div>
|
||
</div>
|
||
</LandingContainer>
|
||
|
||
<!-- Margin calculator -->
|
||
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
|
||
<div class="partners-section-header">
|
||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(30px, 3.6vw, 48px)', letterSpacing: '-0.03em', lineHeight: 1.0, margin: 0, color: t.fg }">{{ c.calc.heading }}</h2>
|
||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.calc.label }}</div>
|
||
</div>
|
||
<LandingPartnerCalculator />
|
||
<p :style="{ marginTop: '16px', fontFamily: '\'Inter\', sans-serif', fontSize: '13px', color: t.fgDim }">{{ c.calc.note }}</p>
|
||
</LandingContainer>
|
||
|
||
<!-- CSP vs Dezky comparison -->
|
||
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
|
||
<div class="partners-section-header">
|
||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(30px, 3.6vw, 48px)', letterSpacing: '-0.03em', lineHeight: 1.0, margin: 0, color: t.fg }">{{ c.compare.heading }}</h2>
|
||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.compare.label }}</div>
|
||
</div>
|
||
<div :style="{ border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
|
||
<div class="partners-compare-grid partners-compare-header" :style="{ background: t.surface, borderBottom: `1px solid ${t.borderStrong}` }">
|
||
<div :style="{ padding: '18px 24px' }" />
|
||
<div :style="{ padding: '18px 24px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '13px', color: t.fgMuted }">{{ c.compare.cols[0] }}</div>
|
||
<div :style="{ padding: '18px 24px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '13px', fontWeight: 600, color: t.fg, background: `${t.signal}22` }">{{ c.compare.cols[1] }}</div>
|
||
</div>
|
||
<div
|
||
v-for="(row, i) in c.compare.rows" :key="i"
|
||
class="partners-compare-grid"
|
||
:style="{ borderTop: i === 0 ? 'none' : `1px solid ${t.border}`, fontFamily: '\'Inter\', sans-serif', fontSize: '15px' }"
|
||
>
|
||
<div class="pc-cat" :style="{ padding: '18px 24px', color: t.fgMuted, fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', letterSpacing: '0.04em', textTransform: 'uppercase', alignSelf: 'center' }">{{ row[0] }}</div>
|
||
<div class="pc-val" :style="{ padding: '18px 24px', color: t.fgMuted }">
|
||
<span class="pc-key">{{ c.compare.cols[0] }}</span>{{ row[1] }}
|
||
</div>
|
||
<div class="pc-val" :style="{ padding: '18px 24px', color: t.fg, fontWeight: 600, background: `${t.signal}11` }">
|
||
<span class="pc-key">{{ c.compare.cols[1] }}</span>{{ row[2] }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</LandingContainer>
|
||
|
||
<!-- Partner tiers -->
|
||
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
|
||
<div class="partners-section-header">
|
||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(30px, 3.6vw, 48px)', letterSpacing: '-0.03em', lineHeight: 1.0, margin: 0, color: t.fg }">{{ c.tiers.heading }}</h2>
|
||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.tiers.label }}</div>
|
||
</div>
|
||
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 240px), 1fr))', gap: '0', border: `1px solid ${t.border}`, borderRadius: '4px', overflow: 'hidden' }">
|
||
<div
|
||
v-for="(tier, i) in c.tiers.items" :key="i"
|
||
:style="{ padding: '32px 28px', background: t.surface, borderLeft: i === 0 ? 'none' : `1px solid ${t.border}`, display: 'flex', flexDirection: 'column', gap: '20px' }"
|
||
>
|
||
<div>
|
||
<div :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '20px', color: t.fg, letterSpacing: '-0.015em' }">{{ tier[0] }}</div>
|
||
<div :style="{ marginTop: '4px', fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.04em' }">{{ tier[1] }}</div>
|
||
</div>
|
||
<div :style="{ display: 'flex', alignItems: 'baseline', gap: '8px' }">
|
||
<span :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '40px', letterSpacing: '-0.03em', color: t.fg }">{{ tier[2] }}</span>
|
||
<span :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgMuted }">margin</span>
|
||
</div>
|
||
<div :style="{ display: 'flex', flexDirection: 'column', gap: '10px' }">
|
||
<div
|
||
v-for="(perk, j) in tier[3]" :key="j"
|
||
:style="{ display: 'flex', gap: '10px', fontFamily: '\'Inter\', sans-serif', fontSize: '14px', lineHeight: 1.45, color: t.fg }"
|
||
>
|
||
<span :style="{ color: t.signal, flexShrink: 0, fontWeight: 700 }">✓</span>
|
||
<span>{{ perk }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p :style="{ marginTop: '16px', fontFamily: '\'Inter\', sans-serif', fontSize: '13px', color: t.fgDim }">{{ c.tiers.note }}</p>
|
||
</LandingContainer>
|
||
|
||
<!-- How to get started -->
|
||
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
|
||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: '32px' }">{{ c.stepsLabel }}</div>
|
||
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 220px), 1fr))', gap: '40px' }">
|
||
<div v-for="(step, i) in c.steps" :key="i" :style="{ paddingTop: '20px', borderTop: `1px solid ${t.borderStrong}` }">
|
||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '12px', color: t.fgDim, letterSpacing: '0.06em' }">step {{ step[0] }}</div>
|
||
<div :style="{ marginTop: '20px', fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '24px', color: t.fg, letterSpacing: '-0.02em' }">{{ step[1] }}</div>
|
||
<p :style="{ marginTop: '12px', fontFamily: '\'Inter\', sans-serif', fontSize: '15px', lineHeight: 1.6, color: t.fgMuted, textWrap: 'pretty' }">{{ step[2] }}</p>
|
||
</div>
|
||
</div>
|
||
</LandingContainer>
|
||
|
||
<!-- Partner FAQ -->
|
||
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) 0">
|
||
<div class="partners-section-header">
|
||
<h2 :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: 'clamp(30px, 3.6vw, 48px)', letterSpacing: '-0.03em', lineHeight: 1.0, margin: 0, color: t.fg }">{{ c.faq.heading }}</h2>
|
||
<div :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '11px', color: t.fgDim, letterSpacing: '0.08em', textTransform: 'uppercase' }">{{ c.faq.label }}</div>
|
||
</div>
|
||
<div>
|
||
<div
|
||
v-for="(item, i) in c.faq.items" :key="i"
|
||
:style="{ borderTop: `1px solid ${t.border}`, borderBottom: i === c.faq.items.length - 1 ? `1px solid ${t.border}` : 'none' }"
|
||
>
|
||
<button
|
||
:style="{ width: '100%', background: 'transparent', border: 'none', padding: '24px 0', display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '24px', cursor: 'pointer', textAlign: 'left' }"
|
||
@click="toggleFaq(i)"
|
||
>
|
||
<span :style="{ fontFamily: '\'Inter Tight\', sans-serif', fontWeight: 600, fontSize: '18px', color: t.fg, letterSpacing: '-0.015em' }">{{ item[0] }}</span>
|
||
<span :style="{ fontFamily: '\'JetBrains Mono\', monospace', fontSize: '20px', color: t.fgMuted, flexShrink: 0 }">{{ openFaq === i ? '−' : '+' }}</span>
|
||
</button>
|
||
<p v-if="openFaq === i" :style="{ margin: '0', padding: '0 0 24px', maxWidth: '720px', fontFamily: '\'Inter\', sans-serif', fontSize: '15px', lineHeight: 1.6, color: t.fgMuted, textWrap: 'pretty' }">{{ item[1] }}</p>
|
||
</div>
|
||
</div>
|
||
</LandingContainer>
|
||
|
||
<!-- Become a partner -->
|
||
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
|
||
<div :style="{ borderTop: `1px solid ${t.borderStrong}`, paddingTop: 'clamp(40px, 5vw, 56px)' }">
|
||
<div class="partner-form-grid" :style="{ display: 'grid', gap: '48px', alignItems: 'start' }">
|
||
<!-- Pitch -->
|
||
<div>
|
||
<div :style="formEyebrow">{{ c.form.label }}</div>
|
||
<h2 :style="formH2">{{ c.form.heading }}</h2>
|
||
<p :style="{ marginTop: '20px', fontFamily: '\'Inter\', sans-serif', fontSize: '18px', lineHeight: 1.6, color: t.fgMuted, maxWidth: '420px', textWrap: 'pretty' }">{{ c.form.lede }}</p>
|
||
<button
|
||
:style="{ marginTop: '28px', background: 'transparent', border: 'none', padding: 0, cursor: 'pointer', fontFamily: '\'Inter\', sans-serif', fontSize: '15px', color: t.fgMuted, textDecoration: 'underline', textUnderlineOffset: '3px' }"
|
||
data-umami-event="book-demo" @click="navigateTo(localePath('/demo'))"
|
||
>{{ c.cta }} →</button>
|
||
</div>
|
||
|
||
<!-- Application form -->
|
||
<form
|
||
@submit.prevent="submitPartner"
|
||
:style="{ padding: 'clamp(24px, 4vw, 36px)', border: `1px solid ${t.border}`, borderRadius: '4px', background: t.surface, display: 'flex', flexDirection: 'column', gap: '18px' }"
|
||
>
|
||
<div>
|
||
<label :style="fieldLabel">{{ c.form.nameLabel }}</label>
|
||
<input v-model="pName" type="text" required :placeholder="c.form.namePh" :style="inputStyle">
|
||
</div>
|
||
<div>
|
||
<label :style="fieldLabel">{{ c.form.companyLabel }}</label>
|
||
<input v-model="pCompany" type="text" required :placeholder="c.form.companyPh" :style="inputStyle">
|
||
</div>
|
||
<div>
|
||
<label :style="fieldLabel">{{ c.form.emailLabel }}</label>
|
||
<input v-model="pEmail" type="email" required :placeholder="c.form.emailPh" :style="inputStyle">
|
||
</div>
|
||
<div>
|
||
<label :style="fieldLabel">{{ c.form.websiteLabel }}</label>
|
||
<input v-model="pWebsite" type="text" :placeholder="c.form.websitePh" :style="inputStyle">
|
||
</div>
|
||
<div>
|
||
<label :style="fieldLabel">{{ c.form.typeLabel }}</label>
|
||
<select v-model="pType" :style="{ ...inputStyle, appearance: 'none', cursor: 'pointer' }">
|
||
<option value="" disabled>{{ c.form.typePh }}</option>
|
||
<option v-for="ty in c.form.types" :key="ty" :value="ty">{{ ty }}</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label :style="fieldLabel">{{ c.form.messageLabel }}</label>
|
||
<textarea v-model="pMessage" rows="3" :placeholder="c.form.messagePh" :style="{ ...inputStyle, resize: 'vertical' }" />
|
||
</div>
|
||
|
||
<button
|
||
type="submit"
|
||
:style="{ marginTop: '4px', background: t.signal, color: t.fg, border: 'none', padding: '16px 24px', fontFamily: '\'Inter\', sans-serif', fontSize: '15px', fontWeight: 600, borderRadius: '4px', cursor: 'pointer', letterSpacing: '-0.005em' }"
|
||
>{{ c.form.submit }} →</button>
|
||
|
||
<p :style="{ margin: 0, fontFamily: '\'Inter\', sans-serif', fontSize: '13px', lineHeight: 1.5, color: t.fgDim }">{{ c.form.note }}</p>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</LandingContainer>
|
||
</template>
|
||
|
||
<style scoped>
|
||
/* Asymmetric 2-col section headers: heading + label side-by-side on desktop, stacked on mobile */
|
||
.partners-section-header {
|
||
display: grid;
|
||
grid-template-columns: 1.1fr 1fr;
|
||
gap: 40px;
|
||
align-items: end;
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
/* Asymmetric 3-col compare table: label + csp-col + dezky-col */
|
||
.partners-compare-grid {
|
||
display: grid;
|
||
grid-template-columns: 1.3fr 1fr 1fr;
|
||
}
|
||
|
||
/* Become-a-partner: pitch + form side-by-side, stacked on mobile */
|
||
.partner-form-grid {
|
||
grid-template-columns: 0.85fr 1.15fr;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
.partner-form-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>
|