PageSourceSearch

https://lgautomatic.com/case-study-marketing-os.html

html lgautomatic.com collected 2026-10-05 02:39:43 UTC 41,629 bytes, 751 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<title>Marketing OS — Full-Funnel Organic Machine on Self-Hosted Stack | Case Study</title>
7<meta name="description" content="GAEL — a full-funnel marketing OS that generates, schedules, publishes, captures and nurtures leads across 6 channels. n8n + DeepSeek + Listmonk + Mautic + Chatwoot + Umami on one self-hosted VM. $0 ad spend, zero SaaS lock-in.">
8<link rel="preconnect" href="https://fonts.googleapis.com">
9<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10<style>
11  :root {
12    --bg:       #0c0c0c;
13    --bg2:      #0c0c0c;
14    --bg3:      #111111;
15    --cyan:     #0EA5E9;
16    --cyan-l:   #38BDF8;
17    --cyan-dim: rgba(14,165,233,0.1);
18    --purple:   #A855F7;
19    --pur-dim:  rgba(168,85,247,0.1);
20    --white:    #E2E8F0;
21    --muted:    #64748B;
22    --green:    #10B981;
23    --grn-dim:  rgba(16,185,129,0.1);
24    --yellow:   #F59E0B;
25    --yel-dim:  rgba(245,158,11,0.1);
26    --red:      #EF4444;
27    --red-dim:  rgba(239,68,68,0.1);
28    --border:   rgba(255,255,255,0.08);
29  }
30  * { margin:0; padding:0; box-sizing:border-box; }
31  body {
32    background: var(--bg);
33    color: var(--white);
34    font-family: 'Inter', 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
35    font-size: 14px;
36    line-height: 1.7;
37    min-height: 100vh;
38  }
39  a { color: var(--cyan-l); text-decoration: none; }
40  a:hover { text-decoration: underline; color: var(--cyan); }
41
42  /* ── HEADER ── */
43  .header {
44    background: linear-gradient(135deg, #0c0c0c 0%, #111111 60%, #0c0c0c 100%);
45    border-bottom: 1px solid var(--border);
46    padding: 56px 64px 48px;
47    position: relative;
48    overflow: hidden;
49  }
50  .header::after {
51    content: '';
52    position: absolute;
53    top: -80px; right: -80px;
54    width: 500px; height: 500px;
55    background: radial-gradient(circle, rgba(14,165,233,0.08) 0%, transparent 65%);
56    pointer-events: none;
57  }
58  .header::before {
59    content: '';
60    position: absolute;
61    bottom: -120px; left: -60px;
62    width: 400px; height: 400px;
63    background: radial-gradient(circle, rgba(168,85,247,0.04) 0%, transparent 65%);
64    pointer-events: none;
65  }
66  .header-inner { max-width: 1100px; margin: 0 auto; position: relative; z-index: 1; }
67  .header-tag {
68    display: inline-flex; align-items: center; gap: 6px;
69    padding: 4px 14px; border-radius: 20px;
70    background: var(--cyan-dim); border: 1px solid rgba(14,165,233,0.25);
71    font-size: 11px; font-weight: 600; color: var(--cyan);
72    letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 20px;
73  }
74  .header h1 { font-size: 38px; font-weight: 700; color: var(--white); margin-bottom: 10px; line-height: 1.2; }
75  .header h1 span { color: var(--cyan); }
76  .header-sub { font-size: 16px; color: var(--muted); margin-bottom: 32px; max-width: 700px; line-height: 1.6; }
77  .header-stats { display: flex; gap: 40px; flex-wrap: wrap; }
78  .stat { display: flex; flex-direction: column; gap: 2px; }
79  .stat-val { font-size: 24px; font-weight: 700; color: var(--cyan); font-family: 'DM Mono', monospace; }
80  .stat-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
81
82  /* ── NAVIGATION ── */
83  .tab-bar {
84    display: flex; gap: 0; background: var(--bg2);
85    border-bottom: 1px solid var(--border);
86    padding: 0 64px; position: sticky; top: 0; z-index: 100;
87  }
88  .tab-btn {
89    padding: 14px 24px; font-size: 13px; font-weight: 600; color: var(--muted);
90    cursor: pointer; border: none; background: none; font-family: 'Inter', 'Outfit', sans-serif;
91    border-bottom: 2px solid transparent; transition: color 0.2s, border-color 0.2s; letter-spacing: 0.02em;
92  }
93  .tab-btn:hover { color: var(--cyan-l); }
94  .tab-btn.active { color: var(--cyan); border-bottom-color: var(--cyan); background: rgba(14,165,233,0.04); }
95  .tab-content { display: none; }
96  .tab-content.active { display: block; }
97
98  /* ── CONTAINER ── */
99  .container { max-width: 1100px; margin: 0 auto; padding: 48px 64px; }
100
101  /* ── SECTION TITLE ── */
102  .section-title {
103    font-size: 18px; font-weight: 700; color: var(--white); margin-bottom: 20px;
104    padding-bottom: 12px; border-bottom: 1px solid var(--border);
105    display: flex; align-items: center; gap: 10px;
106  }
107  .section-title::before { content: ''; width: 3px; height: 20px; background: var(--cyan); border-radius: 2px; }
108
109  /* ── GRIDS ── */
110  .ref-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 48px; }
111  .ref-card {
112    background: var(--bg2); border: 1px solid var(--border); border-radius: 12px; padding: 24px;
113    transition: border-color 0.2s;
114  }
115  .ref-card:hover { border-color: rgba(14,165,233,0.3); }
116  .ref-card.highlight { border-color: rgba(14,165,233,0.3); background: linear-gradient(135deg, rgba(14,165,233,0.06), rgba(168,85,247,0.03)); }
117  .ref-card.full { grid-column: 1 / -1; }
118  .ref-card h3 { font-size: 13px; font-weight: 700; color: var(--cyan); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 14px; }
119  .ref-row {
120    display: flex; justify-content: space-between; align-items: flex-start; gap: 12px;
121    padding: 6px 0; border-bottom: 1px solid rgba(14,165,233,0.05); font-size: 13px;
122  }
123  .ref-row:last-child { border-bottom: none; }
124  .ref-row .label { color: var(--muted); flex-shrink: 0; }
125  .ref-row .value { color: var(--white); font-family: 'DM Mono', monospace; font-size: 12px; text-align: right; }
126
127  /* ── FLOW CARDS ── */
128  .flow-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin-bottom: 48px; }
129  .flow-card {
130    background: var(--bg2); border: 1px solid var(--border); border-radius: 12px;
131    padding: 24px; position: relative; transition: border-color 0.2s;
132  }
133  .flow-card:hover { border-color: rgba(14,165,233,0.3); }
134  .flow-num {
135    position: absolute; top: -1px; left: 24px; background: var(--cyan); color: var(--bg);
136    font-size: 10px; font-weight: 700; padding: 2px 10px; border-radius: 0 0 6px 6px;
137    font-family: 'DM Mono', monospace; letter-spacing: 0.08em;
138  }
139  .flow-icon {
140    width: 42px; height: 42px; background: var(--cyan-dim); border: 1px solid rgba(14,165,233,0.2);
141    border-radius: 10px; display: flex; align-items: center; justify-content: center;
142    font-size: 18px; margin-top: 14px; margin-bottom: 14px;
143  }
144  .flow-card h4 { font-size: 15px; font-weight: 600; color: var(--white); margin-bottom: 8px; }
145  .flow-card p { font-size: 13px; color: var(--muted); line-height: 1.6; }
146
147  /* ── INTENT TABLE ── */
148  .data-table { width: 100%; border-collapse: collapse; margin-bottom: 48px; }
149  .data-table thead th {
150    background: var(--bg3); font-size: 11px; font-weight: 700; color: var(--cyan);
151    text-transform: uppercase; letter-spacing: 0.08em; padding: 12px 16px; text-align: left;
152    border-bottom: 1px solid var(--border);
153  }
154  .data-table tbody td {
155    padding: 12px 16px; font-size: 13px; color: #94A3B8; border-bottom: 1px solid rgba(14,165,233,0.05);
156  }
157  .data-table tbody tr:hover { background: rgba(14,165,233,0.03); }
158  .data-table tbody td:first-child { color: var(--white); font-weight: 600; }
159  .data-table code {
160    font-family: 'DM Mono', monospace; font-size: 11px; background: var(--cyan-dim);
161    color: var(--cyan-l); padding: 2px 8px; border-radius: 4px;
162  }
163
164  /* ── BADGE ── */
165  .badge {
166    display: inline-flex; align-items: center; padding: 2px 10px; border-radius: 12px;
167    font-size: 11px; font-weight: 600; letter-spacing: 0.04em;
168  }
169  .badge-cyan { background: var(--cyan-dim); color: var(--cyan); border: 1px solid rgba(14,165,233,0.2); }
170  .badge-green { background: var(--grn-dim); color: var(--green); border: 1px solid rgba(16,185,129,0.2); }
171  .badge-purple { background: var(--pur-dim); color: var(--purple); border: 1px solid rgba(168,85,247,0.2); }
172
173  /* ── CHALLENGE CARDS ── */
174  .challenge-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 48px; }
175  .challenge-card {
176    background: var(--bg2); border: 1px solid var(--border); border-radius: 12px; padding: 28px;
177    transition: border-color 0.2s;
178  }
179  .challenge-card:hover { border-color: rgba(14,165,233,0.3); }
180  .challenge-card h4 {
181    font-size: 14px; font-weight: 600; color: var(--white); margin-bottom: 12px;
182    display: flex; align-items: center; gap: 8px;
183  }
184  .challenge-card h4 .emoji { font-size: 16px; }
185  .challenge-card p { font-size: 13px; color: var(--muted); line-height: 1.7; }
186  .challenge-card .solution {
187    margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(14,165,233,0.08);
188    font-size: 12px; color: #94A3B8;
189  }
190  .challenge-card .solution strong { color: var(--green); }
191
192  /* ── ARCHITECTURE DIAGRAM ── */
193  .arch-diagram {
194    background: var(--bg3); border: 1px solid var(--border); border-radius: 12px;
195    padding: 32px; margin-bottom: 48px; overflow-x: auto;
196  }
197  .arch-diagram pre {
198    font-family: 'DM Mono', monospace; font-size: 13px; color: var(--cyan-l);
199    line-height: 1.8; white-space: pre; margin: 0;
200  }
201  .arch-diagram .comment { color: var(--muted); }
202  .arch-diagram .highlight { color: var(--yellow); }
203  .arch-diagram .accent { color: var(--purple); }
204
205  /* ── RESULTS GRID ── */
206  .results-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 48px; }
207  .result-card {
208    background: var(--bg2); border: 1px solid var(--border); border-radius: 12px;
209    padding: 24px; text-align: center; transition: border-color 0.2s;
210  }
211  .result-card:hover { border-color: rgba(14,165,233,0.3); }
212  .result-val { font-size: 28px; font-weight: 700; color: var(--cyan); font-family: 'DM Mono', monospace; margin-bottom: 4px; }
213  .result-label { font-size: 12px; color: var(--muted); line-height: 1.4; }
214
215  /* ── DELIVERABLES LIST ── */
216  .deliverables { list-style: none; display: flex; flex-direction: column; gap: 12px; margin-bottom: 48px; }
217  .deliverables li {
218    display: flex; align-items: flex-start; gap: 12px; font-size: 14px; color: #94A3B8;
219    padding: 16px 20px; background: var(--bg2); border: 1px solid var(--border);
220    border-radius: 10px; transition: border-color 0.2s;
221  }
222  .deliverables li:hover { border-color: rgba(14,165,233,0.3); }
223  .deliverables li .icon {
224    width: 32px; height: 32px; background: var(--cyan-dim); border: 1px solid rgba(14,165,233,0.2);
225    border-radius: 8px; display: flex; align-items: center; justify-content: center;
226    font-size: 14px; flex-shrink: 0;
227  }
228  .deliverables li strong { color: var(--white); display: block; margin-bottom: 2px; }
229
230  /* ── VERTICALS GRID ── */
231  .verticals-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 48px; }
232  .vertical-card {
233    background: var(--bg2); border: 1px solid var(--border); border-radius: 10px;
234    padding: 20px; text-align: center; transition: border-color 0.2s;
235  }
236  .vertical-card:hover { border-color: rgba(14,165,233,0.3); }
237  .vertical-card .v-icon { font-size: 28px; margin-bottom: 8px; }
238  .vertical-card .v-name { font-size: 13px; font-weight: 600; color: var(--white); }
239  .vertical-card .v-sub { font-size: 11px; color: var(--muted); margin-top: 2px; }
240
241  /* ── TECH TAGS ── */
242  .tech-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 48px; }
243  .tech-tag {
244    padding: 4px 14px; border-radius: 20px; font-size: 12px; font-weight: 500;
245    background: var(--bg3); border: 1px solid var(--border); color: var(--muted);
246    transition: border-color 0.2s, color 0.2s;
247  }
248  .tech-tag:hover { border-color: rgba(14,165,233,0.3); color: var(--cyan-l); }
249
250  /* ── CTA ── */
251  .cta-section {
252    background: linear-gradient(135deg, rgba(14,165,233,0.08) 0%, rgba(168,85,247,0.05) 100%);
253    border: 1px solid rgba(14,165,233,0.2); border-radius: 16px;
254    padding: 48px; text-align: center; margin-bottom: 48px;
255  }
256  .cta-section h2 { font-size: 24px; font-weight: 700; color: var(--white); margin-bottom: 12px; }
257  .cta-section p { font-size: 15px; color: var(--muted); margin-bottom: 28px; max-width: 500px; margin-left: auto; margin-right: auto; }
258  .cta-btn {
259    display: inline-flex; align-items: center; gap: 8px;
260    padding: 14px 32px; border-radius: 10px;
261    background: var(--cyan); color: var(--bg); font-size: 14px; font-weight: 600;
262    font-family: 'Inter', 'Outfit', sans-serif; border: none; cursor: pointer;
263    transition: background 0.2s; text-decoration: none;
264  }
265  .cta-btn:hover { background: var(--cyan-l); text-decoration: none; }
266  .cta-btn-outline {
267    display: inline-flex; align-items: center; gap: 8px;
268    padding: 14px 32px; border-radius: 10px; margin-left: 12px;
269    background: transparent; color: var(--cyan); font-size: 14px; font-weight: 600;
270    font-family: 'Inter', 'Outfit', sans-serif; border: 1px solid rgba(14,165,233,0.3); cursor: pointer;
271    transition: background 0.2s; text-decoration: none;
272  }
273  .cta-btn-outline:hover { background: rgba(14,165,233,0.1); text-decoration: none; }
274
275  /* ── DEMO BANNER ── */
276  .demo-banner {
277    background: linear-gradient(135deg, rgba(168,85,247,0.1) 0%, rgba(14,165,233,0.08) 100%);
278    border: 1px solid rgba(168,85,247,0.2); border-radius: 12px;
279    padding: 24px 32px; margin-bottom: 48px;
280    display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
281  }
282  .demo-banner-text h3 { font-size: 16px; font-weight: 600; color: var(--white); margin-bottom: 4px; }
283  .demo-banner-text p { font-size: 13px; color: var(--muted); }
284  .demo-banner-btn {
285    display: inline-flex; align-items: center; gap: 6px;
286    padding: 10px 24px; border-radius: 8px;
287    background: var(--purple); color: white; font-size: 13px; font-weight: 600;
288    font-family: 'Inter', 'Outfit', sans-serif; border: none; cursor: pointer;
289    text-decoration: none; transition: background 0.2s; flex-shrink: 0;
290  }
291  .demo-banner-btn:hover { background: #9333EA; text-decoration: none; }
292
293  /* ── FOOTER ── */
294  .footer {
295    border-top: 1px solid var(--border); padding: 32px 64px;
296    text-align: center; color: var(--muted); font-size: 12px;
297  }
298  .footer a { color: var(--cyan); }
299
300  /* ── NOTE BOX ── */
301  .note {
302    display: flex; align-items: flex-start; gap: 10px; padding: 14px 18px;
303    border-radius: 8px; font-size: 13px; margin-bottom: 24px;
304  }
305  .note-cyan { background: rgba(14,165,233,0.06); border: 1px solid rgba(14,165,233,0.15); color: #94A3B8; }
306  .note-green { background: rgba(16,185,129,0.06); border: 1px solid rgba(16,185,129,0.15); color: #94A3B8; }
307
308  /* ── RESPONSIVE ── */
309  @media (max-width: 768px) {
310    .header { padding: 32px 24px 28px; }
311    .header h1 { font-size: 26px; }
312    .header-stats { gap: 24px; }
313    .tab-bar { padding: 0 16px; overflow-x: auto; }
314    .tab-btn { padding: 12px 16px; font-size: 12px; white-space: nowrap; }
315    .container { padding: 32px 24px; }
316    .ref-grid, .flow-grid, .challenge-grid { grid-template-columns: 1fr; }
317    .results-grid, .verticals-grid { grid-template-columns: 1fr 1fr; }
318    .ref-card.full { grid-column: auto; }
319    .flow-card { grid-column: auto; }
320    .cta-btn-outline { margin-left: 0; margin-top: 12px; }
321    .demo-banner { flex-direction: column; text-align: center; }
322    .footer { padding: 24px; }
323  }
324
325    /* liquid-glass card treatment */
326    .liquid-glass {
327      background: rgba(255,255,255,0.01);
328      background-blend-mode: luminosity;
329      backdrop-filter: blur(4px);
330      border: none;
331      box-shadow: inset 0 1px 1px rgba(255,255,255,0.1);
332      position: relative; overflow: hidden;
333    }
334    .liquid-glass::before {
335      content: ''; position: absolute; inset: 0; border-radius: inherit;
336      padding: 1.4px;
337      background: linear-gradient(180deg,
338        rgba(255,255,255,0.45) 0%, rgba(255,2
33855,255,0.15) 20%,
339        rgba(255,255,255,0) 40%, rgba(255,255,255,0) 60%,
340        rgba(255,255,255,0.15) 80%, rgba(255,255,255,0.45) 100%);
341      -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
342      -webkit-mask-composite: xor; mask-composite: exclude;
343      pointer-events: none;
344    }
345</style>
346</head>
347<body>
348
349<!-- Background video -->
350<div style="position:fixed;inset:0;z-index:0;pointer-events:none">
351  <video autoplay loop muted playsinline style="width:100%;height:100%;object-fit:cover;opacity:0.65" src="/bg-video.mp4"></video>
352  <div style="position:absolute;inset:0;background:linear-gradient(to bottom,rgba(12,12,12,0.7),rgba(12,12,12,0.4),rgba(12,12,12,0.8))"></div>
353</div>
354<!-- Ambient orbs -->
355<div style="position:fixed;inset:0;pointer-events:none;z-index:0">
356  <div style="position:absolute;top:0;left:25%;width:600px;height:600px;background:rgba(14,165,233,0.05);border-radius:50%;filter:blur(120px)"></div>
357  <div style="position:absolute;top:33%;right:25%;width:500px;height:500px;background:rgba(168,85,247,0.04);border-radius:50%;filter:blur(120px)"></div>
358</div>
359<div class="page-content" style="position:relative;z-index:10">
360
361<!-- HEADER -->
362<div class="header">
363  <div class="header-inner">
364    <div class="header-tag">&#9670; Case Study &middot; Marketing OS</div>
365    <h1>One Brief In, Full-Funnel Campaign Out &mdash; Across 6 Channels, 3 Languages, <span style="color:var(--cyan)">Zero Ad Spend</span></h1>
366    <p class="header-desc">GAEL is a self-hosted marketing operating system. Feed it a campaign brief and it generates, adapts, schedules, publishes, captures leads, and nurtures them &mdash; with n8n orchestrating DeepSeek, Listmonk, Mautic, Chatwoot and Umami on a single VM. No Zapier, no Make, no Mailchimp, no HubSpot.</p>
367    <div class="header-stats"><div class="header-stats">
368      <div class="stat"><div class="stat-val">6</div><div class="stat-label">Channels Orchestrated</div></div>
369      <div class="stat"><div class="stat-val">20+</div><div class="stat-label">n8n Workflows</div></div>
370      <div class="stat"><div class="stat-val">3</div><div class="stat-label">Languages</div></div>
371      <div class="stat"><div class="stat-val">Unlimited</div><div class="stat-label">Email Capacity</div></div>
372      <div class="stat"><div class="stat-val">$0</div><div class="stat-label">Ad Spend</div></div>
373    </div>
374  </div>
375</div>
376
377<!-- TABS -->
378<div class="tab-bar">
379  <button class="tab-btn active" onclick="switchTab('overview')">Overview</button>
380  <button class="tab-btn" onclick="switchTab('architecture')">Architecture</button>
381  <button class="tab-btn" onclick="switchTab('challenges')">Challenges</button>
382  <button class="tab-btn" onclick="switchTab('results')">Results</button>
383  <button class="tab-btn" onclick="switchTab('contact')">Work With Me</button>
384</div>
385
386<!-- OVERVIEW -->
387<div id="tab-overview" class="tab-content active">
388  <div class="container">
389
390    <div class="demo-banner">
391      <div class="demo-banner-label">Production deployment</div>
392      <div class="demo-banner-title">Running in production for a multilingual content brand</div>
393      <div class="demo-banner-link" style="cursor:default;">Client engagement &middot; NDA</div>
394    </div>
395
396    <div class="section-title">The 4 Campaign Templates</div>
397    <table class="data-table">
398      <thead>
399        <tr><th>Template</th><th>Duration</th><th>Cadence</th><th>Use Case</th></tr>
400      </thead>
401      <tbody>
402        <tr>
403          <td><strong>Product Launch</strong></td>
404          <td>14 days</td>
405          <td>Teaser &rarr; Reveal &rarr; Social proof &rarr; Urgency</td>
406          <td>New product, feature release, course drop</td>
407        </tr>
408        <tr>
409          <td><strong>Evergreen</strong></td>
410          <td>Continuous</td>
411          <td>3 pins/day, 1 post/day, 2 tweets/day, weekly newsletter</td>
412          <td>Always-on traffic to landing pages</td>
413        </tr>
414        <tr>
415          <td><strong>Flash Sale</strong></td>
416          <td>24&ndash;72 h</td>
417          <td>All channels, posts every 2&ndash;4 h, email blast + reminder</td>
418          <td>Promo, last-call, inventory clearance</td>
419        </tr>
420        <tr>
421          <td><strong>Lead Magnet</strong></td>
422          <td>Continuous</td>
423          <td>Free value &rarr; landing &rarr; auto-delivery &rarr; nurture</td>
424          <td>Ebook, checklist, webinar opt-in</td>
425        </tr>
426      </tbody>
427    </table>
428
429    <div class="note note-cyan">
430      <div>
430New campaign types can be added as n8n sub-workflows in hours. The brief schema, content engine and distribution hub stay the same &mdash; only the cadence template changes.</div>
431    </div>
432
433    <div class="section-title">Tech Stack</div>
434    <div class="ref-grid">
435      <div class="ref-card">
436        <h3>Orchestration</h3>
437        <div class="ref-row"><span class="label">n8n</span><span class="value">20+ workflows</span></div>
438        <div class="ref-row"><span class="label">PostgreSQL</span><span class="value">content queue + CRM</span></div>
439        <div class="ref-row"><span class="label">Redis</span><span class="value">rate limits + cache</span></div>
440        <div class="ref-row"><span class="label">Telegram bot</span><span class="value">ops + alerts</span></div>
441      </div>
442      <div class="ref-card">
443        <h3>Content + AI</h3>
444        <div class="ref-row"><span class="label">DeepSeek</span><span class="value">copy + chat AI</span></div>
445        <div class="ref-row"><span class="label">GPT Image</span><span class="value">visuals</span></div>
446        <div class="ref-row"><span class="label">Leon (Remotion)</span><span class="value">video render API</span></div>
447        <div class="ref-row"><span class="label">Google TTS</span><span class="value">voiceover</span></div>
448      </div>
449      <div class="ref-card">
450        <h3>Capture + Nurture</h3>
451        <div class="ref-row"><span class="label">Mautic</span><span class="value">landing pages + forms</span></div>
452        <div class="ref-row"><span class="label">Listmonk</span><span class="value">email (36 lists)</span></div>
453        <div class="ref-row"><span class="label">Chatwoot</span><span class="value">omnichannel inbox</span></div>
454        <div class="ref-row"><span class="label">Umami</span><span class="value">analytics + funnels</span></div>
455      </div>
456    </div>
457
458  </div>
459</div>
460
461<div id="tab-architecture" class="tab-content">
462  <div class="container">
463
464    <div class="section-title">System Architecture</div>
465    <p>GAEL is structured as a 6-layer pipeline. Every layer is self-hosted on a single Oracle Cloud ARM VM, orchestrated by n8n workflows, and state lives in PostgreSQL. No external SaaS dependencies for the core pipeline &mdash; only the channel APIs themselves.</p>
466
467    <div class="arch-diagram" style="padding:32px; overflow-x:auto;">
468      <div style="display:flex; flex-direction:column; gap:14px; min-width:760px;">
469
470        <div style="background:linear-gradient(135deg, var(--cyan-dim), rgba(14,165,233,0.03)); border:1px solid var(--cyan); border-radius:12px; padding:16px 22px;">
471          <div style="font-size:11px; color:var(--cyan); font-family:'DM Mono',monospace; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:8px;">Layer 1 &middot; Input</div>
472          <div style="font-size:14px; color:var(--white);">Campaign brief webhook &middot; scheduled triggers &middot; Telegram commands</div>
473        </div>
474
475        <div style="background:linear-gradient(135deg, rgba(168,85,247,0.1), rgba(168,85,247,0.02)); border:1px solid var(--purple); border-radius:12px; padding:16px 22px;">
476          <div style="font-size:11px; color:var(--purple); font-family:'DM Mono',monospace; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:8px;">Layer 2 &middot; Content Engine (n8n)</div>
477          <div style="font-size:14px; color:var(--white);">Campaign Router &rarr; DeepSeek text gen &middot; GPT Image visuals &middot; Leon video render &middot; Mautic page gen</div>
478        </div>
479
480        <div style="background:linear-gradient(135deg, rgba(168,85,247,0.1), rgba(168,85,247,0.02)); border:1px solid var(--purple); border-radius:12px; padding:16px 22px;">
481          <div style="font-size:11px; color:var(--purple); font-family:'DM Mono',monospace; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:8px;">Layer 3 &middot; Distribution Hub</div>
482          <div style="font-size:14px; color:var(--white);">Postgres content_queue &rarr; hourly publisher &rarr; Pinterest &middot; Facebook &middot; Twitter/X &middot; Instagram &middot; TikTok &middot; Listmonk</div>
483        </div>
484
485        <div style="background:linear-gradient(135deg, var(--grn-dim), rgba(16,185,129,0.02)); border:1px solid var(--green); border-radius:12px; padding:16px 22px;">
486          <div style="font-size:11px; color:var(--green); font-family:'DM Mono',monospace; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:8px;">Layer 4 &middot; Capture (Mautic)</div>
487          <div style="font-size:14px; color:var(--white);">Landing pages per campaign &middot; forms with UTM tracking &middot;
487 lead scoring by engagement</div>
488        </div>
489
490        <div style="background:linear-gradient(135deg, var(--grn-dim), rgba(16,185,129,0.02)); border:1px solid var(--green); border-radius:12px; padding:16px 22px;">
491          <div style="font-size:11px; color:var(--green); font-family:'DM Mono',monospace; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:8px;">Layer 5 &middot; Nurture</div>
492          <div style="font-size:14px; color:var(--white);">Listmonk drip campaigns (36 segmented lists) &middot; Chatwoot omnichannel inbox with AI agent</div>
493        </div>
494
495        <div style="background:linear-gradient(135deg, var(--yel-dim), rgba(245,158,11,0.02)); border:1px solid var(--yellow); border-radius:12px; padding:16px 22px;">
496          <div style="font-size:11px; color:var(--yellow); font-family:'DM Mono',monospace; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:8px;">Layer 6 &middot; Intelligence</div>
497          <div style="font-size:14px; color:var(--white);">Umami funnels &middot; engagement scoring &middot; A/B test feedback loop &rarr; back into Layer 1</div>
498        </div>
499
500      </div>
501    </div>
502
503    <div class="section-title">Core n8n Workflows</div>
504    <table class="data-table">
505      <thead>
506        <tr><th>Workflow</th><th>Trigger</th><th>What it does</th></tr>
507      </thead>
508      <tbody>
509        <tr><td><code>campaign-launcher</code></td><td>Webhook / manual</td><td>Receives brief, decides channels, kicks off content generation, starts email sequence</td></tr>
510        <tr><td><code>content-generator</code></td><td>Called by launcher</td><td>DeepSeek prompt chain adapts copy per channel (length, tone, hashtags)</td></tr>
511        <tr><td><code>multi-publisher</code></td><td>Cron hourly</td><td>Pulls from content_queue, publishes to the right API, marks published</td></tr>
512        <tr><td><code>lead-processor</code></td><td>Mautic webhook</td><td>UTM attribution, Listmonk list assignment, nurture kickoff, Chatwoot contact creation</td></tr>
513        <tr><td><code>chatwoot-ai-agent</code></td><td>Chatwoot webhook</td><td>Detects language, loads context, DeepSeek reply, escalates hot leads to Telegram</td></tr>
514        <tr><td><code>analytics-loop</code></td><td>Cron nightly</td><td>Umami event rollup, DeepSeek scoring, drafts new content ideas, notifies via Telegram</td></tr>
515      </tbody>
516    </table>
517
518    <div class="section-title">Content Queue Schema</div>
519    <table class="data-table">
520      <thead>
521        <tr><th>Column</th><th>Type</th><th>Purpose</th></tr>
522      </thead>
523      <tbody>
524        <tr><td><code>campaign_id</code></td><td>VARCHAR</td><td>Links every post back to the originating brief</td></tr>
525        <tr><td><code>platform</code></td><td>VARCHAR</td><td>pinterest / facebook / twitter / instagram / tiktok / email</td></tr>
526        <tr><td><code>content</code></td><td>JSONB</td><td>Platform-specific payload (copy, hashtags, media URLs)</td></tr>
527        <tr><td><code>scheduled_at</code></td><td>TIMESTAMP</td><td>When the publisher should fire</td></tr>
528        <tr><td><code>status</code></td><td>VARCHAR</td><td>pending / published / failed &mdash; drives retry logic</td></tr>
529        <tr><td><code>response</code></td><td>JSONB</td><td>Raw API response for audit + debugging</td></tr>
530      </tbody>
531    </table>
532
533    <div class="note note-cyan">
534      <div>Adding a new channel is a matter of implementing one n8n HTTP node against the platform API and inserting rows into <code>content_queue</code> with the new <code>platform</code> value. The publisher does the rest.</div>
535    </div>
536
537  </div>
538</div>
539
540<div id="tab-challenges" class="tab-content">
541  <div class="container">
542
543    <div class="section-title">Engineering Challenges Solved</div>
544    <div class="challenge-grid">
545      <div class="challenge-card">
546        <h4><span class="emoji">&#128204;</span> Pinterest Board ID Precision Loss</h4>
547        <p>Pinterest Board IDs exceed <code>Number.MAX_SAFE_INTEGER</code> (&gt;10<sup>18</sup>). Any dynamic JS computation silently produces wrong IDs and posts land on the wrong board.</p>
548        <div class="solution"><strong>Solution:</strong> Static string mapping in the publisher workflow instead of dynamic computation. Discovered after hours debugging "wrong board" errors &mdash; the kind of edge case you only catch in production.</div>
549      </div>
550      <div class="challenge-card">
551        <h4><span class="emoji">&#127760;</span> Native Multilingual Copy (Not Translation)</h4>
552        <p>Machine-translated marketing copy sounds robotic and kills engagement. But generating content 3&times; multiplies LLM cost and drift.</p>
553        <div class="solution"><strong>Solution:</strong> Single DeepSeek prompt with language-specific "voice" variables. Scripts are written natively in ES/EN/PT from the same brief, not translated. Cost stays flat, quality stays high.</div>
554      </div>
555      <div class="challenge-card">
556        <h4><span class="emoji">&#9201;&#65039;</span> Rate Limits Across 6 Channels</h4>
557        <p>Every platform has different rate limits: Twitter 1500/mo, Listmonk 300/day, Pinterest polite backoff, Facebook burst limits.</p>
558        <div class="solution"><strong>Solution:</strong> Centralized content_queue with per-platform scheduling windows. Redis tracks burst counters. Publisher respects platform caps and defers overflow to the next window automatically.</div>
559      </div>
560      <div class="challenge-card">
561        <h4><span class="emoji">&#128273;</span> Instagram Meta Business Lockout</h4>
562        <p>Meta restricted API access mid-project, killing direct publishing. A hard blocker for most stacks.<
562/p>
563        <div class="solution"><strong>Solution:</strong> Pivoted Instagram to Chatwoot as the inbox layer, which preserves DM conversations and the AI chat agent. Publishing stays manual for IG, but capture + nurture kept working &mdash; loss contained.</div>
564      </div>
565      <div class="challenge-card">
566        <h4><span class="emoji">&#128230;</span> State Consistency Across 20+ Workflows</h4>
567        <p>n8n workflows run independently. A lead can trigger lead-processor before chatwoot-ai-agent finishes writing the contact.</p>
568        <div class="solution"><strong>Solution:</strong> PostgreSQL is the single source of truth. Every workflow reads/writes through explicit upserts with conflict resolution. No in-workflow state, no race conditions.</div>
569      </div>
570      <div class="challenge-card">
571        <h4><span class="emoji">&#128266;</span> AI Chat Without Hallucination</h4>
572        <p>Letting a raw LLM answer lead DMs is a legal and brand risk. But scripted bots feel dead.</p>
573        <div class="solution"><strong>Solution:</strong> DeepSeek gets strict system prompt + contact context from Postgres + product catalog as retrieval. Hot leads detected by intent scoring are always routed to a human via Telegram before any commitment.</div>
574      </div>
575    </div>
576
577    <div class="note note-cyan">
578      &#128161; <span>This entire marketing OS pattern ports to any vertical. Change the brief schema, swap the language mix, point to different channel APIs &mdash; the orchestration layer stays the same.</span>
579    </div>
580
581  </div>
582</div>
583
584<div id="tab-results" class="tab-content">
585  <div class="container">
586
587    <div class="section-title">Results &amp; Metrics</div>
588    <div class="results-grid">
589      <div class="result-card">
590        <div class="result-val">6</div>
591        <div class="result-label">Channels<br>orchestrated</div>
592      </div>
593      <div class="result-card">
594        <div class="result-val">20+</div>
595        <div class="result-label">n8n workflows<br>in production</div>
596      </div>
597      <div class="result-card">
598        <div class="result-val"></div>
599        <div class="result-label">Pins ready<br>+ dynamic gen</div>
600      </div>
601      <div class="result-card">
602        <div class="result-val">$0</div>
603        <div class="result-label">Ad spend<br>CAC organic</div>
604      </div>
605    </div>
606
607    <div class="section-title">Automation Coverage</div>
608    <div class="ref-grid">
609      <div class="ref-card">
610        <h3>Publishing Cadence</h3>
611        <div class="ref-row"><span class="label">Pinterest</span><span class="value">3 pins/day/lang</span></div>
612        <div class="ref-row"><span class="label">Facebook</span><span class="value">1-3 posts/day</span></div>
613        <div class="ref-row"><span class="label">Twitter/X</span><span class="value">2-5 tweets/day (1500/mo cap)</span></div>
614        <div class="ref-row"><span class="label">Email (Listmonk)</span><span class="value">multi-list drip campaigns</span></div>
615        <div class="ref-row"><span class="label">Video (via Leon)</span><span class="value">3/day multi-platform</span></div>
616      </div>
617      <div class="ref-card">
618        <h3>SaaS Replaced</h3>
619        <div class="ref-row"><span class="label">Zapier / Make</span><span class="value">&rarr; n8n</span></div>
620        <div class="ref-row"><span class="label">Mailchimp / SendGrid</span><span class="value">&rarr; Listmonk</span></div>
621        <div class="ref-row"><span class="label">HubSpot / ActiveCampaign</span><span class="value">&rarr; Mautic</span></div>
622        <div class="ref-row"><span class="label">Intercom / Drift</span><span class="value">&rarr; Chatwoot</span></div>
623        <div class="ref-row"><span class="label">Google Analytics</span><span class="value">&rarr; Umami</span></div>
624        <div class="ref-row"><span class="label">Monthly SaaS saved</span><span class="value">~$400+/mo</span></div>
625      </div>
626    </div>
627
628    <div class="section-title">Deliverables</div>
629    <ul class="deliverables">
630      <li>
631        <div class="icon">&#129302;</div>
632        <div><strong>20+ n8n Workflows</strong>Campaign launcher, content generator, multi-publisher, lead processor, chat AI agent, intelligence monitor</div>
633      </li>
634      <li>
635        <div class="icon">&#128230;</div>
636        <div><strong>Content Queue Schema</strong>PostgreSQL source of truth for every scheduled post, across every platform, with full audit trail</div>
637      </li>
638      <li>
639        <div class="icon">&#128231;</div>
640        <div><strong>Trilingual Email System</strong>Listmonk (36 lists) + Mautic (9 campaigns, 15 emails, lead scoring) &mdash; all natively in ES/EN/PT</div>
641      </li>
642      <li>
643        <div class="icon">&#128172;</div>
644        <div><strong>Omnichannel Chat Agent</strong>Chatwoot inbox with DeepSeek AI agent, language auto-detect, hot-lead escalation to Telegram</div>
645      </li>
646      <li>
647        <div class="icon">&#128200;</div>
648        <div><strong>Intelligence + Analytics</strong>Umami funnels + engagement scoring + A/B test feedback loop back into campaign planning</
648div>
649      </li>
650      <li>
651        <div class="icon">&#128218;</div>
652        <div><strong>Operations Runbook</strong>Telegram command interface (<code>/campaign</code>, <code>/stats</code>, <code>/queue</code>, <code>/lead</code>) and full docs for handover</div>
653      </li>
654    </ul>
655
656    <div class="section-title">Technology Tags</div>
657    <div class="tech-tags">
658      <span class="tech-tag">n8n</span>
659      <span class="tech-tag">PostgreSQL</span>
660      <span class="tech-tag">Redis</span>
661      <span class="tech-tag">DeepSeek</span>
662      <span class="tech-tag">GPT Image</span>
663      <span class="tech-tag">Listmonk</span>
664      <span class="tech-tag">Mautic</span>
665      <span class="tech-tag">Chatwoot</span>
666      <span class="tech-tag">Umami</span>
667      
668      <span class="tech-tag">Pinterest API</span>
669      <span class="tech-tag">Facebook Graph API</span>
670      <span class="tech-tag">Twitter API</span>
671      <span class="tech-tag">TikTok API</span>
672      <span class="tech-tag">Telegram Bot</span>
673      <span class="tech-tag">Docker</span>
674      <span class="tech-tag">Oracle Cloud</span>
675      <span class="tech-tag">Multilingual</span>
676      <span class="tech-tag">Lead Scoring</span>
677      <span class="tech-tag">Self-Hosted</span>
678    </div>
679
680  </div>
681</div>
682
683<div id="tab-contact" class="tab-content">
684  <div class="container">
685
686    <div class="cta-section">
687      <h2>Need a Marketing OS That <span style="color:var(--cyan)">Runs Itself</span>?</h2>
688      <p>I build self-hosted, full-funnel marketing systems &mdash; from campaign brief to publish to capture to nurture. One VM, one codebase, zero SaaS lock-in.</p>
689      <a href="https://contra.com/leongael" class="cta-btn" target="_blank">&#128172; Hire Me on Contra</a>
690      <a href="/" class="cta-btn-outline">&#8592; Back to portfolio</a>
691    </div>
692
693    <div class="section-title">What I Build</div>
694    <div class="ref-grid">
695      <div class="ref-card">
696        <h3>Voice AI Agents</h3>
697        <div class="ref-row"><span class="label">Inbound receptionists</span><span class="value">Any language</span></div>
698        <div class="ref-row"><span class="label">Outbound sales agents</span><span class="value">Cold + warm</span></div>
699        <div class="ref-row"><span class="label">Appointment booking</span><span class="value">Real-time CRM</span></div>
700        <div class="ref-row"><span class="label">Emergency routing</span><span class="value">Live transfer</span></div>
701      </div>
702      <div class="ref-card">
703        <h3>Content Automation</h3>
704        <div class="ref-row"><span class="label">AI content generation</span><span class="value">Multilingual</span></div>
705        <div class="ref-row"><span class="label">Blog &amp; social publishing</span><span class="value">Multi-platform</span></div>
706        <div class="ref-row"><span class="label">Email drip campaigns</span><span class="value">Full funnel</span></div>
707        <div class="ref-row"><span class="label">Trend intelligence</span><span class="value">Automated</span></div>
708      </div>
709      <div class="ref-card">
710        <h3>Video Production</h3>
711        <div class="ref-row"><span class="label">AI script writing</span><span class="value">Native multilingual</span></div>
712        <div class="ref-row"><span class="label">AI image generation</span><span class="value">GPT Image</span></div>
713        <div class="ref-row"><span class="label">TTS voiceover</span><span class="value">WaveNet voices</span></div>
714        <div class="ref-row"><span class="label">Remotion render</span><span class="value">Custom templates</span></div>
715      </div>
716      <div class="ref-card">
717        <h3>
717Infrastructure</h3>
718        <div class="ref-row"><span class="label">N8N workflows</span><span class="value">Self-hosted</span></div>
719        <div class="ref-row"><span class="label">PostgreSQL + Redis</span><span class="value">Production-grade</span></div>
720        <div class="ref-row"><span class="label">Monitoring &amp; alerts</span><span class="value">24/7</span></div>
721        <div class="ref-row"><span class="label">Documentation</span><span class="value">Runbooks + guides</span></div>
722      </div>
723    </div>
724
725    <div class="note note-green">
726      &#9989; <span>Based in Honduras (UTC-6). Available for async collaboration across all timezones. Fluent in English, Spanish, and French.</span>
727    </div>
728
729  </div>
730</div>
731
732<!-- FOOTER -->
733<div class="footer">
734  <p>&copy; 2026 &middot; Freelance engineering portfolio</p>
735</div>
736
737<!-- SCRIPTS -->
738<script>
739function switchTab(name) {
740  document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
741  document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
742  document.getElementById('tab-' + name).classList.add('active');
743  event.target.classList.add('active');
744  window.scrollTo({ top: 0, behavior: 'smooth' });
745}
746</script>
746
747
748</div><!-- /page-content -->
749
750</body>
751</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.