Stop canonicalizing 21 translated locales into English
Every non-English page carried a hardcoded canonical to its /en twin — `export const metadata` is module-level, so it could not vary by locale and 28 pages ship...
Derive the add-on unit from the catalog, and correct stale prices
The storage add-on advertised as "+$4 /user/month" for a pool the same page called organization-wide. Cause: mapAddOns matched addonCode "additional_storage" wh...
Render Contact sales for add-ons with no list price
PageAddOn.price was typed number and built with Number(addon.pricePerUnit), so an add-on with no list price — the enterprise-negotiated origination systems — re...
Stop the runtime theme sheet whitening the canvas on light-mode machines
DynamicMetaLoader appends theme.<domain>/css/globals.css to <head> after our own stylesheet, and that file still carries create-next-app boilerplate: :root{--ba...
Submit the lead at step 1 instead of only on booking
handleFormSubmit checked the *** and advanced the step — it made no network call at all, so the contact details a visitor typed were only ever persisted if they...
Remove design-partner program page and links — no free-usage giveaways
Slug map in a plain module — client-reference proxy made server metadata render Article Not Found
500s on dynamic-segment pages + en fallback for missing locale keys
Removing the global force-dynamic made routes with generateStaticParams attempt static rendering for on-demand params, which throws DYNAMIC_SERVER_USAGE because...
Phase 3+4 — convert the traffic that already arrives
- changelog (#2 destination) gets a conversion block; careers shows the honest no-openings state - /subscribe page: post-registration plan intent lands somewher...
Related-content modules + signup CTA retargeting on programmatic templates
Solutions, integrations, alternatives, and glossary templates were organic dead ends: no internal related links and "Start free" CTAs pointing at /pricing inste...
Phase 0+1 — instrument the real funnel, unblock first paint
- forward data-* props through MagneticButton/RippleButton (events never reached the DOM) - instrument SharedNavigation, Hero, PreviewBanner, section CTAs; ?ref...
Commit outstanding work in progress
Staged and committed as-is so nothing is left uncommitted in the working tree. 4 files changed, 72 insertions(+)
Harden the kubectl download against flaky egress [skip ci]
dl.k8s.io over the runner's egress intermittently drops mid-transfer: curl: (56) OpenSSL SSL_read: decryption failed or bad record mac which fails the deploy ...
Starter capped at 5 users, Business at 25
Repositions the published seat caps on /pricing (and the calculator, JSON-LD offers, and llms.txt summaries) to match the subscription catalog: Starter 25 -> 5 ...
Replace forked chat with hosted <kamo-chat> embed; delete legacy proxy widget
Swaps the forked AI/live-support React chat (PublicChatWidget + AiChatPanel + LiveSupportPanel + the /api/chat/** server proxy routes) for the hosted, self-them...
Distinguish actively-translating vs queued messages
The translation queue was already sequential, but every queued bubble rendered "Translating…". Now the single in-flight message shows "Translating…" with a puls...
Show error icon when a message fails to translate
When the per-message translate call returns translated:false, errors out, or times out, the chat bubble now renders an AlertTriangle icon in red with tooltip "F...
Stream translations newest-first for visitor history
When the visitor lands on a non-English locale and the support chat has existing English messages, every bubble flagged needsTranslation by the backend now show...
Auto-translate visitor and agent messages
Live agent replies arriving in a language other than the visitor's URL locale now show a "Translating…" bubble while the TranslateService call resolves, then sw...
Move translations to kamo-translation-dictionary
All 22 locale files move to the centralized kamo-translation-dictionary repo. The CI now clones that repo and copies the kamo-marketing/ folder into messages/ b...
Scope concurrency to build jobs, not Kubernetes deploys
Workflow-level concurrency serialized the whole pipeline (including kubectl rollouts). Move the group to the build job only so deploy jobs from different runs c...
Periodic STOMP presence heartbeat for public support
Refresh Redis TTL for public session presence so agents see accurate online state.
Show active offer banner with live millisecond countdown
Fetches the active promotion server-side on the home page and renders a HeroOfferBanner between the subheadline and the free-forever callout. The banner shows t...
Drive minimized-tab entry through animation controls
Switching animate={...} to animate={tabControls} broke the entry animation: tabControls has no queued target on mount, so the button stayed at its initial { y: ...
Live unread badge + bounce on new agent message
- Keeps LiveSupportPanel mounted offscreen while the widget is minimized (only when a support session exists) so the STOMP subscription stays alive and onUn...
Remove SSR guard causing hydration mismatch; drop SockJS for native WebSocket
- Hero.tsx: remove typeof window check around ParticleField — ParticleField already has ssr:false so the guard produced a server/client tree mismatch (React #...
Use /public/{mainDomain}-{tld} path matching actual MinIO bucket layout
The getThemePath() was generating /domain/{tld}/{split}/kamosite paths that don't exist in MinIO — theme files live in the public bucket under kamocrm-com/, bad...
Add suppressHydrationWarning and fix broken placeholder images
suppressHydrationWarning on <html> prevents React error #418 caused by browser extensions (e.g. translate, dark mode) mutating html attributes between SSR and c...
Remove duplicate connectStomp call in sendMessage
When the first message creates a new session, onSessionCreated updates props which triggers useEffect to call connectStomp. The inline call in sendMessage was r...
Remove kamo-capcha-widget node_modules from repo
Drop tracked files under **************** so the tree matches a clean install (dependencies belong in package lock, not git).
Show live state with changelog link and daily count
Replace preview countdown and signup copy with a compact "Now LIVE" callout, keep the changelog link, and always show today’s change count from the API after it...
Render system events as friendly text in visitor support chat
NAME_CHANGED system events no longer show raw JSON in the chat bubble; they display as a centered italic pill ("Changes name to {name}"). Other system events ar...
Skip VISITOR_MESSAGE echoes in LiveSupportPanel STOMP handler
Visitor messages are now tagged messageType=VISITOR_MESSAGE by the backend. Skip these in the STOMP subscription so the visitor's own messages don't re-appear o...
Replace native WebSocket with STOMP/SockJS in LiveSupportPanel
- Add @stomp/stompjs and sockjs-client packages - Connect to media.{domain}/ws/public-chat via SockJS+STOMP instead of raw WebSocket to api.{domain} which was...
Add /contact-confirmed page with animated hero, timeline, and CTAs
Replace inline success message on contact form with a dedicated premium confirmation page: animated SVG checkmark with particle burst, 'What Happens Next' 3-ste...
Correct MEDIASERVICE_URL port from 443 to 80 for contact form
ClusterIP service only exposes port 80; using 443 with http caused a ConnectTimeoutError on every contact form submission.
Add API modification callout to kamo-api-reference page
Shown only on /kamo-api-reference, prompts users to submit API change requests via /requests or reach out via /contact.
Stabilize countdown width to prevent banner text wobble
Fixed-width monospace container with tabular-nums prevents layout shifts as countdown digits change every 50ms.
Like what you see shipping?
All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.
