Files
dezky/apps/website/pages/partners.vue
T
Ronni Baslund d2096eb847 feat(website): book-a-demo & status pages, expand roadmap/contact
- /demo: book-a-demo page with a what-to-expect column + a form that
  composes a prefilled email to info@dezky.eu (interim, no backend); built
  to swap for a self-hosted scheduler later. Wire every "Book a demo" CTA
  (nav, hero, pricing, the previously-dead final-CTA button, and the
  contact/partners/migration/coming-soon CTAs) to /demo.
- /status: manually-maintained system-status page (overall banner,
  per-service rows, incident history). Live modules operational; Video/Chat
  marked coming soon.
- Roadmap: expand the board (5 items/column) + a "the bigger picture"
  themes grid + a "suggest a feature" CTA + a directional-timelines note.
- Contact: purpose-specific channels (info@ / legal@ / privacy@), a
  response-time note, and a company + "see it live" demo block.
- Drop /status from the [slug].vue stub map; tidy now-unused imports.
2026-06-06 19:19:19 +02:00

205 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { ref } from 'vue'
import { useTheme, useCopy } from '~/composables/useLanding'
definePageMeta({ layout: 'page' })
const t = useTheme()
const copy = useCopy()
const c = computed(() => copy.value.pages.partners)
const openFaq = ref<number | null>(0)
function toggleFaq(i: number) {
openFaq.value = openFaq.value === i ? null : i
}
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>
<!-- CTA -->
<LandingContainer pad="clamp(48px, 7vw, 72px) clamp(20px, 5vw, 64px) clamp(80px, 12vw, 160px)">
<LandingBtn variant="primary" size="lg" @click="navigateTo('/demo')">{{ c.cta }} </LandingBtn>
</LandingContainer>
</template>
<style scoped>
/* Asymmetric 2-col section headers: heading + label side-by-side on desktop, stacked on mobile */
.partners-section-header {
display: grid;
grid-template-columns: 1.1fr 1fr;
gap: 40px;
align-items: end;
margin-bottom: 32px;
}
/* Asymmetric 3-col compare table: label + csp-col + dezky-col */
.partners-compare-grid {
display: grid;
grid-template-columns: 1.3fr 1fr 1fr;
}
/* Per-cell column label only appears once the table stacks on mobile. */
.pc-key {
display: none;
}
@media (max-width: 768px) {
.partners-section-header {
grid-template-columns: 1fr;
}
.partners-compare-grid {
grid-template-columns: 1fr;
}
/* Header row redundant once each value is labelled inline. */
.partners-compare-header {
display: none;
}
.pc-cat {
padding-bottom: 4px !important;
}
.pc-val {
padding-top: 10px !important;
padding-bottom: 12px !important;
}
.pc-key {
display: block;
font-family: 'JetBrains Mono', monospace;
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: rgba(10, 10, 10, 0.5);
margin-bottom: 4px;
font-weight: 500;
}
}
</style>