diff --git a/astro-site/public/fonts/DepartureMono-LICENSE.txt b/astro-site/public/fonts/DepartureMono-LICENSE.txt
new file mode 100644
index 00000000..de524761
--- /dev/null
+++ b/astro-site/public/fonts/DepartureMono-LICENSE.txt
@@ -0,0 +1,93 @@
+Copyright 2022–2024 Helena Zhang (helenazhang.com).
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/astro-site/public/fonts/DepartureMono-Regular.woff2 b/astro-site/public/fonts/DepartureMono-Regular.woff2
new file mode 100644
index 00000000..b4a23dc1
Binary files /dev/null and b/astro-site/public/fonts/DepartureMono-Regular.woff2 differ
diff --git a/astro-site/public/images/stamp-portrait.webp b/astro-site/public/images/stamp-portrait.webp
new file mode 100644
index 00000000..11c663cc
Binary files /dev/null and b/astro-site/public/images/stamp-portrait.webp differ
diff --git a/astro-site/public/images/stickers/demo-nights.webp b/astro-site/public/images/stickers/demo-nights.webp
new file mode 100644
index 00000000..9ddaf00c
Binary files /dev/null and b/astro-site/public/images/stickers/demo-nights.webp differ
diff --git a/astro-site/public/images/stickers/filter-coffee.svg b/astro-site/public/images/stickers/filter-coffee.svg
new file mode 100644
index 00000000..6ecd5e90
--- /dev/null
+++ b/astro-site/public/images/stickers/filter-coffee.svg
@@ -0,0 +1,9 @@
+
diff --git a/astro-site/public/images/stickers/goodreads.svg b/astro-site/public/images/stickers/goodreads.svg
new file mode 100644
index 00000000..43d9eb1c
--- /dev/null
+++ b/astro-site/public/images/stickers/goodreads.svg
@@ -0,0 +1,3 @@
+
diff --git a/astro-site/public/images/stickers/letterboxd.svg b/astro-site/public/images/stickers/letterboxd.svg
new file mode 100644
index 00000000..fdcfac45
--- /dev/null
+++ b/astro-site/public/images/stickers/letterboxd.svg
@@ -0,0 +1,34 @@
+
+
\ No newline at end of file
diff --git a/astro-site/src/components/LetterPanel.astro b/astro-site/src/components/LetterPanel.astro
new file mode 100644
index 00000000..1948f75e
--- /dev/null
+++ b/astro-site/src/components/LetterPanel.astro
@@ -0,0 +1,64 @@
+---
+import { Image } from 'astro:assets';
+
+/** One panel of the unfolded letter: an icon, an underlined label, its entries. */
+interface Props {
+ label: string;
+ href: string;
+ icon: 0 | 1 | 2;
+ items: {
+ title: string;
+ meta: string;
+ url: string;
+ image?: any;
+ imageUrl?: string | null;
+ mono?: string;
+ external?: boolean;
+ }[];
+}
+
+const { label, href, icon, items } = Astro.props;
+---
+
+
diff --git a/astro-site/src/content/writing/toko-poster/index.md b/astro-site/src/content/writing/toko-poster/index.md
index 7b101c16..1cc8967a 100644
--- a/astro-site/src/content/writing/toko-poster/index.md
+++ b/astro-site/src/content/writing/toko-poster/index.md
@@ -48,7 +48,7 @@ Getting a Midjourney Pro account, helped fill this gap. The team didn't need to
## Speeding up a feature
-Around the same time, [Juneza](https://www.junezaniyazi.com) was working with the retailers and noticed an un-met iser need. Our retailers needed help marketing their store and with liquidating their stock. Solving this problem was important to us as we were about to launch our own Agri-brand; Jivaprodi.
+Around the same time, [Juneza](https://www.junezaniyazi.com) was working with the retailers and noticed an un-met user need. Our retailers needed help marketing their store and with liquidating their stock. Solving this problem was important to us as we were about to launch our own Agri-brand; Jivaprodi.

diff --git a/astro-site/src/pages/index.astro b/astro-site/src/pages/index.astro
index 469d67ae..47a95248 100644
--- a/astro-site/src/pages/index.astro
+++ b/astro-site/src/pages/index.astro
@@ -1,221 +1,2193 @@
---
-import BaseLayout from '../layouts/BaseLayout.astro';
import { getCollection } from 'astro:content';
import { Image } from 'astro:assets';
+import PostHog from '../components/posthog.astro';
-// Get all published writing posts
-const internalWritings = await getCollection('writing', ({ data }) => {
- return data.status === 'published';
-});
+// This page is its own document rather than a BaseLayout child: it owns the
+// whole viewport, has no container or site chrome, and paints its own footer.
+// So the head BaseLayout would have given it is assembled here instead — same
+// canonical, card and schema contract, or the home page would lose the SEO the
+// rest of the site has.
+const siteUrl = 'https://kenneth.dsouza.im';
+const canonicalUrl = `${siteUrl}/`;
+const pageTitle = 'Kenneth Mark Dsouza — Product Design Manager';
+const pageDescription =
+ 'I design digital products, explore emerging tech and build things that live on the internet. Product Design Manager with experience building and leading design teams in India and Indonesia.';
+const cardImage = new URL('/images/profile.png', siteUrl).href;
+const jsonLd = {
+ '@context': 'https://schema.org',
+ '@graph': [
+ {
+ '@type': 'Person',
+ '@id': `${siteUrl}/#person`,
+ name: 'Kenneth Mark Dsouza',
+ url: `${siteUrl}/`,
+ image: cardImage,
+ jobTitle: 'Product Design Manager',
+ description:
+ 'Product Design Manager with experience building and leading design teams in India and Indonesia.',
+ sameAs: ['https://x.com/kenneth', 'https://bsky.app/profile/ken.cv', 'https://github.com/inosaint'],
+ },
+ {
+ '@type': 'WebSite',
+ '@id': `${siteUrl}/#website`,
+ url: `${siteUrl}/`,
+ name: 'Kenneth Mark Dsouza',
+ description: pageDescription,
+ publisher: { '@id': `${siteUrl}/#person` },
+ inLanguage: 'en',
+ },
+ {
+ '@type': 'WebPage',
+ '@id': canonicalUrl,
+ url: canonicalUrl,
+ name: pageTitle,
+ description: pageDescription,
+ isPartOf: { '@id': `${siteUrl}/#website` },
+ about: { '@id': `${siteUrl}/#person` },
+ },
+ ],
+};
+
+// --- Sheet 1: Work -------------------------------------------------------
+const work = (await getCollection('work', ({ data }) => data.status === 'published'))
+ .sort((a, b) => parseInt(b.data.year.split('-')[0]) - parseInt(a.data.year.split('-')[0]))
+ .slice(0, 3)
+ .map((item) => ({
+ title: item.data.title,
+ meta: item.data.year,
+ url: `/work/${item.id}`,
+ image: item.data.image ?? null,
+ imageUrl: null as string | null,
+ external: false,
+ }));
+
+// --- Sheet 2: Writing ----------------------------------------------------
+// A short external list mirrors the homepage; the study only needs the newest few.
const externalWritings = [
{
title: 'Squads as a Growth Engine for Designers',
- description: 'How we used squads to help our designers level up at Jiva',
url: 'https://medium.com/notes-from-the-fields/how-we-used-squads-to-help-our-designers-level-up-443fcbc52e2f',
- image: 'https://miro.medium.com/v2/resize:fit:1400/format:webp/1*iuV8e1TmjIe0zsr8yQfXyA.jpeg',
- date: new Date('2025-08-18')
+ imageUrl: 'https://miro.medium.com/v2/resize:fit:1400/format:webp/1*iuV8e1TmjIe0zsr8yQfXyA.jpeg',
+ date: new Date('2025-08-18'),
},
{
title: 'Redesigning the Sahabat Jiva app',
- description: 'From Field Insights to Better UX for our Sahabat Jivas',
url: 'https://medium.com/notes-from-the-fields/from-field-insights-to-better-ux-for-our-sahabat-jivas-3056b32ba51f',
- image: 'https://miro.medium.com/v2/resize:fit:1400/format:webp/1*3Fnwkn7IeOiYbTxz-n3K7w.jpeg',
- date: new Date('2025-05-15')
+ imageUrl: 'https://miro.medium.com/v2/resize:fit:1400/format:webp/1*3Fnwkn7IeOiYbTxz-n3K7w.jpeg',
+ date: new Date('2025-05-15'),
},
{
title: 'How a sticker design became an unofficial Mascot',
- description: 'The story of how a last-minute sticker design became a beloved company icon',
url: 'https://medium.com/notes-from-the-fields/more-than-swag-how-a-last-minute-sticker-became-a-company-icon-42d86246e736',
- image: 'https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Nq0RLdn32pPVk-zLPFy-Cw.png',
- date: new Date('2024-11-28')
- },
- {
- title: 'Designing the Akar Design System',
- description: 'Taming the chaos of hypergrowth with a lean approach to building a design system',
- url: 'https://medium.com/notes-from-the-fields/taming-the-chaos-of-hypergrowth-our-lean-approach-to-building-a-design-system-part-1-2adbb24ddef0',
- image: 'https://miro.medium.com/v2/resize:fit:4800/format:webp/1*L1SeHFlgAyu3LkCQXR252g.png',
- date: new Date('2024-10-17')
+ imageUrl: 'https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Nq0RLdn32pPVk-zLPFy-Cw.png',
+ date: new Date('2024-11-28'),
},
- {
- title: 'Gamify DesignOps with human connection',
- description: 'A talk about bringing human connection into DesignOps',
- url: 'https://www.youtube.com/watch?v=c__raLe34ZU',
- image: '/images/sddefault.jpg',
- date: new Date('2023-08-9')
- },
- {
- title: 'How Gojek\'s design & research teams collaborate effectively across borders',
- description: 'Insights on cross-border collaboration',
- url: 'https://www.gojek.io/blog/near-far-wherever-we-are',
- image: 'https://blog.gojek.io/content/images/2020/10/Blog-banners_Design-research-05.jpg',
- date: new Date('2020-09-08')
- },
- {
- title: 'What every young designer should know',
- description: 'Advice for product designers fresh out of college or the first hire to a company',
- url: 'https://uxdesign.cc/what-every-young-designer-should-know-abb2af79f43e',
- image: 'https://miro.medium.com/v2/resize:fit:4800/format:webp/1*qVDD_s14Q9ZZDP9ZXZ51Nw.jpeg',
- date: new Date('2016-07-25')
- }
];
-// Combine and sort all writings by date (newest first)
-const allWritings = [
- ...internalWritings.map((post) => ({
+const writing = [
+ ...(await getCollection('writing', ({ data }) => data.status === 'published')).map((post) => ({
title: post.data.title,
- description: post.data.description,
- url: `/writing/${post.id}/`,
- image: post.data.image,
- imageUrl: post.data.image ? null : 'https://placehold.co/450x250',
date: new Date(post.data.date),
- isInternal: true
+ url: post.data.url ?? `/writing/${post.id}/`,
+ image: post.data.image ?? null,
+ imageUrl: null as string | null,
+ external: Boolean(post.data.url),
})),
- ...externalWritings.map((post) => ({
- ...post,
- imageUrl: post.image,
- isInternal: false
- }))
-].sort((a, b) => b.date.getTime() - a.date.getTime());
-
-// Get the 2 most recent posts for the home page
-const recentWritings = allWritings.slice(0, 2);
-
-// Get all published work and sort by year (newest first)
-const allWork = await getCollection('work', ({ data }) => {
- return data.status === 'published';
-});
-const sortedWork = allWork.sort((a, b) => {
- // Extract the starting year from the year string (e.g., "2023-2024" -> 2023)
- const yearA = parseInt(a.data.year.split('-')[0]);
- const yearB = parseInt(b.data.year.split('-')[0]);
- return yearB - yearA;
-});
-
-// Get the 2 most recent work items for the home page
-const work = sortedWork.slice(0, 2);
-
-// Date formatter function
-const formatDate = (date: Date) => {
- return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
+ ...externalWritings.map((post) => ({ ...post, image: null, external: true })),
+]
+ .sort((a, b) => b.date.getTime() - a.date.getTime())
+ .slice(0, 3)
+ .map((post) => ({
+ title: post.title,
+ // Day-month-year: a letter is dated, not just filed under a month.
+ meta: post.date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }),
+ url: post.url,
+ image: post.image,
+ imageUrl: post.imageUrl,
+ external: post.external,
+ }));
+
+// --- Sheet 3: Projects ---------------------------------------------------
+// Pulled at build time from the AI playground, which is the canonical list and
+// lives outside this repo. Its tiles are newest-first and carry everything the
+// sheet needs on the anchor itself, so a regex over the markup is enough — no
+// parser, and no feed to keep in sync. If the fetch fails the build must not,
+// so a hand-written trio stands in.
+//
+// Every setting for that pull lives here. Note the page is static: `live` is
+// read when the site is BUILT, so the letter shows whatever the playground held
+// at deploy time and only refreshes on the next build.
+const PROJECTS = {
+ /** Where the tiles come from. */
+ url: 'https://kenneth.dsouza.im/ai/',
+ /** How many to show. The letter is sized for three. */
+ count: 3,
+ /** false → skip the network entirely and use the hand-written trio below. */
+ live: true,
+ /** false → leave art-less tiles on their category glyph instead of going to
+ * the project's own page for an og:image. One fetch per art-less tile. */
+ fetchMissingArt: true,
+ /** Give up on any single request after this. */
+ timeoutMs: 8000,
+};
+
+const AI_PROJECTS_URL = PROJECTS.url;
+
+// One icon set, drawn in a single thin-stroke vocabulary, used twice: once at
+// the head of each letter, and again as the stand-in when an entry has no
+// artwork. Each viewBox is cropped to the drawing's stroke-inclusive bounds
+// (with half a unit of slack), so the box's foot IS the glyph's foot — which is
+// what lets the heading icons sit on the type's baseline. --vw/--vh carry those
+// units so a single scale sizes every icon without distorting any of them.
+//
+// The three letter ids come first; the rest are the categories the AI page tags
+// its tiles with, so a project falls back to its own kind before its letter's.
+const ICONS: Record = {
+ work: { box: '1.5 3 21 17.5', vw: 21, vh: 17.5, d: ['M3 7.5h18v12H3z', 'M8.5 7.5v-2a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v2'] },
+ writing: { box: '2.5 2.5 19 18.5', vw: 19, vh: 18.5, d: ['M4 20l4.5-1.2L20 7.3l-3.3-3.3L5.2 15.5z', 'M15.6 5.1l3.3 3.3'] },
+ projects: { box: '1.5 1.5 21 20.5', vw: 21, vh: 20.5, d: ['M12 3l2.2 6.8L21 12l-6.8 2.2L12 21l-2.2-6.8L3 12l6.8-2.2z'] },
+ event: { box: '3 3 18 18', vw: 18, vh: 18, d: ['M3.5 6h17v14h-17z', 'M3.5 11h17', 'M8 3.5v5', 'M16 3.5v5'] },
+ app: { box: '4 4 16 16', vw: 16, vh: 16, d: ['M4.5 4.5h6v6h-6z', 'M13.5 4.5h6v6h-6z', 'M4.5 13.5h6v6h-6z', 'M13.5 13.5h6v6h-6z'] },
+ exploration: { box: '2.5 2.5 19 19', vw: 19, vh: 19, d: ['M12 3.5a8.5 8.5 0 1 0 0 17 8.5 8.5 0 0 0 0-17z', 'M15.5 8.5l-2 5-5 2 2-5z'] },
+ visualization: { box: '3 8 18 13', vw: 18, vh: 13, d: ['M4 20h16', 'M7.5 20v-7', 'M12 20v-11', 'M16.5 20v-4'] },
+ game: { box: '2 7 20 10', vw: 20, vh: 10, d: ['M7 8h10a4 4 0 0 1 0 8H7a4 4 0 0 1 0-8z', 'M9.5 10.5v3', 'M8 12h3', 'M15.5 11h.01', 'M17 13.5h.01'] },
};
+
+type SheetItem = {
+ title: string;
+ meta: string;
+ url: string;
+ mono?: string;
+ cat?: string;
+ image: any;
+ imageUrl: string | null;
+ external: boolean;
+};
+
+const projectsFallback: SheetItem[] = [
+ { title: 'AI Projects', meta: 'Tinkering', url: AI_PROJECTS_URL, cat: 'app', image: null, imageUrl: null, external: true },
+ { title: 'Design Demo Nights', meta: 'Community', url: 'https://designdemonights.com', cat: 'event', image: null, imageUrl: null, external: true },
+ { title: 'Travelogues', meta: 'Writing', url: 'https://aninosaintlife.wordpress.com/category/travels/', cat: 'exploration', image: null, imageUrl: null, external: true },
+];
+
+const decode = (str: string) =>
+ str
+ .replace(/'|'/g, '\u2019')
+ .replace(/"/g, '"')
+ .replace(/’/g, '\u2019')
+ .replace(/&/g, '&');
+
+// Two initials, the same shape as the monograms above, for tiles with no art.
+const monogram = (title: string) =>
+ title
+ .replace(/[^A-Za-z ]/g, '')
+ .split(/\s+/)
+ .filter(Boolean)
+ .slice(0, 2)
+ .map((w) => w[0].toUpperCase())
+ .join('') || 'AI';
+
+async function fetchAiProjects(): Promise {
+ const res = await fetch(AI_PROJECTS_URL, { signal: AbortSignal.timeout(PROJECTS.timeoutMs) });
+ if (!res.ok) throw new Error(`${res.status} from ${AI_PROJECTS_URL}`);
+ const html = await res.text();
+
+ const all: SheetItem[] = [];
+ for (const tile of html.match(/]*class="tile"[\s\S]*?<\/a>/g) ?? []) {
+ const attr = (name: string) => tile.match(new RegExp(`${name}="([^"]*)"`))?.[1] ?? null;
+ const href = attr('href');
+ const title = tile.match(/([^<]*)<\/span>/)?.[1];
+ // Placeholder tiles point at '#'; they have nothing to open.
+ if (!href || href === '#' || !title) continue;
+
+ const img = attr('data-img');
+ const cat = attr('data-cat') ?? '';
+ all.push({
+ title: decode(title),
+ meta: cat ? cat[0].toUpperCase() + cat.slice(1) : 'Project',
+ url: new URL(href, AI_PROJECTS_URL).href,
+ cat,
+ image: null,
+ imageUrl: img ? new URL(img, AI_PROJECTS_URL).href : null,
+ external: true,
+ });
+ }
+
+ // Newest first, exactly as the page orders them.
+ const chosen = all.slice(0, PROJECTS.count);
+
+ // Not every tile carries art. Rather than pick different projects, ask the
+ // ones that don't for their own og:image — a monogram beside two photographs
+ // reads as a gap. Failures fall back to the monogram, so this can only add.
+ await Promise.all(
+ chosen.map(async (item) => {
+ if (item.imageUrl || !PROJECTS.fetchMissingArt) return;
+ try {
+ const page = await fetch(item.url, { signal: AbortSignal.timeout(PROJECTS.timeoutMs) });
+ if (!page.ok) return;
+ const og = (await page.text()).match(
+ /]*property="og:image"[^>]*content="([^"]+)"/i
+ )?.[1];
+ if (og) {
+ item.imageUrl = new URL(decode(og), item.url).href;
+ }
+ } catch {
+ /* keep the monogram */
+ }
+ })
+ );
+
+ if (!chosen.length) throw new Error('no tiles matched');
+ return chosen;
+}
+
+let projects = projectsFallback.slice(0, PROJECTS.count);
+if (PROJECTS.live) {
+ try {
+ projects = await fetchAiProjects();
+ } catch (err) {
+ console.warn(`[home] AI projects fetch failed, using fallback: ${err}`);
+ }
+}
+
+const sheets = [
+ { id: 'work', label: 'Work', href: '/work', items: work },
+ { id: 'writing', label: 'Writing', href: '/writing', items: writing },
+ { id: 'projects', label: 'Projects', href: 'https://kenneth.dsouza.im/ai/', items: projects },
+];
+
+// --- Stickers: interests, lifted from the resume -------------------------
+const stickers = [
+ { id: 'coffee', emoji: '☕️', label: 'Filter coffee', url: 'https://www.instagram.com/filtercoffeeconnoisseur/', art: '/images/stickers/filter-coffee.svg', artW: 82, artH: 66, tip: '@filtercoffeeconnoisseur' },
+ { id: 'books', emoji: '📚', label: 'Reading', url: 'https://www.goodreads.com/user/show/86283809-kenneth-dsouza', art: '/images/stickers/goodreads.svg', artW: 132, artH: 33, tip: 'What I\u2019m reading' },
+ { id: 'nights', emoji: '🌙', label: 'Demo Nights', url: 'https://designdemonights.com', art: '/images/stickers/demo-nights.webp', artW: 96, artH: 106, tip: 'designdemonights.com' },
+ { id: 'films', emoji: '🎞️', label: 'Letterboxd', url: 'https://letterboxd.com/papercutx/', art: '/images/stickers/letterboxd.svg', artW: 64, artH: 64, tip: 'What I\u2019m watching' },
+];
---
-
-
-
-
Born and brought up in the coastal city of Mangalore in a Roman Catholic household, I pursued a career as an engineer until, by a spot of luck, I came across the field of design. After a Master's degree in Information and Interface Design from the National Institute of Design, I began my practice as a digital product designer.
+ Recently, I was a senior design manager at Jiva (opens in new tab) (2022-2025), an Agritech startup, where I built and led the product design and research team, working on a suite of mobile apps and web apps for users in rural Indonesia. You can read about the work we did in our Medium publication, Notes from the Fields (opens in new tab).
+
+
+ As a product design leader, I have built remote-first, diverse teams across countries. I believe in building a culture where you can feel supported while being yourself. I believe in learning by doing, even in failure. And that good design isn't possible without a sponsor.
+