BackendCombined login is all-or-nothing, and Stalwart returns 404 for
/dav/card/<account>/ when the account's default address book was never
created (it doesn't auto-create on the gateway's PROPFIND the way the
calendar home worked) — so CardDAV killed every otherwise-successful
EAS login. Exchange accounts now bundle mail + calendar; contacts stay
on the Apple-profile CardDAV path. Re-enable BackendCardDAV once
platform-api provisions a default address book at mailbox creation.
Copy/docs aligned: portal hint, SERVICES.md, website FAQ (da+en).
Stalwart never had ActiveSync built in — that now comes from the
zpush gateway. SERVICES.md gains a zpush section with debug
commands; website copy (da+en) states what actually works: IMAP
everywhere, CalDAV/CardDAV, Exchange accounts in the phone's
built-in apps, CalDAV Synchronizer for Outlook on Windows.
Add a standalone bilingual /coming-soon page (branded, dark, email signup
via mailto:info@dezky.eu, fires a waitlist-signup Umami event, noindex) plus
a global middleware that redirects every route to the locale-correct holding
page while NUXT_PUBLIC_COMING_SOON=true.
- Env-gated (default off, so dev and the live site are unaffected); flip the
env in Coolify to show/hide the holding page with no code change.
- Preview the real site behind the gate via ?preview=<previewToken>
(NUXT_PUBLIC_PREVIEW_TOKEN), which sets a 7-day cookie.
- Locale-preserving redirects (/da/* -> /da/coming-soon), no loops.
Make the marketing site deployable standalone from apps/website (the repo
has no root package.json, so the build must run in this subdir — set Base
Directory to apps/website in Coolify).
- Add a multi-stage Dockerfile (pnpm build -> node .output/server/index.mjs,
port 3000, NUXT_PUBLIC_SITE_URL default) + .dockerignore.
- Add a "start" script for the Nixpacks path (Nuxt SSR has no default start).
- Guard the bind-mount-only /shared-packages components dir with existsSync
so standalone builds don't warn on the missing path (the site uses no
shared component).
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).
Add @nuxtjs/i18n: English is the default locale (no prefix), Danish lives
under /da (prefix_except_default). Both server-rendered and indexed with
hreflang alternates + per-locale canonical (useLocaleHead in app.vue).
First-visit browser language is auto-detected and remembered in the
i18n_redirected cookie (redirectOn root).
- Keep the hand-authored COPY object; useLang/useCopy now read the i18n
locale; useLocalizeHref/useLangToggle added. Every internal link is
localized so navigation stays in-locale.
- Clear segmented EN|DA language switcher (active segment filled) replacing
the ambiguous "en · da" pill.
- SEO: useSeoMeta defaults are locale-aware; og:image switches per locale
(English / Danish share card); favicon links; robots.txt + sitemap.xml;
env-aware siteUrl/baseUrl (localhost in dev, dezky.eu in prod).
- Update the cookie policy (dezky-lang -> i18n_redirected).
- Gate the Traefik wss:443 HMR behind DEZKY_TRAEFIK so localhost dev/DevTools
connect over plain ws (fixes the DevTools disconnect loop).
Replace the bare partner CTA with a "Become a partner" section: a pitch
column (with a secondary "book a partner call" link to /demo) plus an
application form — name, company, work email, website, partnership type
(reseller / white-label / not sure) and message. On submit it composes a
prefilled email to info@dezky.eu (interim, no backend), to be swapped for
a real intake/CRM later. Bilingual and responsive.
- Write the brand name as lowercase "dezky" across all user-facing copy
(the legal entity "Dezky ApS" stays capitalised).
- Change the general contact email kontakt@dezky.eu -> info@dezky.eu.
- Footer tagline now "European" rather than only Danish business.
- Compare table header logo uses the on-dark treatment (signal-green
squircle + carbon d) instead of a low-contrast green d on a light chip.
- /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.
Extend /about with the "why": a "the problem" section (jurisdiction vs
geography, creeping prices, lock-in, vendor sprawl) as a 2x2 pain grid, and
a "we're founders ourselves" section. Both bilingual and responsive.
Also point the footer partner link to /partners (was the homepage
#whitelabel section).
Replace the /sla and /cookies stubs with real pages (da + en), matching
the DPA/terms/privacy layout. Drop both from the [slug].vue stub map.
- SLA: 99% uptime target framed as a transparency-backed commitment (no
public service credits); contractual credits offered only via Enterprise
agreements. Support first-response targets table; status-page reference.
- Cookie policy: strictly-necessary + language preference only, cookie-free
analytics (Umami), no third-party tracking, "no banner needed"; cookie
table; final (v1.0), no draft banner.
useLang now seeds from a first-party `dezky-lang` cookie on SSR init and
toggleLang writes it (12 months, SameSite=Lax). The chosen language now
survives reloads/return visits (fixes the reset-to-Danish quirk) and makes
the cookie-policy entry accurate.
Replace the /terms stub with a real Servicevilkår page (da + en),
mirroring the DPA/privacy layout and draft / legal-review banner.
- 12 clauses tailored to the system: the service, per-user accounts,
acceptable use, customer data (EU-hosted, DPA ref, export/no lock-in),
pricing & payment (49 kr/user/mo excl. VAT), term/renewal/cancellation,
uptime & SLA ref, IP, liability cap (last 12 months' fees), changes,
Danish law / venue Esbjerg
- Drop 'terms' from the [slug].vue stub map so the real page wins
- Contact: legal@dezky.eu
Still a 0.1 draft pending legal review (flagged on the page).
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
Replace the /brand stub with the full Brand Guide from the Claude Design
handoff (cover, logo, color, typography, voice, applications), rendered inside
the site layout, English-only, with illustrative copy adapted to Dezky's real
EU-sovereign voice. Extend NodeMark with a `variant` (donut/solid/outline) and
NodeLockup with a separate `wordmark` colour so the mark reads correctly on
dark surfaces. Drops the old brand stub copy.
Set TMPDIR=/tmp in the dev script so the Nuxt vite-node Unix socket path stays
under macOS's 104-char limit (fixes "Failed to restrict vite-node socket
permissions" on local runs; harmless in the Linux container). Also fix the
package.json description: dezky.com → dezky.eu.
The /partners margin calculator now derives margin from the user count using
progressive brackets (like tax brackets): first 500 users at 15%, 501–1000 at
30%, 1001+ at 40%, off the 49 kr/user/mo list price. Replaces the manual margin
slider with a live per-bracket breakdown. Rates are read from the tier copy;
tier thresholds aligned to 501 / 1.001 so the cards and calculator agree.
Add the /partners page rendering the partner pitch: benefits, an interactive
margin calculator (seats × margin → monthly/annual, off the 49 kr list price),
a reseller-facing "CSP vs Dezky" comparison, partner tiers, a 3-step "get
started", and a partner FAQ. Wire the section-06 "see the partner program"
button to it, and align the whitelabel margin bullet to 15–40%.
Wire every footer link to a real route. Adds a shared `page` layout (Nav +
content + Footer), reusable PageHeader/ComingSoon components, six content pages
(about, contact, brand, roadmap, changelog, migration), and a dynamic [slug]
catch-all for the not-yet-built pages — unknown slugs 404, legal slugs get a
distinct "contact us" body.
Footer links repointed from dead "#" to real paths; section anchors ("/#suite")
smooth-scroll on the homepage and route home + scroll from a sub-page; logo
links home. Page copy (da + en) added under COPY.pages.
Hero headline was a broken comma-splice ("Den produktivitetssuite, dine data
bliver i Danmark med."); replace with a clean two-sentence line in both
languages:
da: "Din digitale arbejdsplads. Data der bliver i EU."
en: "Your digital workplace. Data that stays in the EU."
Also move all brand references off the unowned dezky.com to dezky.eu
(status page, app dashboard mockup, config/comments). External domains
(zulip.com, Google Fonts) are left untouched.
Partner demo cards in section 06 were hardcoded Danish strings, so they
stayed Danish in EN mode. Move name + subtitle into COPY.whitelabel.partners
for both languages and render them via a mapped computed; per-card accent and
the placeholder style remain presentational config in the component.
Also harden PartnerCard's avatar-initial against an empty name to satisfy
noUncheckedIndexedAccess.
New standalone apps/website (Nuxt 4) serving the public marketing site at
dezky.local / www.dezky.local. The customer portal moves off the root domain
to app.dezky.local only.
Landing page ported from the Dezky design handoff: light theme, Danish
default, hero variant A, with a working da/en toggle. Self-contained colour
system threaded through components (utils/landingTokens.ts), full bilingual
copy (utils/landingCopy.ts), and shared state (composables/useLanding.ts).
Sections live under components/landing/* with the Node logo under
components/brand/*.
Wired into docker-compose (website service, volume, Traefik labels, network
aliases) and bootstrap.sh (hosts list + service URLs).