{"id":2319,"date":"2026-08-28T08:21:52","date_gmt":"2026-08-28T08:21:52","guid":{"rendered":"https:\/\/dominhtunglam.com\/?page_id=2319"},"modified":"2026-08-28T08:29:48","modified_gmt":"2026-08-28T08:29:48","slug":"one-showcase-clone","status":"publish","type":"page","link":"https:\/\/dominhtunglam.com\/?page_id=2319","title":{"rendered":"Munchi App Showcase"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"2319\" class=\"elementor elementor-2319\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ea71564 e-con-full e-flex e-con e-parent\" data-id=\"ea71564\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c8f7f3c elementor-widget elementor-widget-html\" data-id=\"c8f7f3c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- Munchi Case Study: GLOBAL STYLES. Paste into a SINGLE HTML widget at the top of the page. -->\n<!-- Identical to the Talora\/ONE base tokens (portfolio's teal brand #22797F) plus new blocks at the end: .tlcs-hero-icon, .tlcs-swatches, .tlcs-status-list, .tlcs-fact-strip, .tlcs-video, .tlcs-shot-grid\/.tlcs-shot-phone. -->\n<!-- If the site already has Talora\/ONE's 00-global-styles.html pasted site-wide: just add the MUNCHI ADDITIONS block at the end of this file, no need to paste the whole thing again. -->\n<style>\n  \/* ============ TALORA WEB ADMIN TOKENS (synced from Figma DS) \u2014 d\u00f9ng chung cho to\u00e0n b\u1ed9 case study trong portfolio ============ *\/\n  .tlcs {\n    \/* ground \u2014 light (content area c\u1ee7a site l\u00e0 n\u1ec1n tr\u1eafng) *\/\n    --bg: #ffffff;              \/* background\/primary\/default *\/\n    --surface: #f8fafc;         \/* background\/secondary \u2014 gray blue\/50 *\/\n    --surface-2: #f1f5f9;       \/* gray blue\/100 *\/\n    --tint: #f2fbfa;            \/* background\/brand-subtle \u2014 turquoise blue\/50 *\/\n    --tint-2: #d3f4f2;          \/* turquoise blue\/100 *\/\n    --ink: #09090b;             \/* text\/primary *\/\n    --ink-2: #64748b;           \/* text\/secondary *\/\n    --ink-3: #94a3b8;           \/* muted \u2014 gray blue\/400 *\/\n    --line: #e2e8f0;            \/* border\/secondary \u2014 gray blue\/200 *\/\n    --line-strong: #cbd5e1;     \/* border\/primary \u2014 gray blue\/300 *\/\n\n    \/* brand \u2014 turquoise blue *\/\n    --brand-300: #74d3d4;\n    --brand-500: #2e989e;\n    --brand: #22797f;           \/* background\/brand\/default \u2014 turquoise blue\/600 *\/\n    --brand-700: #1f6066;\n    --brand-800: #1d4d52;\n\n    \/* status (Web Admin DS) *\/\n    --ok: #417d0e;      --ok-subtle: #f5fee7;\n    --warn: #fc6c00;    --warn-subtle: #fff9ec;\n    --danger: #dc2626;  --danger-subtle: #fef2f2;\n    --info: #0687ff;    --info-subtle: #edf9ff;\n\n    \/* brand cover world (hero\/footer) \u2014 from Figma cover *\/\n    --cover-bg: #1a1a1a;\n    --cover-ink: #ffffff;\n    --cover-ink-2: #a1a1aa;\n    --logo-teal: #63c2c2;\n    --logo-teal-deep: #338291;\n    --logo-cyan: #b4dee3;\n    --logo-purple: #915cb9;\n    --logo-purple-light: #d1b3e7;\n\n    --radius: 8px;              \/* radius m *\/\n    --radius-sm: 6px;           \/* radius s *\/\n\n    --font: -apple-system, BlinkMacSystemFont, \"SF Pro Display\", \"SF Pro Text\", \"Segoe UI\", system-ui, sans-serif;\n    --mono: \"SF Mono\", ui-monospace, \"Cascadia Code\", Menlo, Consolas, monospace;\n  }\n\n\n  \/* ============ BASE ============ *\/\n  .tlcs {\n    font-family: var(--font);\n    background: transparent;\n    color: var(--ink);\n    line-height: 1.6;\n    font-size: 16px;\n    -webkit-font-smoothing: antialiased;\n  }\n  .tlcs a { color: var(--brand); text-decoration: none; }\n  .tlcs a:hover { color: var(--brand-700); text-decoration: underline; }\n  .tlcs a:focus-visible, .tlcs button:focus-visible {\n    outline: 2px solid var(--brand);\n    outline-offset: 2px;\n    border-radius: 2px;\n  }\n  .tlcs ::selection { background: #d3f4f2; color: #1d4d52; }\n\n  .tlcs-wrap { max-width: 1080px; margin: 0 auto; padding: 0 32px; }\n  @media (max-width: 640px) { .tlcs-wrap { padding: 0 20px; } }\n\n\n  .tlcs-eyebrow {\n    font-family: var(--mono);\n    font-size: 12px;\n    letter-spacing: 0.14em;\n    text-transform: uppercase;\n    color: var(--brand);\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    margin-bottom: 20px;\n  }\n  .tlcs-eyebrow::after { content: \"\"; flex: 1; height: 1px; background: var(--line); }\n\n  .tlcs h2 {\n    font-size: clamp(26px, 3.4vw, 36px);\n    font-weight: 600;\n    letter-spacing: -0.02em;\n    line-height: 1.15;\n    text-wrap: balance;\n    margin: 0 0 16px;\n  }\n  .tlcs h3 { font-size: 19px; font-weight: 600; letter-spacing: -0.01em; margin: 0 0 8px; }\n  .tlcs-lede { font-size: 17px; color: var(--ink-2); max-width: 62ch; margin: 0 0 40px; }\n  .tlcs p { margin: 0 0 14px; }\n\n  .tlcs-num { font-variant-numeric: tabular-nums; }\n\n  \/* ============ HERO \u2014 brand cover world ============ *\/\n  .tlcs-hero {\n    background: var(--cover-bg);\n    color: var(--cover-ink);\n    padding: 72px 0 80px;\n    position: relative;\n    overflow: hidden;\n  }\n  .tlcs-hero::before {\n    \/* faint chart-grid, a nod to nav charts *\/\n    content: \"\";\n    position: absolute; inset: 0;\n    background-image:\n      linear-gradient(rgba(99,194,194,.06) 1px, transparent 1px),\n      linear-gradient(90deg, rgba(99,194,194,.06) 1px, transparent 1px);\n    background-size: 48px 48px;\n    pointer-events: none;\n  }\n  .tlcs-hero::after {\n    \/* gradient rule from the logo facets *\/\n    content: \"\";\n    position: absolute; left: 0; right: 0; bottom: 0; height: 3px;\n    background: linear-gradient(90deg, var(--logo-purple), var(--logo-teal) 45%, var(--logo-cyan));\n  }\n  .tlcs-hero .tlcs-wrap { position: relative; }\n  .tlcs-hero .tlcs-logo { width: 170px; height: auto; display: block; margin-bottom: 64px; }\n  .tlcs-hero-tag {\n    font-family: var(--mono);\n    font-size: 12px;\n    letter-spacing: 0.16em;\n    text-transform: uppercase;\n    color: var(--logo-teal);\n    margin-bottom: 24px;\n    display: inline-flex;\n    align-items: center;\n    gap: 10px;\n  }\n  .tlcs-hero-tag::before { content: \"\"; width: 28px; height: 1px; background: var(--logo-teal); }\n  .tlcs-hero h1 {\n    font-size: clamp(40px, 6.5vw, 72px);\n    font-weight: 600;\n    letter-spacing: -0.03em;\n    line-height: 1.02;\n    margin: 0 0 20px;\n    text-wrap: balance;\n    color: #fff;\n  }\n  .tlcs-hero h1 .tlcs-accent { color: var(--logo-teal); }\n  .tlcs-hero .tlcs-sub {\n    font-size: clamp(17px, 2vw, 20px);\n    color: var(--cover-ink-2);\n    max-width: 58ch;\n    margin: 0 0 40px;\n  }\n  .tlcs-hero-meta { display: flex; flex-wrap: wrap; gap: 10px; }\n  .tlcs-hero-meta span {\n    font-family: var(--mono);\n    font-size: 12.5px;\n    letter-spacing: 0.04em;\n    padding: 7px 14px;\n    border: 1px solid rgba(99,194,194,.35);\n    border-radius: 999px;\n    color: var(--logo-cyan);\n  }\n\n  \/* ============ SNAPSHOT ============ *\/\n  .tlcs-snapshot {\n    border: 1px solid var(--line);\n    border-radius: var(--radius);\n    overflow: hidden;\n    display: grid;\n    grid-template-columns: repeat(6, 1fr);\n    background: var(--line);\n    gap: 1px;\n    margin: 0;\n  }\n  .tlcs-snapshot > div { background: var(--bg); padding: 22px 20px; }\n  .tlcs-snapshot dt {\n    font-family: var(--mono);\n    font-size: 11px;\n    letter-spacing: 0.12em;\n    text-transform: uppercase;\n    color: var(--ink-3);\n    margin-bottom: 8px;\n  }\n  .tlcs-snapshot dd { margin: 0; font-size: 15px; font-weight: 500; line-height: 1.45; }\n  @media (max-width: 900px) { .tlcs-snapshot { grid-template-columns: repeat(3, 1fr); } }\n  @media (max-width: 560px) { .tlcs-snapshot { grid-template-columns: repeat(2, 1fr); } }\n\n  \/* ============ SCREENSHOTS ============ *\/\n  .tlcs-shot {\n    margin: 36px 0 0;\n    border: 1px solid var(--line);\n    border-radius: var(--radius);\n    overflow: hidden;\n    background: var(--surface);\n    box-shadow: 0 1px 3px rgba(9,9,11,.06);\n  }\n  .tlcs-shot img { display: block; width: 100%; height: auto; }\n  .tlcs-shot figcaption {\n    font-family: var(--mono); font-size: 12px; letter-spacing: .08em; text-transform: uppercase;\n    color: var(--ink-3); padding: 12px 16px; border-top: 1px solid var(--line);\n  }\n\n  \/* ============ TWO-TIER DIAGRAM ============ *\/\n  .tlcs-tiers { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 36px; }\n  @media (max-width: 720px) { .tlcs-tiers { grid-template-columns: 1fr; } }\n  .tlcs-tier {\n    border: 1px solid var(--line);\n    border-radius: var(--radius);\n    padding: 26px 24px;\n    background: var(--bg);\n  }\n  .tlcs-tier.tlcs-central { background: var(--tint); border-color: var(--brand-300); }\n  .tlcs-tier .tlcs-chip {\n    font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase;\n    display: inline-block; padding: 4px 10px; border-radius: 999px; margin-bottom: 14px;\n  }\n  .tlcs-tier.tlcs-central .tlcs-chip { background: var(--brand); color: #fff; }\n  .tlcs-tier.tlcs-customer .tlcs-chip { background: var(--tint-2); color: var(--brand-800); }\n  .tlcs-tier ul { margin: 10px 0 0; padding-left: 18px; color: var(--ink-2); font-size: 14.5px; }\n  .tlcs-tier li { margin-bottom: 5px; }\n  .tlcs-tier li::marker { color: var(--brand); }\n\n  .tlcs-modules-strip { margin-top: 20px; display: flex; flex-wrap: wrap; gap: 8px; }\n  .tlcs-modules-strip span {\n    font-family: var(--mono); font-size: 12.5px;\n    padding: 7px 13px; border: 1px solid var(--line); border-radius: var(--radius-sm);\n    background: var(--surface); color: var(--ink-2);\n  }\n\n  \/* ============ INSIGHT CALLOUT ============ *\/\n  .tlcs-insight {\n    border-left: 3px solid var(--brand);\n    background: var(--tint);\n    border-radius: 0 var(--radius) var(--radius) 0;\n    padding: 22px 26px;\n    margin: 32px 0 0;\n    font-size: 17px;\n    font-weight: 500;\n    max-width: 70ch;\n  }\n  .tlcs-insight .tlcs-who {\n    display: block; font-family: var(--mono); font-size: 11.5px; font-weight: 400;\n    letter-spacing: .1em; text-transform: uppercase; color: var(--brand); margin-top: 12px;\n  }\n\n  \/* ============ TABLES ============ *\/\n  .tlcs-table-scroll { overflow-x: auto; margin-top: 36px; border: 1px solid var(--line); border-radius: var(--radius); }\n  .tlcs table { border-collapse: collapse; width: 100%; min-width: 760px; font-size: 14px; }\n  .tlcs thead th {\n    font-family: var(--mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase;\n    text-align: left; color: var(--ink-3); font-weight: 500;\n    padding: 14px 16px; border-bottom: 1px solid var(--line-strong);\n    background: var(--surface);\n    white-space: nowrap;\n  }\n  .tlcs tbody td { padding: 14px 16px; border-bottom: 1px solid var(--line); vertical-align: top; color: var(--ink-2); }\n  .tlcs tbody tr:last-child td { border-bottom: none; }\n  .tlcs tbody td:first-child { color: var(--ink); font-weight: 500; white-space: nowrap; }\n  .tlcs tbody tr:hover td { background: var(--surface); }\n\n  .tlcs-matrix td, .tlcs-matrix th { text-align: center; }\n  .tlcs-matrix td:first-child, .tlcs-matrix th:first-child { text-align: left; }\n  .tlcs-matrix th.tlcs-talora, .tlcs-matrix td.tlcs-talora { background: var(--tint); }\n  .tlcs-matrix thead th.tlcs-talora { color: var(--brand-800); font-weight: 700; }\n\n  .tlcs-dot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; vertical-align: -1px; }\n  .tlcs-dot.tlcs-full { background: var(--brand); }\n  .tlcs-dot.tlcs-half { background: transparent; border: 2px solid var(--warn); }\n  .tlcs-dot.tlcs-none { background: transparent; border: 2px solid var(--line-strong); }\n  .tlcs-legend { display: flex; gap: 22px; flex-wrap: wrap; margin-top: 14px; font-size: 13px; color: var(--ink-3); align-items: center; }\n  .tlcs-legend span { display: inline-flex; align-items: center; gap: 7px; }\n\n  .tlcs-ai-badge {\n    font-family: var(--mono); font-size: 11px; letter-spacing: .06em;\n    padding: 3px 9px; border-radius: 999px; white-space: nowrap;\n  }\n  .tlcs-ai-badge.tlcs-live { background: var(--ok-subtle); color: var(--ok); border: 1px solid var(--ok); }\n  .tlcs-ai-badge.tlcs-partial { background: var(--warn-subtle); color: var(--warn); border: 1px solid var(--warn); }\n  .tlcs-ai-badge.tlcs-no { background: transparent; color: var(--ink-3); border: 1px solid var(--line-strong); }\n\n  \/* ============ CARD GRIDS ============ *\/\n  .tlcs-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 36px; }\n  .tlcs-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 36px; }\n  @media (max-width: 860px) { .tlcs-grid-3 { grid-template-columns: 1fr; } .tlcs-grid-2 { grid-template-columns: 1fr; } }\n\n  .tlcs-card {\n    border: 1px solid var(--line);\n    border-radius: var(--radius);\n    padding: 26px 24px;\n    background: var(--bg);\n  }\n  .tlcs-card .tlcs-k {\n    font-family: var(--mono); font-size: 12px; letter-spacing: .1em;\n    color: var(--brand); text-transform: uppercase; display: block; margin-bottom: 12px;\n  }\n  .tlcs-card p { color: var(--ink-2); font-size: 14.5px; margin: 0; }\n\n  .tlcs-stat-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: 36px; }\n  @media (max-width: 860px) { .tlcs-stat-row { grid-template-columns: repeat(2, 1fr); } }\n  .tlcs-stat {\n    border-top: 3px solid var(--brand);\n    background: var(--surface);\n    border-radius: 0 0 var(--radius) var(--radius);\n    padding: 22px 20px 20px;\n  }\n  .tlcs-stat b {\n    display: block; font-size: clamp(30px, 4vw, 40px); font-weight: 600;\n    letter-spacing: -0.02em; line-height: 1; color: var(--brand-800);\n    font-variant-numeric: tabular-nums;\n    margin-bottom: 8px;\n  }\n  .tlcs-stat span { font-size: 13.5px; color: var(--ink-2); }\n\n  \/* ============ USP LIST ============ *\/\n  .tlcs-usp-list { margin-top: 36px; display: grid; grid-template-columns: 1fr 1fr; gap: 0 48px; }\n  @media (max-width: 720px) { .tlcs-usp-list { grid-template-columns: 1fr; } }\n  .tlcs-usp {\n    display: grid; grid-template-columns: 20px 1fr; gap: 14px;\n    padding: 20px 0; border-bottom: 1px solid var(--line);\n  }\n  .tlcs-usp .tlcs-tick { color: var(--brand); font-weight: 700; line-height: 1.5; }\n  .tlcs-usp h3 { font-size: 15.5px; margin-bottom: 4px; }\n  .tlcs-usp p { font-size: 14px; color: var(--ink-2); margin: 0; }\n\n  \/* ============ PERSONAS ============ *\/\n  .tlcs-persona {\n    border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--bg);\n  }\n  .tlcs-persona header { background: #1d4d52; color: #fff; padding: 20px 24px; }\n  .tlcs-persona header .tlcs-role { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: #74d3d4; display: block; margin-bottom: 6px; }\n  .tlcs-persona header h3 { color: #fff; margin: 0; font-size: 18px; }\n  .tlcs-persona .tlcs-body { padding: 22px 24px; }\n  .tlcs-persona .tlcs-body h4 {\n    font-family: var(--mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase;\n    color: var(--ink-3); margin: 16px 0 6px; font-weight: 500;\n  }\n  .tlcs-persona .tlcs-body h4:first-child { margin-top: 0; }\n  .tlcs-persona .tlcs-body p, .tlcs-persona .tlcs-body li { font-size: 14px; color: var(--ink-2); }\n  .tlcs-persona .tlcs-body ul { margin: 0; padding-left: 18px; }\n  .tlcs-persona .tlcs-body li { margin-bottom: 4px; }\n  .tlcs-persona .tlcs-body li::marker { color: var(--brand); }\n\n  \/* ============ PROCESS ============ *\/\n  .tlcs-process { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: 36px; counter-reset: step; }\n  @media (max-width: 860px) { .tlcs-process { grid-template-columns: 1fr 1fr; } }\n  @media (max-width: 560px) { .tlcs-process { grid-template-columns: 1fr; } }\n  .tlcs-step {\n    counter-increment: step;\n    border: 1px solid var(--line); border-radius: var(--radius);\n    padding: 24px 20px; background: var(--bg);\n  }\n  .tlcs-step::before {\n    content: \"0\" counter(step);\n    font-family: var(--mono); font-size: 13px; color: var(--brand);\n    display: block; margin-bottom: 14px; letter-spacing: .08em;\n  }\n  .tlcs-step h3 { font-size: 16px; }\n  .tlcs-step p { font-size: 13.5px; color: var(--ink-2); margin: 0; }\n\n  \/* ============ SOLUTIONS ============ *\/\n  .tlcs-solution { margin-top: 48px; padding-top: 48px; border-top: 1px solid var(--line); }\n  .tlcs-solution:first-of-type { margin-top: 36px; padding-top: 0; border-top: none; }\n  .tlcs-solution .tlcs-n {\n    font-family: var(--mono); font-size: 13px; color: var(--brand); letter-spacing: .08em;\n    display: block; margin-bottom: 10px;\n  }\n  .tlcs-solution h3 { font-size: clamp(20px, 2.6vw, 26px); letter-spacing: -0.015em; }\n  .tlcs-solution .tlcs-goal { color: var(--ink-2); max-width: 66ch; }\n  .tlcs-features { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; margin-top: 22px; }\n  @media (max-width: 720px) { .tlcs-features { grid-template-columns: 1fr; } }\n  .tlcs-feature {\n    background: var(--surface); border-radius: var(--radius-sm);\n    padding: 16px 18px; font-size: 14px; color: var(--ink-2);\n    display: grid; grid-template-columns: 22px 1fr; gap: 10px;\n  }\n  .tlcs-feature .tlcs-fkey {\n    font-family: var(--mono); font-size: 12px; color: var(--brand-700); font-weight: 700; padding-top: 1px;\n  }\n  .tlcs-feature b { color: var(--ink); font-weight: 600; display: block; margin-bottom: 2px; }\n  .tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }\n  .tags span {\n    font-family: var(--mono); font-size: 11.5px; letter-spacing: .04em;\n    padding: 5px 11px; border-radius: 999px;\n    background: var(--tint); color: var(--brand-800);\n  }\n\n  .tlcs-note {\n    display: flex; gap: 10px; margin-top: 18px;\n    font-size: 13.5px; color: var(--ink-2);\n    background: var(--info-subtle); border: 1px solid var(--info);\n    border-radius: var(--radius-sm); padding: 12px 16px; max-width: 72ch;\n  }\n  .tlcs-note::before { content: \"\u24d8\"; color: var(--info); }\n\n  \/* ============ REFLECTIONS ============ *\/\n  .tlcs-reflection { border-top: 2px solid var(--brand); padding-top: 20px; }\n  .tlcs-reflection .tlcs-k { font-family: var(--mono); font-size: 12px; color: var(--brand); letter-spacing: .08em; display: block; margin-bottom: 10px; }\n\n  \/* ============ ELEMENTOR HELPERS ============ *\/\n  .tlcs-section { padding: 44px 0; }   \/* ch\u1ec9nh spacing gi\u1eefa c\u00e1c section t\u1ea1i \u0111\u00e2y, ho\u1eb7c d\u00f9ng margin c\u1ee7a Elementor widget *\/\n\n  \/* ============ THEME OVERRIDE GUARDS (theme jayden set heading\/text m\u00e0u tr\u1eafng) ============ *\/\n  .tlcs h2, .tlcs h3, .tlcs h4 { word-spacing: normal !important; }\n  .tlcs h2 { color: var(--ink) !important; font-size: clamp(26px, 3.4vw, 36px) !important; line-height: 1.15 !important; letter-spacing: -0.02em !important; }\n  .tlcs h3 { color: var(--ink) !important; font-size: 19px !important; line-height: 1.4 !important; letter-spacing: -0.01em !important; }\n  .tlcs-hero h1 { color: #fff !important; font-size: clamp(40px, 6.5vw, 72px) !important; line-height: 1.02 !important; letter-spacing: -0.03em !important; word-spacing: normal !important; }\n  .tlcs-hero h1 .tlcs-accent { color: var(--logo-teal) !important; }\n  .tlcs-solution h3 { font-size: clamp(20px, 2.6vw, 26px) !important; }\n  .tlcs-persona header h3 { color: #fff !important; font-size: 18px !important; }\n  .tlcs-step h3 { font-size: 16px !important; }\n  .tlcs-usp h3 { font-size: 15.5px !important; }\n  .tlcs-persona .tlcs-body h4 { color: var(--ink-3) !important; font-size: 11px !important; line-height: 1.5 !important; letter-spacing: .1em !important; word-spacing: .1em !important; }\n  .tlcs tbody td:first-child { color: var(--ink) !important; }\n  .tlcs-snapshot dd { color: var(--ink) !important; }\n  .tlcs-stat b { color: var(--brand-800) !important; }\n  .tlcs-feature b { color: var(--ink) !important; }\n  .tlcs-insight { color: var(--ink) !important; }\n  .tlcs-usp .tlcs-tick { color: var(--brand) !important; }\n\n  \/* ---- TABLES: ch\u1ed1ng theme \u0111\u00e8 layout \/ border \/ c\u0103n gi\u1eefa ---- *\/\n  .tlcs table {\n    table-layout: auto !important;\n    border-collapse: collapse !important;\n    border: none !important;\n    margin: 0 !important;\n    width: 100% !important;\n  }\n  .tlcs thead th {\n    text-align: left !important;\n    background: var(--surface) !important;\n    color: var(--ink-3) !important;\n    border: none !important;\n    border-bottom: 1px solid var(--line-strong) !important;\n    padding: 14px 16px !important;\n  }\n  .tlcs tbody td {\n    text-align: left !important;\n    vertical-align: top !important;\n    border: none !important;\n    border-bottom: 1px solid var(--line) !important;\n    padding: 14px 16px !important;\n  }\n  .tlcs tbody tr:last-child td { border-bottom: none !important; }\n  .tlcs tbody td:first-child { white-space: nowrap !important; }\n  \/* matrix: c\u0103n gi\u1eefa dots, c\u1ed9t \u0111\u1ea7u c\u0103n tr\u00e1i *\/\n  .tlcs-matrix thead th, .tlcs-matrix tbody td { text-align: center !important; vertical-align: middle !important; }\n  .tlcs-matrix thead th:first-child, .tlcs-matrix tbody td:first-child { text-align: left !important; }\n  .tlcs-matrix thead th.tlcs-talora { color: var(--brand-800) !important; }\n  .tlcs-matrix th.tlcs-talora, .tlcs-matrix td.tlcs-talora { background: var(--tint) !important; }\n\n  \/* ============ MUNCHI ADDITIONS: not present in the original Talora\/ONE file ============ *\/\n\n  \/* Hero: rounded-square app icon in place of a wordmark SVG (Munchi has no vector logo) *\/\n  .tlcs-hero-icon {\n    width: 84px; height: 84px;\n    border-radius: 20px;\n    display: block;\n    margin-bottom: 40px;\n    box-shadow: 0 10px 28px rgba(0,0,0,.4), 0 0 0 1px rgba(255,255,255,.06);\n  }\n\n  \/* Color token swatches: used in the design system section *\/\n  .tlcs-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 14px; margin-top: 28px; }\n  .tlcs-swatch { border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; background: var(--bg); }\n  .tlcs-swatch i { display: block; height: 56px; font-style: normal; }\n  .tlcs-swatch b { display: block; font-size: 13px; font-weight: 600; padding: 10px 12px 2px; color: var(--ink) !important; }\n  .tlcs-swatch span { display: block; font-family: var(--mono); font-size: 11.5px; color: var(--ink-3); padding: 0 12px 10px; }\n\n  \/* Status board: compact visual list for the deploy\/production section, badge instead of paragraph *\/\n  .tlcs-status-list { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; margin-top: 28px; background: var(--line); display: grid; gap: 1px; }\n  .tlcs-status-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px 20px; background: var(--bg); }\n  .tlcs-status-row b { display: block; font-size: 14.5px; font-weight: 600; color: var(--ink) !important; }\n  .tlcs-status-row .tlcs-status-detail { display: block; font-size: 13px; color: var(--ink-2); margin-top: 2px; }\n  .tlcs-status-row .tlcs-ai-badge { flex-shrink: 0; }\n  @media (max-width: 640px) { .tlcs-status-row { flex-direction: column; align-items: flex-start; gap: 6px; } }\n\n  \/* Fact strip: short mono chips for hard numbers\/IDs (bundle id, version...), reuses the modules-strip look at a smaller size *\/\n  .tlcs-fact-strip { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 8px; }\n  .tlcs-fact-strip span {\n    font-family: var(--mono); font-size: 12px;\n    padding: 6px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm);\n    background: var(--surface); color: var(--ink-2);\n  }\n\n  \/* Video embed: transparent frame, no fake device bezel. The source recording already shows the phone. *\/\n  .tlcs-video { max-width: 420px; margin: 36px auto 0; border-radius: var(--radius); overflow: hidden; background: transparent; }\n  .tlcs-video video { display: block; width: 100%; height: auto; }\n  .tlcs-video-caption { text-align: center; margin-top: 16px; font-family: var(--mono); font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }\n\n  \/* Phone screenshot gallery: lays shots side by side in a grid, each capped to a fixed phone-shaped frame. *\/\n  \/* Scoped to Munchi only (doesn't touch the shared .tlcs-shot rule Talora\/ONE rely on for full-width desktop screenshots). *\/\n  .tlcs-shot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; margin-top: 36px; }\n  .tlcs-shot-grid .tlcs-shot { margin-top: 0; }\n  .tlcs-shot img.tlcs-shot-phone { aspect-ratio: 9 \/ 19.5; height: auto; object-fit: cover; object-position: top; }\n<\/style>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-e8b5eb6 e-con-full e-flex e-con e-parent\" data-id=\"e8b5eb6\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7defbb4 elementor-widget elementor-widget-html\" data-id=\"7defbb4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-hero\">\n  <div class=\"tlcs-wrap\">\n    <img decoding=\"async\" class=\"tlcs-hero-icon\" src=\"https:\/\/dominhtunglam.com\/wp-content\/uploads\/2026\/08\/munchi-icon.png\" alt=\"Munchi app icon\">\n    <div class=\"tlcs-hero-tag\">Hobby Project \u00b7 iOS App<\/div>\n    <h1>Munchi<\/h1>\n    <p class=\"tlcs-sub\">Snap a photo of your meal and let AI log the calories. No search bar, no typing out every ingredient. A personal iOS nutrition diary I vibecoded solo in about two weeks, and now use every day.<\/p>\n    <div class=\"tlcs-hero-meta\">\n      <span>iOS 17+ \u00b7 SwiftUI + SwiftData<\/span>\n      <span>4 swappable AI providers<\/span>\n      <span>Solo, vibecoded with Claude Code<\/span>\n      <span>Built May 2026<\/span>\n    <\/div>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-feab41f e-con-full e-flex e-con e-parent\" data-id=\"feab41f\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-6e3607d elementor-widget elementor-widget-html\" data-id=\"6e3607d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">At a glance<\/div>\n    <dl class=\"tlcs-snapshot\">\n      <div><dt>Status<\/dt><dd>Personal build, running on my own iPhone<\/dd><\/div>\n      <div><dt>Platform<\/dt><dd>iOS 17+, SwiftUI + SwiftData<\/dd><\/div>\n      <div><dt>AI stack<\/dt><dd>Gemini, Groq, Claude, OpenRouter<\/dd><\/div>\n      <div><dt>Built with<\/dt><dd>Solo, vibecoded with Claude Code<\/dd><\/div>\n      <div><dt>Timeframe<\/dt><dd>About 2 weeks (May 2026)<\/dd><\/div>\n      <div><dt>Version<\/dt><dd>1.0, build 1<\/dd><\/div>\n    <\/dl>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-ce2706b e-con-full e-flex e-con e-parent\" data-id=\"ce2706b\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-dfba400 elementor-widget elementor-widget-html\" data-id=\"dfba400\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">Why I built this<\/div>\n    <h2>A food diary, not a food dictionary<\/h2>\n    <p class=\"tlcs-lede\">Most calorie-counting apps are built for the US market: an English food database and a search-then-select flow for every ingredient. For Vietnamese dishes, a bowl of bun bo or a plate of com tam with a dozen components, looking up each item just to add up the calories is enough friction that most people quit within a few days. Munchi (working title: \"Ngoam Ngoam,\" the Vietnamese word for the sound of chewing) started from that exact personal need: a food diary where logging a meal is faster than eating it.<\/p>\n\n    <div class=\"tlcs-insight\">\n      The real product bet wasn't which AI to use. It was removing the search bar entirely. A single photo replaces typing, scrolling, and picking items one by one.\n      <span class=\"tlcs-who\">Core design decision<\/span>\n    <\/div>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-883180b e-flex e-con-boxed e-con e-parent\" data-id=\"883180b\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-1d0af97 elementor-widget elementor-widget-html\" data-id=\"1d0af97\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">Core features<\/div>\n    <h2>Four tabs, one loop: eat, log, look back<\/h2>\n    <p class=\"tlcs-lede\">The bottom tab bar is a custom shape with a concave notch for a floating \"+\" button. Every meal starts with one tap, no matter which tab you're on.<\/p>\n\n    <div class=\"tlcs-process\">\n      <div class=\"tlcs-step\">\n        <h3>Diary (Nh\u1eadt k\u00fd)<\/h3>\n        <p>Dashboard: calorie ring, macro cards, an intake\/burn bar, and the day's meals and supplements.<\/p>\n      <\/div>\n      <div class=\"tlcs-step\">\n        <h3>Journey (H\u00e0nh tr\u00ecnh)<\/h3>\n        <p>30\/90\/180 day weight chart, steps and active calories synced from Apple Health, weekly intake vs. burn balance.<\/p>\n      <\/div>\n      <div class=\"tlcs-step\">\n        <h3>History (L\u1ecbch s\u1eed)<\/h3>\n        <p>Calendar view with a total calorie count per day. Quick to check back a week or a month.<\/p>\n      <\/div>\n      <div class=\"tlcs-step\">\n        <h3>Profile (H\u1ed3 s\u01a1)<\/h3>\n        <p>TDEE, calorie goal, AI model choice, supplement tracker, and light\/dark appearance.<\/p>\n      <\/div>\n    <\/div>\n\n    <div class=\"tlcs-features\">\n      <div class=\"tlcs-feature\">\n        <span class=\"tlcs-fkey\">01<\/span>\n        <div><b>Barcode scanner<\/b>Looks up packaged products through Open Food Facts and pre-fills the nutrition facts.<\/div>\n      <\/div>\n      <div class=\"tlcs-feature\">\n        <span class=\"tlcs-fkey\">02<\/span>\n        <div><b>Exercise log<\/b>Logs a workout and offsets it against the day's calorie budget.<\/div>\n      <\/div>\n      <div class=\"tlcs-feature\">\n        <span class=\"tlcs-fkey\">03<\/span>\n        <div><b>Daily goal override<\/b>Overrides the calorie target for just today, without touching the base configuration.<\/div>\n      <\/div>\n      <div class=\"tlcs-feature\">\n        <span class=\"tlcs-fkey\">04<\/span>\n        <div><b>3-step onboarding<\/b>Basic info, then goal and activity level, then a calorie target (AI-calculated or self-set).<\/div>\n      <\/div>\n      <div class=\"tlcs-feature\">\n        <span class=\"tlcs-fkey\">05<\/span>\n        <div><b>Micronutrient tracking<\/b>Also tracks fiber, sugar, sodium, calcium, iron, and vitamins A, B, and C.<\/div>\n      <\/div>\n      <div class=\"tlcs-feature\">\n        <span class=\"tlcs-fkey\">06<\/span>\n        <div><b>Adaptive light\/dark<\/b>Every color token follows the system setting, or can be set by hand in Profile.<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-354a6f4 e-flex e-con-boxed e-con e-parent\" data-id=\"354a6f4\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e4d63fc elementor-widget elementor-widget-html\" data-id=\"e4d63fc\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">AI architecture<\/div>\n    <h2>One feature, four swappable AI providers<\/h2>\n    <p class=\"tlcs-lede\">Photograph the meal. AI returns a list of dishes with estimated calories and macros. The user edits each card before saving. Instead of locking into one API, Munchi lets the user pick a provider in Profile: switch when one runs out of quota, when speed matters more, or when a free model will do.<\/p>\n\n    <div class=\"tlcs-table-scroll\">\n      <table>\n        <thead>\n          <tr><th>Provider<\/th><th>Typical model<\/th><th>Vision<\/th><th>Cost<\/th><th>Notes<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr>\n            <td>Claude<\/td>\n            <td>claude-haiku-4-5<\/td>\n            <td><span class=\"tlcs-ai-badge tlcs-live\">Yes<\/span><\/td>\n            <td>Paid<\/td>\n            <td>The most accurate at recognizing Vietnamese dishes of the four<\/td>\n          <\/tr>\n          <tr>\n            <td>Gemini<\/td>\n            <td>gemini-2.0-flash<\/td>\n            <td><span class=\"tlcs-ai-badge tlcs-live\">Yes<\/span><\/td>\n            <td>Free tier<\/td>\n            <td>The default. Fast, and enough for daily use<\/td>\n          <\/tr>\n          <tr>\n            <td>Groq<\/td>\n            <td>LLaMA vision<\/td>\n            <td><span class=\"tlcs-ai-badge tlcs-live\">Yes<\/span><\/td>\n            <td>Free tier<\/td>\n            <td>The fastest inference. Used when a quick reply matters<\/td>\n          <\/tr>\n          <tr>\n            <td>OpenRouter<\/td>\n            <td>Llama 3.2 11B Vision, Qwen2.5 VL<\/td>\n            <td><span class=\"tlcs-ai-badge tlcs-live\">Yes<\/span><\/td>\n            <td>Has free models<\/td>\n            <td>An aggregator that checks which models are actually available, right inside the app<\/td>\n          <\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n\n    <div class=\"tlcs-insight\">\n      AI only produces a draft, not the final word. Every dish it recognizes becomes its own editable card: rename it, adjust the portion, or delete it, all before the meal is saved to the diary.\n      <span class=\"tlcs-who\">Design principle for the AI flow<\/span>\n    <\/div>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-504de2e e-flex e-con-boxed e-con e-parent\" data-id=\"504de2e\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-96be946 elementor-widget elementor-widget-html\" data-id=\"96be946\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">Demo<\/div>\n    <h2>Watch it log a meal<\/h2>\n\n    <div class=\"tlcs-video\">\n      <video controls playsinline preload=\"metadata\" poster=\"REPLACE-IMG-URL\/munchi-video-poster.png\">\n        <source src=\"https:\/\/dominhtunglam.com\/wp-content\/uploads\/2026\/08\/munchi-.mp4\" type=\"video\/mp4\">\n      <\/video>\n    <\/div>\n    <p class=\"tlcs-video-caption\">Photo to logged meal, start to finish<\/p>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-83a3791 e-con-full e-flex e-con e-parent\" data-id=\"83a3791\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7e31a03 elementor-widget elementor-widget-html\" data-id=\"7e31a03\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">Design system<\/div>\n    <h2>Its own color tokens, the same semantic structure<\/h2>\n    <p class=\"tlcs-lede\">Munchi has its own brand palette (a burnt orange, #D64C30, different from this portfolio's teal) but follows the same semantic token structure: background, text, border, status. Each token has a light and dark pair, so there's no hex code hardcoded somewhere in the code.<\/p>\n\n    <div class=\"tlcs-swatches\">\n      <div class=\"tlcs-swatch\"><i style=\"background:#D64C30\"><\/i><b>Brand<\/b><span>#D64C30<\/span><\/div>\n      <div class=\"tlcs-swatch\"><i style=\"background:#0687FF\"><\/i><b>Protein<\/b><span>#0687FF<\/span><\/div>\n      <div class=\"tlcs-swatch\"><i style=\"background:#FC6C00\"><\/i><b>Carbs<\/b><span>#FC6C00<\/span><\/div>\n      <div class=\"tlcs-swatch\"><i style=\"background:#D64C30\"><\/i><b>Fat<\/b><span>#D64C30<\/span><\/div>\n      <div class=\"tlcs-swatch\"><i style=\"background:#417D0E\"><\/i><b>Success<\/b><span>#417D0E<\/span><\/div>\n      <div class=\"tlcs-swatch\"><i style=\"background:#FC6C00\"><\/i><b>Warning<\/b><span>#FC6C00<\/span><\/div>\n      <div class=\"tlcs-swatch\"><i style=\"background:#DC2626\"><\/i><b>Danger<\/b><span>#DC2626<\/span><\/div>\n      <div class=\"tlcs-swatch\"><i style=\"background:#0687FF\"><\/i><b>Info<\/b><span>#0687FF<\/span><\/div>\n    <\/div>\n\n    <div class=\"tlcs-grid-2\">\n      <div class=\"tlcs-card\">\n        <span class=\"tlcs-k\">Typography<\/span>\n        <p>SF Pro through the system font stack, no webfont to load. Text sizes follow a display\/title\/body\/label\/caption scale, with tabular numbers for every calorie count.<\/p>\n      <\/div>\n      <div class=\"tlcs-card\">\n        <span class=\"tlcs-k\">Signature component<\/span>\n        <p>The bottom tab bar is drawn with a custom Shape that has a concave notch in the middle. The \"+\" button floats above that notch, with its own shadow for light and dark mode.<\/p>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-13cca37 e-con-full e-flex e-con e-parent\" data-id=\"13cca37\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-33b8d77 elementor-widget elementor-widget-html\" data-id=\"33b8d77\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">Engineering practice<\/div>\n    <h2>Vibecoded, then reviewed like real production code<\/h2>\n    <p class=\"tlcs-lede\">Munchi was built solo with Claude Code, but \"vibecoded\" here doesn't mean stopping at prompting features into existence. Once the core app worked, an automated code review pass scanned the whole codebase before the next round of changes went in.<\/p>\n\n    <div class=\"tlcs-process\">\n      <div class=\"tlcs-step\">\n        <h3>Data model &amp; onboarding<\/h3>\n        <p>8 SwiftData models (FoodEntry, UserProfile, WeightEntry, and more), a 3-step onboarding flow that calculates TDEE.<\/p>\n      <\/div>\n      <div class=\"tlcs-step\">\n        <h3>Core loop<\/h3>\n        <p>Dashboard, add meal, and history, built before AI was wired in, to make sure the manual flow was solid first.<\/p>\n      <\/div>\n      <div class=\"tlcs-step\">\n        <h3>AI integration<\/h3>\n        <p>4 providers for photo-to-dish recognition, barcode lookup through Open Food Facts, Apple Health sync.<\/p>\n      <\/div>\n      <div class=\"tlcs-step\">\n        <h3>Automated code review<\/h3>\n        <p>Scanned 11 key files, sorted findings into critical\/warning\/info, and fixed whatever was safe to fix automatically.<\/p>\n      <\/div>\n    <\/div>\n\n    <div class=\"tlcs-stat-row\">\n      <div class=\"tlcs-stat\"><b class=\"tlcs-num\">15<\/b><span>Issues found<\/span><\/div>\n      <div class=\"tlcs-stat\"><b class=\"tlcs-num\">4<\/b><span>Critical (crash or performance risk)<\/span><\/div>\n      <div class=\"tlcs-stat\"><b class=\"tlcs-num\">9\/10<\/b><span>Fixed automatically in one pass<\/span><\/div>\n      <div class=\"tlcs-stat\"><b class=\"tlcs-num\">1<\/b><span>Deliberately skipped: a high-risk refactor left for a human call<\/span><\/div>\n    <\/div>\n\n    \n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-a24752f e-con-full e-flex e-con e-parent\" data-id=\"a24752f\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c186f6d elementor-widget elementor-widget-html\" data-id=\"c186f6d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">Deploy &amp; production<\/div>\n    <h2>What \"production\" means for a one-person app<\/h2>\n    <p class=\"tlcs-lede\">No App Store, no CI. Just the real setup behind the build that runs on my phone every day.<\/p>\n\n    <div class=\"tlcs-fact-strip\">\n      <span>com.lamdmt.Munchi<\/span>\n      <span>iOS 17+<\/span>\n      <span>Automatic signing<\/span>\n      <span>v1.0, build 1<\/span>\n    <\/div>\n\n    <div class=\"tlcs-process\">\n      <div class=\"tlcs-step\">\n        <h3>Build<\/h3>\n        <p><code>xcodebuild<\/code> against iPhone 16 Pro &amp; 17 Pro simulators<\/p>\n      <\/div>\n      <div class=\"tlcs-step\">\n        <h3>Review<\/h3>\n        <p>Claude Code review pass before the next round of edits<\/p>\n      <\/div>\n      <div class=\"tlcs-step\">\n        <h3>Sign &amp; install<\/h3>\n        <p>Automatic signing, straight onto my own iPhone<\/p>\n      <\/div>\n      <div class=\"tlcs-step\">\n        <h3>Daily use<\/h3>\n        <p>The real production environment, right now<\/p>\n      <\/div>\n    <\/div>\n\n    <div class=\"tlcs-status-list\">\n      <div class=\"tlcs-status-row\">\n        <div><b>Secrets<\/b><span class=\"tlcs-status-detail\">Default Gemini key bundled, other 3 providers bring your own key<\/span><\/div>\n        <span class=\"tlcs-ai-badge tlcs-live\">Bundled + BYO<\/span>\n      <\/div>\n      <div class=\"tlcs-status-row\">\n        <div><b>Code signing<\/b><span class=\"tlcs-status-detail\">Personal Apple Developer account, no manual profiles<\/span><\/div>\n        <span class=\"tlcs-ai-badge tlcs-live\">Automatic<\/span>\n      <\/div>\n      <div class=\"tlcs-status-row\">\n        <div><b>HealthKit<\/b><span class=\"tlcs-status-detail\">Steps &amp; active energy only<\/span><\/div>\n        <span class=\"tlcs-ai-badge tlcs-live\">Read-only<\/span>\n      <\/div>\n      <div class=\"tlcs-status-row\">\n        <div><b>Camera &amp; Photos<\/b><span class=\"tlcs-status-detail\">Meal photos and barcode scanning only<\/span><\/div>\n        <span class=\"tlcs-ai-badge tlcs-live\">Scoped<\/span>\n      <\/div>\n      <div class=\"tlcs-status-row\">\n        <div><b>Data storage<\/b><span class=\"tlcs-status-detail\">SwiftData, on-device<\/span><\/div>\n        <span class=\"tlcs-ai-badge tlcs-live\">Local<\/span>\n      <\/div>\n      <div class=\"tlcs-status-row\">\n        <div><b>CloudKit sync<\/b><span class=\"tlcs-status-detail\">Model configured for it, iCloud capability not turned on yet<\/span><\/div>\n        <span class=\"tlcs-ai-badge tlcs-partial\">Coded, off<\/span>\n      <\/div>\n      <div class=\"tlcs-status-row\">\n        <div><b>CI\/CD<\/b><span class=\"tlcs-status-detail\">Verified locally before every change<\/span><\/div>\n        <span class=\"tlcs-ai-badge tlcs-no\">None<\/span>\n      <\/div>\n      <div class=\"tlcs-status-row\">\n        <div><b>Distribution<\/b><span class=\"tlcs-status-detail\">Installed via Xcode onto my own iPhone<\/span><\/div>\n        <span class=\"tlcs-ai-badge tlcs-no\">Not published<\/span>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-e3edded e-con-full e-flex e-con e-parent\" data-id=\"e3edded\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f1a8028 elementor-widget elementor-widget-html\" data-id=\"f1a8028\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">What shipped<\/div>\n    <h2>A full app, not a demo<\/h2>\n    <p class=\"tlcs-lede\">From onboarding to AI meal logging to activity tracking, plus a token-based design system of its own: Munchi covers a full personal product used daily, not a prototype that stops at the happy path.<\/p>\n\n    <div class=\"tlcs-stat-row\">\n      <div class=\"tlcs-stat\"><b class=\"tlcs-num\">5<\/b><span>Tabs &amp; flows (Diary, Journey, History, Profile, Add Meal)<\/span><\/div>\n      <div class=\"tlcs-stat\"><b class=\"tlcs-num\">8<\/b><span>SwiftData models<\/span><\/div>\n      <div class=\"tlcs-stat\"><b class=\"tlcs-num\">4<\/b><span>Swappable AI providers<\/span><\/div>\n      <div class=\"tlcs-stat\"><b class=\"tlcs-num\">100%<\/b><span>Vietnamese UI, adaptive light\/dark<\/span><\/div>\n    <\/div>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-f8ec249 e-con-full e-flex e-con e-parent\" data-id=\"f8ec249\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9f7bd12 elementor-widget elementor-widget-html\" data-id=\"9f7bd12\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"tlcs\">\n<div class=\"tlcs-section\">\n<div class=\"tlcs-wrap\">\n    <div class=\"tlcs-eyebrow\">Lessons &amp; what's next<\/div>\n    <h2>What stuck, and what's left<\/h2>\n    <div class=\"tlcs-grid-3\">\n      <div class=\"tlcs-reflection\">\n        <span class=\"tlcs-k\">01. Photo-first beats search-first<\/span>\n        <p>The most important product decision wasn't which AI to use. It was removing the search bar. A photo replaces typing, scrolling, and picking items one by one, and that's the actual reason logging a meal stuck as a daily habit.<\/p>\n      <\/div>\n      <div class=\"tlcs-reflection\">\n        <span class=\"tlcs-k\">02. Multiple AI providers is a resilience feature<\/span>\n        <p>Letting Claude, Gemini, Groq, and OpenRouter be swapped isn't a novelty. It means a feature used every day never gets blocked by one API running out of quota, going slow, or costing too much.<\/p>\n      <\/div>\n      <div class=\"tlcs-reflection\">\n        <span class=\"tlcs-k\">03. An automated review catches what a solo pass misses<\/span>\n        <p>The post-build review pass found exactly the mistakes that happen when coding alone and fast: repeated JSON decoding, retain cycles in async tasks, force-unwraps that could crash. Caught before anyone used it, not after.<\/p>\n      <\/div>\n    <\/div>\n\n    <div class=\"tlcs-card\" style=\"margin-top:36px\">\n      <span class=\"tlcs-k\">Next up<\/span>\n      <ul style=\"margin:0;padding-left:18px;color:var(--ink-2);font-size:14.5px\">\n        <li style=\"margin-bottom:6px\">Turn on the iCloud capability so CloudKit sync actually runs across two phones.<\/li>\n        <li style=\"margin-bottom:6px\">Install it on a friend's phone through TestFlight and see if the AI flow holds up outside my own eating habits.<\/li>\n        <li>Come back to the one skipped refactor: the duplicated food-editing form between the photo review screen and the meal detail screen.<\/li>\n      <\/ul>\n    <\/div>\n  <\/div>\n<\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-13c348d e-flex e-con-boxed e-con e-parent\" data-id=\"13c348d\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-1add74b elementor-widget elementor-widget-home2_footer\" data-id=\"1add74b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"home2_footer.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t          \n             <footer class=\"tf-footer style-2 section-has-dot\">\n                    \n        \n        <div class=\"line-page\"><\/div>\n        \n            <span><\/span>\n            <div class=\"tf-container\">\n                <div class=\"row\">\n                    <div class=\"offset-xl-2 col-xl-8 offset-lg-2 col-lg-8\">\n                        <div class=\"wrap-section\">\n                                                        <div class=\"text-body-2 dot-before subtitle style-center\">Get in touch<\/div>\n                                                        <div class=\"avatar text-center\">\n                                                                <h6 class=\"name fw-medium\">Do Minh Tung Lam<\/h6>\n                                <div class=\"meta text-body-2\">Sr. UI\/UX Designer<\/div>\n                            <\/div>\n                                                                                    <ul class=\"social-links justify-content-center\">\n                                                                    <li>\n                                        <a href=\"https:\/\/www.instagram.com\/richardmaz_\/\">\n                                            <i class=\"icon icon-instagram\"><\/i>\n                                        <\/a>\n                                    <\/li>\n                                                                    <li>\n                                        <a href=\"https:\/\/www.facebook.com\/ominhtunglam\/\">\n                                            <i class=\"icon icon-facebook-f\"><\/i>\n                                        <\/a>\n                                    <\/li>\n                                                                    <li>\n                                        <a href=\"https:\/\/www.linkedin.com\/in\/%C4%91%E1%BB%97-minh-t%C3%B9ng-l%C3%A2m-21a466197\/\">\n                                            <i class=\"icon-linkedin-in\"><\/i>\n                                        <\/a>\n                                    <\/li>\n                                                            <\/ul>\n                                                                                    <div class=\"coppyright\">\n                                <i class=\"icon icon-global\"><\/i>\n                                <div class=\"text-body-3 font-poppins\">2026 All Rights Reserved <span class=\"text-white\">Do Minh Tung Lam<\/span><\/div>\n                            <\/div>\n                                                    <\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n            <img decoding=\"async\" src=\"https:\/\/dominhtunglam.com\/wp-content\/themes\/jayden\/images\/section\/img-bg-2.png\" alt=\"\" class=\"map\">\n            <div class=\"box-gradient2\"><\/div>\n        <\/footer>\n        \n        \t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Hobby Project \u00b7 iOS App Munchi Snap a photo of your meal and let AI log the calories. No search bar, no typing out every ingredient. A personal iOS nutrition diary I vibecoded solo in about two weeks, and now use every day. iOS 17+ \u00b7 SwiftUI + SwiftData 4 swappable AI providers Solo, vibecoded [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"_acf_changed":false,"pagelayer_contact_templates":[],"_pagelayer_content":"","footnotes":""},"class_list":["post-2319","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/dominhtunglam.com\/index.php?rest_route=\/wp\/v2\/pages\/2319","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dominhtunglam.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/dominhtunglam.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/dominhtunglam.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dominhtunglam.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2319"}],"version-history":[{"count":4,"href":"https:\/\/dominhtunglam.com\/index.php?rest_route=\/wp\/v2\/pages\/2319\/revisions"}],"predecessor-version":[{"id":2331,"href":"https:\/\/dominhtunglam.com\/index.php?rest_route=\/wp\/v2\/pages\/2319\/revisions\/2331"}],"wp:attachment":[{"href":"https:\/\/dominhtunglam.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2319"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}