{"workflow":{"id":13993,"name":"Send weekly GA4 WoW email reports with Gemini AI executive summary","views":94,"recentViews":1,"totalViews":94,"createdAt":"2026-03-11T07:45:20.281Z","description":"## Weekly Google Analytics 4 Report - Full WoW Tracking & Auto-Generated with Gemini AI\n\nStop manually building weekly analytics reports. This workflow automatically fetches your GA4 data every Monday morning, generates an AI-written executive summary using Gemini, builds a premium formatted HTML email with deep Week-over-Week (WoW) comparisons for *every* metric, and delivers it straight to your stakeholders' inboxes — fully hands-free.\n\n## 👤 Who is this for?\n\n* **Marketing teams** and **agency owners** who report GA4 metrics weekly to clients\n* **Product managers** and **founders** who want a Monday morning performance digest\n* **Freelancers** managing analytics for multiple clients who want to automate reporting\n* Anyone who spends 30–60 minutes every week manually pulling GA4 numbers and calculating WoW changes\n\n## 🚩 What problem does this solve?\n\nManual GA4 reporting is repetitive, error-prone, and time-consuming. This template eliminates that entirely — every Monday at 8:00 AM in your configured timezone, a fully formatted report lands in your inbox. With the new dual-node architecture, it automatically calculates accurate WoW trends for your overall metrics, specific pages, traffic sources, and more, all contextualized by a Gemini-generated executive summary.\n\n## ⚙️ What this workflow does\n\n1. **⏰ Triggers every Monday at 8:00 AM** via the Schedule Trigger node\n2. **📡 Fetches 14 GA4 reports in parallel** - pulling both *Current Week* and *Previous Week* data simultaneously for speed:\n    - Overview metrics\n    - Top 5 Screens / Pages by views\n    - Top 5 Traffic Sources / Referrals\n    - Top 5 Events by count\n    - Top 5 Countries by sessions\n    - Device breakdown (mobile / desktop / tablet)\n    - New vs Returning users\n\n\n3. **🔀 Merges all 14 responses** and passes the complete historical dataset forward\n4. **🤖 Gemini writes a 3-5 bullet point summary** analyzing the full WoW dataset to highlight performance trends, audience behaviour, and actionable recommendations\n5. **🧮 Code node processes all data** - aligns current vs. previous week data, calculates WoW % changes for *every single category*, handles new/dropped entries, and builds the full inline-CSS HTML email\n6. **📧 Sends the report** via standard SMTP / Email node to your configured recipients\n\n## 📧 What the email report includes\n\n* **Header** - dark luxury card with 4 KPI tiles (Users, Sessions, Bounce Rate, Avg Duration) and WoW arrows\n* **AI Executive Summary** - 3-5 bullet point Gemini-generated insight (hidden automatically if Gemini fails)\n* **Overview Table** - all 5 core metrics with This Week / Last Week / WoW % change pill badges\n* **Audience** - New vs Returning users with visual progress bars and WoW changes\n* **Top Screens** - ranked by views, including previous week values and WoW trend pills. `(not set)` and `(empty)` rows are preserved for transparency\n* **Traffic Sources** - top referral channels with WoW changes; direct traffic auto-labelled as `Direct / App Open`\n* **Top Events** - tracks interaction trends with WoW changes; system events (first_open, os_update, etc.) are filtered out automatically\n* **Geography** - top 5 countries by sessions with WoW comparisons\n* **Devices** - mobile / desktop / tablet with visual progress bars and WoW trend pills\n\n## 🛠️ Setup Instructions\n\n### Step 1 - Google Analytics 4 Credential\n\n1. Go to n8n Credentials → Add new → search **Google Analytics OAuth2**\n2. Sign in with the Google account that has access to your GA4 property\n3. Assign this credential to all **14** GA4 nodes\n\n### Step 2 - Set your GA4 Property ID\n\n1. Open each of the **14** GA4 nodes\n2. In the **Property ID** field, replace `{YOUR_PROPERTY_ID}` with your GA4 numeric property ID\n3. Find your Property ID at: **GA4 Admin → Property Settings → Property ID** (looks like `123456789`)\n\n### Step 3 - Gemini API Credential\n\n1. Get your free API key at [aistudio.google.com](https://aistudio.google.com/api-keys)\n2. Go to n8n Credentials → Add new → search **Google Gemini**\n3. Paste your API key and assign it to the `Generate AI Summary` node\n\n### Step 4 - Email / SMTP Credential\n\n1. Go to n8n Credentials → Add new → search **SMTP** (or swap the node for Gmail OAuth2 if preferred)\n2. Enter your email host, port, and login credentials\n3. Assign to the `Send Weekly Report` node\n\n### Step 5 - Set Recipients\n\n1. Open the `Send Weekly Report` node (or the Code node depending on your mapping preference)\n2. Update the **To Email** field with your recipient address(es): `'email@example.com, email2@example.com'`\n3. Alternatively, update the `recipients:` line at the bottom of the `Build Report & Email HTML` Code node.\n\n### Step 6 - Set Timezone\n\n1. Open the `Weekly Monday Trigger` node\n2. Update the workflow settings to match your local timezone so the 8:00 AM trigger fires correctly.\n\n## 🔧 How to Customise\n\n* **Change the schedule** → Open the trigger node, adjust the day and time to any cadence you need (daily, bi-weekly, monthly)\n* **Change the client brand in the footer** → Search for `AppStoneLab Technologies` in the Code node and replace it with your client or company name\n* **Filter or change events** → In the Code node, find the `EXCLUDE_EVENTS` array and add/remove event names to control which events appear in the report\n* **Change the AI summary language or tone** → Edit the prompt inside the `Generate AI Summary` Gemini node to match your client's preferred reporting style\n\n## 📦 Requirements\n\n| Service | Purpose | Free Tier Available |\n| --- | --- | --- |\n| Google Analytics 4 | Source of all report data | ✅ Yes |\n| Google Gemini API | AI executive summary generation | ✅ Yes (via AI Studio) |\n| SMTP / Email | Email delivery | ✅ Yes |\n\n## ⚠️ Notes\n\n* **Mobile app properties** - If your GA4 property tracks a mobile app, the workflow uses `unifiedScreenName` instead of `pagePath`, which works correctly for both web and app properties\n* **WoW calculations** - Bounce rate change is intentionally inverted (a decrease is shown as positive/green). If a metric like a specific page or country is new this week, the workflow dynamically tags it as \"New\" instead of breaking the calculation.\n* **Gemini failure handling** - If the Gemini node fails for any reason, the AI summary section is automatically hidden and the rest of the report sends normally\n* **Execute Once** - All 14 GA4 nodes have Execute Once enabled to prevent duplicate rows from the merge operation","workflow":{"id":"7sF6TGsrR5dlGVAx","meta":{"instanceId":"8329856bd6925d4f2750e54c41dcb3cdefa9cd6cf7a589adead15ca668174a9f","templateCredsSetupCompleted":true},"name":"Weekly GA4 Analytics Report For Website/App","tags":[],"nodes":[{"id":"242dcef9-b89b-4d05-b9dd-616c8f51942c","name":"GA4 - Overview Current Week","type":"n8n-nodes-base.googleAnalytics","position":[-544,-464],"parameters":{"endDate":"={{$now.minus({days: 1}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 7}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{},{"listName":"sessions"},{"name":"bounce_rate","listName":"custom","expression":"bounceRate"},{"name":"average_session_dur","listName":"custom","expression":"averageSessionDuration"},{"name":"new_users","listName":"custom","expression":"newUsers"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{}]},"additionalFields":{}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"eb6868d0-0dab-49ab-99ba-b34108d16a17","name":"GA4 - Overview Previous Week","type":"n8n-nodes-base.googleAnalytics","position":[-320,-464],"parameters":{"endDate":"={{$now.minus({days: 8}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 14}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{},{"listName":"sessions"},{"name":"bounce_rate","listName":"custom","expression":"bounceRate"},{"name":"average_session_dur","listName":"custom","expression":"averageSessionDuration"},{"name":"new_users","listName":"custom","expression":"newUsers"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{}]},"additionalFields":{}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"dca8f70f-f4ad-439a-b8b8-e9395d5b540e","name":"GA4 - Top 5 Pages","type":"n8n-nodes-base.googleAnalytics","position":[-544,-272],"parameters":{"limit":5,"endDate":"={{$now.minus({days: 1}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 7}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"name":"screen_page_view","listName":"custom","expression":"screenPageViews"},{"name":"average_session_duration","listName":"custom","expression":"averageSessionDuration"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"name":"unifiedScreenName","listName":"other"}]},"additionalFields":{}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"85950514-c48e-4cab-b3c5-159ecc0d7d4a","name":"GA4 - Top 5 Referrals","type":"n8n-nodes-base.googleAnalytics","position":[-544,-80],"parameters":{"limit":5,"endDate":"={{$now.minus({days: 1}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 7}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"listName":"sessions"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"name":"sessionSourceMedium","listName":"other"}]},"additionalFields":{"orderByUI":{"metricOrderBy":[{"desc":true,"metricName":"sessions"}]}}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"0d766056-e67f-4827-838c-e6fe339231d9","name":"GA4 - Top 5 Events","type":"n8n-nodes-base.googleAnalytics","position":[-544,112],"parameters":{"limit":5,"endDate":"={{$now.minus({days: 1}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 7}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"name":"event_count","listName":"custom","expression":"eventCount"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"name":"eventName","listName":"other"}]},"additionalFields":{"orderByUI":{"metricOrderBy":[{"desc":true,"metricName":"=event_count"}]}}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"9f0dabdd-36c0-41d3-9052-b9d2cdf8ea85","name":"GA4 - Top 5 Countries","type":"n8n-nodes-base.googleAnalytics","position":[-544,304],"parameters":{"limit":5,"endDate":"={{$now.minus({days: 1}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 7}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"listName":"sessions"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"listName":"country"}]},"additionalFields":{"orderByUI":{"metricOrderBy":[{"desc":true,"metricName":"sessions"}]}}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"1c982d79-9f8a-47c4-bb35-1b210abb1524","name":"GA4 - Device Breakdown","type":"n8n-nodes-base.googleAnalytics","position":[-544,496],"parameters":{"endDate":"={{$now.minus({days: 1}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 7}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"listName":"sessions"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"listName":"deviceCategory"}]},"additionalFields":{"orderByUI":{"metricOrderBy":[{"desc":true,"metricName":"sessions"}]}}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"7de9e216-c86a-44b3-9ae5-1872e44e264a","name":"New vs Returning Users Breakdown","type":"n8n-nodes-base.googleAnalytics","position":[-544,688],"parameters":{"endDate":"={{$now.minus({days: 1}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 7}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"listName":"sessions"},{}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"name":"newVsReturning","listName":"other"}]},"additionalFields":{}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"0ea9bb61-586a-4d25-8a15-c7b875464470","name":"Weekly Monday Trigger","type":"n8n-nodes-base.scheduleTrigger","position":[-976,112],"parameters":{"rule":{"interval":[{"field":"weeks","triggerAtDay":[1],"triggerAtHour":8}]}},"typeVersion":1.3},{"id":"43f5b7c2-562e-4003-9517-7022ab148529","name":"Wait for All GA4 Data","type":"n8n-nodes-base.merge","position":[64,32],"parameters":{"mode":"combine","options":{"includeUnpaired":true},"combineBy":"combineByPosition","numberInputs":7},"typeVersion":3.2},{"id":"404f04c1-57ff-456e-917e-62ee967655be","name":"Generate AI Summary","type":"@n8n/n8n-nodes-langchain.googleGemini","position":[368,112],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"models/gemini-3.1-flash-lite-preview","cachedResultName":"models/gemini-3.1-flash-lite-preview"},"options":{},"messages":{"values":[{"content":"=You are a professional digital analytics consultant. Analyze the full week-over-week data below and write a concise executive summary.\n\n=== OVERVIEW ===\nTHIS WEEK — Users: {{$('GA4 - Overview Current Week').first().json.totalUsers}} | Sessions: {{$('GA4 - Overview Current Week').first().json.sessions}} | New Users: {{$('GA4 - Overview Current Week').first().json.new_users}} | Bounce Rate: {{$('GA4 - Overview Current Week').first().json.bounce_rate}} | Avg Duration (sec): {{$('GA4 - Overview Current Week').first().json.average_session_dur}}\nPREV WEEK — Users: {{$('GA4 - Overview Previous Week').first().json.totalUsers}} | Sessions: {{$('GA4 - Overview Previous Week').first().json.sessions}} | New Users: {{$('GA4 - Overview Previous Week').first().json.new_users}} | Bounce Rate: {{$('GA4 - Overview Previous Week').first().json.bounce_rate}} | Avg Duration (sec): {{$('GA4 - Overview Previous Week').first().json.average_session_dur}}\n\n=== TOP SCREENS ===\nTHIS WEEK: {{$('GA4 - Top 5 Pages').all().map((i,idx) => `${idx+1}. ${i.json.unifiedScreenName} (${i.json.screen_page_view} views)`).join(' | ')}}\nPREV WEEK: {{$('GA4 - Top 5 Pages Previous Week').all().map((i,idx) => `${idx+1}. ${i.json.unifiedScreenName} (${i.json.screen_page_view} views)`).join(' | ')}}\n\n=== TRAFFIC SOURCES ===\nTHIS WEEK: {{$('GA4 - Top 5 Referrals').all().map((i,idx) => `${idx+1}. ${i.json.sessionSourceMedium} (${i.json.sessions} sessions)`).join(' | ')}}\nPREV WEEK: {{$('GA4 - Top 5 Referrals Previous Week').all().map((i,idx) => `${idx+1}. ${i.json.sessionSourceMedium} (${i.json.sessions} sessions)`).join(' | ')}}\n\n=== TOP EVENTS ===\nTHIS WEEK: {{$('GA4 - Top 5 Events').all().map((i,idx) => `${idx+1}. ${i.json.eventName} (${i.json.event_count})`).join(' | ')}}\nPREV WEEK: {{$('GA4 - Top 5 Events Previous Week').all().map((i,idx) => `${idx+1}. ${i.json.eventName} (${i.json.event_count})`).join(' | ')}}\n\n=== TOP COUNTRIES ===\nTHIS WEEK: {{$('GA4 - Top 5 Countries').all().map((i,idx) => `${idx+1}. ${i.json.country} (${i.json.sessions} sessions)`).join(' | ')}}\nPREV WEEK: {{$('GA4 - Top 5 Countries Previous Week').all().map((i,idx) => `${idx+1}. ${i.json.country} (${i.json.sessions} sessions)`).join(' | ')}}\n\n=== DEVICES ===\nTHIS WEEK: {{$('GA4 - Device Breakdown').all().map(i => `${i.json.deviceCategory}: ${i.json.sessions}`).join(' | ')}}\nPREV WEEK: {{$('GA4 - Device Breakdown Previous Week').all().map(i => `${i.json.deviceCategory}: ${i.json.sessions}`).join(' | ')}}\n\n=== NEW VS RETURNING ===\nTHIS WEEK: {{$('New vs Returning Users Breakdown').all().map(i => `${i.json.newVsReturning}: ${i.json.totalUsers} users`).join(' | ')}}\nPREV WEEK: {{$('GA4 - New vs Returning Previous Week').all().map(i => `${i.json.newVsReturning}: ${i.json.totalUsers} users`).join(' | ')}}\n\n=== END OF DATA ===\n\nWrite exactly 3 to 5 bullet points summarizing the most important trends and changes. If a country, screen, or source appears in one week but not the other, treat it as entered or dropped from the top 5.\n\nRules:\n- Start each bullet with a dash (-)\n- Single sentence per bullet, maximum 25 words\n- Reference specific numbers and week-over-week changes\n- No markdown, no bold, no headers\n- Do not start any bullet with \"Based on the data\""}]},"builtInTools":{}},"credentials":{"googlePalmApi":{"id":"credential-id","name":"Google Gemini(PaLM) Api account"}},"executeOnce":true,"typeVersion":1.1},{"id":"b173f712-0bfe-4ec1-8ad4-ef011a7bb3a1","name":"Build Report & Email HTML","type":"n8n-nodes-base.code","position":[864,112],"parameters":{"jsCode":"// ============================================================\n// GA4 WEEKLY REPORT v9 — ALL ALIGNMENT FIXED\n// AppStoneLab Technologies\n// ============================================================\n\n// ── 1. PULL DATA ─────────────────────────────────────────────\n\nconst currentWeek  = $('GA4 - Overview Current Week').first().json;\nconst previousWeek = $('GA4 - Overview Previous Week').first().json;\n\nconst pages = $('GA4 - Top 5 Pages').all()\n  .map(i => i.json)\n  .map(p => ({ ...p, unifiedScreenName: (!p.unifiedScreenName || p.unifiedScreenName === '') ? '(empty)' : p.unifiedScreenName }))\n  .slice(0, 5);\n\nconst pagesPrev = $('GA4 - Top 5 Pages Previous Week').all()\n  .map(i => i.json)\n  .map(p => ({ ...p, unifiedScreenName: (!p.unifiedScreenName || p.unifiedScreenName === '') ? '(empty)' : p.unifiedScreenName }));\n\nconst referrals = $('GA4 - Top 5 Referrals').all()\n  .map(i => i.json)\n  .map(r => ({ ...r, sessionSourceMedium: r.sessionSourceMedium === '(direct) / (none)' ? 'Direct / App Open' : (r.sessionSourceMedium || 'Direct / App Open') }));\n\nconst referralsPrev = $('GA4 - Top 5 Referrals Previous Week').all()\n  .map(i => i.json)\n  .map(r => ({ ...r, sessionSourceMedium: r.sessionSourceMedium === '(direct) / (none)' ? 'Direct / App Open' : (r.sessionSourceMedium || 'Direct / App Open') }));\n\nconst countries     = $('GA4 - Top 5 Countries').all().map(i => i.json);\nconst countriesPrev = $('GA4 - Top 5 Countries Previous Week').all().map(i => i.json);\nconst devices       = $('GA4 - Device Breakdown').all().map(i => i.json);\nconst devicesPrev   = $('GA4 - Device Breakdown Previous Week').all().map(i => i.json);\nconst newVsRet      = $('New vs Returning Users Breakdown').all().map(i => i.json);\nconst newVsRetPrev  = $('GA4 - New vs Returning Previous Week').all().map(i => i.json);\n\nconst EXCLUDE_EVENTS = ['first_visit','first_open','page_view','app_remove','os_update','app_update','app_store_refund'];\nconst events     = $('GA4 - Top 5 Events').all().map(i => i.json).filter(e => !EXCLUDE_EVENTS.includes(e.eventName)).slice(0, 5);\nconst eventsPrev = $('GA4 - Top 5 Events Previous Week').all().map(i => i.json).filter(e => !EXCLUDE_EVENTS.includes(e.eventName));\n\n// Gemini\nlet aiSummary = '';\ntry {\n  const g = $('Generate AI Summary').first().json;\n  aiSummary = g.content?.parts?.[0]?.text || g.text || g.output || g.response || g.message?.content || g.candidates?.[0]?.content?.parts?.[0]?.text || '';\n} catch(e) { aiSummary = ''; }\naiSummary = typeof aiSummary === 'string' ? aiSummary : '';\n\nlet formattedAiSummary = '';\nif (aiSummary.trim().length > 0) {\n  const lines = aiSummary.split('\\n').map(l => l.trim()).filter(l => l.length > 0);\n  const items = lines.map(l => `<li style=\"margin-bottom:10px;padding-left:4px;\">${l.replace(/^[\\*\\-•]\\s*/, '')}</li>`).join('');\n  formattedAiSummary = `<ul style=\"margin:0;padding-left:18px;font-family:Georgia,serif;font-size:13.5px;color:#3D3530;line-height:1.7;\">${items}</ul>`;\n}\n\n// ── 2. HELPERS ───────────────────────────────────────────────\n\nfunction wowChange(current, previous) {\n  const c = parseFloat(current), p = parseFloat(previous);\n  if (!p || p === 0) return { text: 'N/A', color: '#9CA3AF', arrow: '—', pill: '#F3F4F6', pillText: '#6B7280' };\n  const pct = ((c - p) / p) * 100;\n  const up = pct >= 0;\n  return { text: (up ? '+' : '') + pct.toFixed(1) + '%', color: up ? '#2D6A4F' : '#B91C1C', arrow: up ? '↑' : '↓', pill: up ? '#ECFDF5' : '#FEF2F2', pillText: up ? '#2D6A4F' : '#B91C1C' };\n}\n\nfunction wowChangeBounce(current, previous) {\n  const c = parseFloat(current), p = parseFloat(previous);\n  if (!p || p === 0) return { text: 'N/A', color: '#9CA3AF', arrow: '—', pill: '#F3F4F6', pillText: '#6B7280' };\n  const pct = ((c - p) / p) * 100;\n  const isGood = pct <= 0;\n  return { text: (pct >= 0 ? '+' : '') + pct.toFixed(1) + '%', color: isGood ? '#2D6A4F' : '#B91C1C', arrow: isGood ? '↓' : '↑', pill: isGood ? '#ECFDF5' : '#FEF2F2', pillText: isGood ? '#2D6A4F' : '#B91C1C' };\n}\n\nfunction listWoWWithPrev(currItem, prevItems, nameField, valueField) {\n  const prev = prevItems.find(p => p[nameField] === currItem[nameField]);\n  const currVal = parseInt(currItem[valueField] || 0);\n  if (!prev) return { wow: { text: 'New', arrow: '★', pill: '#FBF5EB', pillText: '#C9A96E' }, prevValue: null };\n  const prevVal = parseInt(prev[valueField] || 0);\n  if (!prevVal || prevVal === 0) return { wow: { text: 'New', arrow: '★', pill: '#FBF5EB', pillText: '#C9A96E' }, prevValue: null };\n  const pct = ((currVal - prevVal) / prevVal) * 100;\n  const up = pct >= 0;\n  return {\n    wow: { text: (up ? '+' : '') + pct.toFixed(1) + '%', arrow: up ? '↑' : '↓', pill: up ? '#ECFDF5' : '#FEF2F2', pillText: up ? '#2D6A4F' : '#B91C1C' },\n    prevValue: prevVal,\n  };\n}\n\nfunction formatDuration(s) {\n  const sec = parseFloat(s);\n  if (isNaN(sec)) return '0s';\n  const m = Math.floor(sec / 60), r = Math.round(sec % 60);\n  return m > 0 ? `${m}m ${r}s` : `${r}s`;\n}\nfunction fmt(n) { const num = parseInt(n); return isNaN(num) ? '0' : num.toLocaleString('en-IN'); }\nfunction fmtPct(n) { return (parseFloat(n) * 100).toFixed(1) + '%'; }\n\n// ── 3. WoW ───────────────────────────────────────────────────\n\nconst wow = {\n  totalUsers:  wowChange(currentWeek.totalUsers,          previousWeek.totalUsers),\n  sessions:    wowChange(currentWeek.sessions,            previousWeek.sessions),\n  newUsers:    wowChange(currentWeek.new_users,           previousWeek.new_users),\n  bounceRate:  wowChangeBounce(currentWeek.bounce_rate,   previousWeek.bounce_rate),\n  avgDuration: wowChange(currentWeek.average_session_dur, previousWeek.average_session_dur),\n};\n\nconst newUsersRow     = newVsRet.find(r => r.newVsReturning === 'new')           || { totalUsers: 0 };\nconst returningRow    = newVsRet.find(r => r.newVsReturning === 'returning')     || { totalUsers: 0 };\nconst newUsersPrevRow = newVsRetPrev.find(r => r.newVsReturning === 'new')       || { totalUsers: 0 };\nconst retUsersPrevRow = newVsRetPrev.find(r => r.newVsReturning === 'returning') || { totalUsers: 0 };\nconst totalNvR        = (parseInt(newUsersRow.totalUsers) + parseInt(returningRow.totalUsers)) || 1;\nconst wowNewUsers     = wowChange(newUsersRow.totalUsers,  newUsersPrevRow.totalUsers);\nconst wowRetUsers     = wowChange(returningRow.totalUsers, retUsersPrevRow.totalUsers);\nconst totalDeviceSessions = devices.reduce((s, d) => s + parseInt(d.sessions || 0), 0) || 1;\n\nconst startLabel     = $now.minus({days: 7}).toFormat('MMM dd, yyyy');\nconst endLabel       = $now.minus({days: 1}).toFormat('MMM dd, yyyy');\nconst prevStartLabel = $now.minus({days: 14}).toFormat('MMM dd');\nconst prevEndLabel   = $now.minus({days: 8}).toFormat('MMM dd');\n\n// ── 4. DESIGN TOKENS ─────────────────────────────────────────\n\nconst C = {\n  bg: '#F7F5F2', card: '#FFFFFF', headerBg: '#1C1C1C', headerSub: '#A89880',\n  gold: '#C9A96E', sectionText: '#1C1C1C', label: '#6B6560', value: '#1C1C1C',\n  prevValue: '#B0A89E', rowAlt: '#FDFCFB', rowBase: '#FFFFFF', divider: '#EDE8E2',\n  footerBg: '#1C1C1C', footerText: '#6B6560',\n};\n\n// ── 5. HTML COMPONENTS ───────────────────────────────────────\n\nconst sectionGap = `<tr><td colspan=\"5\" style=\"padding:8px 0;background:${C.bg};font-size:0;line-height:0;\">&nbsp;</td></tr>`;\n\nconst sectionHeader = (title, subtitle) => `\n<tr><td colspan=\"5\" style=\"padding:0;\">\n  <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n    <tr><td style=\"padding:18px 24px 13px;border-bottom:2px solid ${C.gold};background:${C.card};\">\n      <span style=\"font-family:Georgia,serif;font-size:15px;font-weight:bold;color:${C.sectionText};letter-spacing:0.3px;\">${title}</span>\n      ${subtitle ? `<span style=\"font-family:Arial,sans-serif;font-size:10px;color:${C.headerSub};margin-left:10px;text-transform:uppercase;letter-spacing:0.8px;\">${subtitle}</span>` : ''}\n    </td></tr>\n  </table>\n</td></tr>`;\n\n// colHeaders accepts array of {label, align} — aligns header text to match data cells\nconst colHeaders = (cols) => `\n<tr style=\"background:#F9F7F4;\">\n  ${cols.map((c, i) => {\n    const align = c.align || 'left';\n    const padLeft  = (align === 'left' && i === 0) ? '24px' : '12px';\n    const padRight = align === 'right' ? '20px' : '12px';\n    return `<td style=\"font-family:Arial,sans-serif;font-size:10px;font-weight:bold;color:${C.label};\n      padding:10px ${padRight} 10px ${padLeft};border-bottom:1px solid ${C.divider};\n      text-transform:uppercase;letter-spacing:1px;text-align:${align};white-space:nowrap;\">${c.label}</td>`;\n  }).join('')}\n</tr>`;\n\n// Change cell: \"was X\" + pill both right-aligned, stacked, in a right-aligned block\n// Both elements are display:block and text-align:right so they stack flush right\nfunction changeCell(wowObj, prevValue) {\n  const wasLine = prevValue !== null\n    ? `<span style=\"display:block;font-family:Arial,sans-serif;font-size:10px;color:#B0A89E;text-align:right;margin-bottom:4px;\">was ${fmt(prevValue)}</span>`\n    : '';\n  const pillLine = `<span style=\"display:block;text-align:right;\">\n    <span style=\"display:inline-block;background:${wowObj.pill};border-radius:12px;padding:3px 8px;\n      font-family:Arial,sans-serif;font-size:11px;font-weight:bold;color:${wowObj.pillText};white-space:nowrap;\">\n      ${wowObj.arrow}&thinsp;${wowObj.text}\n    </span>\n  </span>`;\n  return `<td style=\"padding:13px 20px 13px 12px;border-bottom:1px solid ${C.divider};vertical-align:middle;\">${wasLine}${pillLine}</td>`;\n}\n\n// Inline pill (no \"was\" — used in overview where Last Week column already exists)\nfunction pillOnly(wowObj) {\n  return `<span style=\"display:inline-block;background:${wowObj.pill};border-radius:12px;padding:3px 8px;\n    font-family:Arial,sans-serif;font-size:11px;font-weight:bold;color:${wowObj.pillText};white-space:nowrap;\">\n    ${wowObj.arrow}&thinsp;${wowObj.text}\n  </span>`;\n}\n\n// Overview row: label(left) / this week(right) / last week(right) / pill(right)\nconst overviewRow = (label, curr, prev, wowObj, isAlt) => `\n<tr style=\"background:${isAlt ? C.rowAlt : C.rowBase};\">\n  <td style=\"font-family:Arial,sans-serif;font-size:13px;color:${C.label};padding:14px 12px 14px 24px;border-bottom:1px solid ${C.divider};width:34%;text-align:left;\">${label}</td>\n  <td style=\"font-family:Georgia,serif;font-size:15px;font-weight:bold;color:${C.value};padding:14px 12px;border-bottom:1px solid ${C.divider};width:18%;text-align:right;\">${curr}</td>\n  <td style=\"font-family:Arial,sans-serif;font-size:13px;color:${C.prevValue};padding:14px 12px;border-bottom:1px solid ${C.divider};width:18%;text-align:right;\">${prev}</td>\n  <td style=\"padding:14px 20px 14px 12px;border-bottom:1px solid ${C.divider};width:30%;text-align:right;\">${pillOnly(wowObj)}</td>\n</tr>`;\n\n// List row: rank(center) / name(left) / value(right) / change cell(right-stacked)\nconst listRowWoW = (rank, name, value, wowObj, prevValue, isAlt) => `\n<tr style=\"background:${isAlt ? C.rowAlt : C.rowBase};\">\n  <td style=\"font-family:Arial,sans-serif;font-size:12px;color:${C.gold};font-weight:bold;padding:13px 0 13px 24px;border-bottom:1px solid ${C.divider};width:6%;text-align:center;\">${rank}</td>\n  <td style=\"font-family:Arial,sans-serif;font-size:13px;color:${C.value};padding:13px 12px;border-bottom:1px solid ${C.divider};text-align:left;\">${name}</td>\n  <td style=\"font-family:Georgia,serif;font-size:14px;font-weight:bold;color:${C.value};padding:13px 12px;border-bottom:1px solid ${C.divider};text-align:right;white-space:nowrap;\">${value}</td>\n  ${changeCell(wowObj, prevValue)}\n</tr>`;\n\n// Bar row: label(left) / bar / sessions n+%(right) / change cell(right-stacked)\nconst barRowWoW = (label, value, total, barColor, wowObj, prevValue, isAlt) => {\n  const pct = ((parseInt(value || 0) / total) * 100).toFixed(1);\n  const barWidth = Math.min(99, Math.max(1, Math.round(parseFloat(pct))));\n  return `\n<tr style=\"background:${isAlt ? C.rowAlt : C.rowBase};\">\n  <td style=\"font-family:Arial,sans-serif;font-size:13px;color:${C.value};padding:14px 24px 14px 24px;border-bottom:1px solid ${C.divider};width:22%;text-align:left;\">${label}</td>\n  <td style=\"padding:14px 12px;border-bottom:1px solid ${C.divider};width:26%;\">\n    <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n      <td style=\"width:${barWidth}%;background:${barColor};height:6px;border-radius:3px 0 0 3px;font-size:0;line-height:0;\"></td>\n      <td style=\"width:${100-barWidth}%;background:#EDE8E2;height:6px;border-radius:0 3px 3px 0;font-size:0;line-height:0;\"></td>\n    </tr></table>\n  </td>\n  <td style=\"font-family:Georgia,serif;font-size:14px;font-weight:bold;color:${C.value};padding:14px 12px;border-bottom:1px solid ${C.divider};text-align:right;white-space:nowrap;width:24%;\">\n    ${fmt(value)}&thinsp;<span style=\"font-family:Arial,sans-serif;font-size:11px;color:${C.prevValue};font-weight:normal;\">${pct}%</span>\n  </td>\n  ${changeCell(wowObj, prevValue)}\n</tr>`;\n};\n\n// ── 6. BUILD ROWS ─────────────────────────────────────────────\n\nconst deviceColors = { mobile: '#C9A96E', desktop: '#5C6B73', tablet: '#8C7B6E' };\nconst deviceLabels = { mobile: 'Mobile', desktop: 'Desktop', tablet: 'Tablet' };\n\nconst deviceRows = devices.map((d, i) => {\n  const cat = (d.deviceCategory || 'other').toLowerCase();\n  const { wow: wowDev, prevValue } = listWoWWithPrev(d, devicesPrev, 'deviceCategory', 'sessions');\n  return barRowWoW(deviceLabels[cat] || cat, d.sessions, totalDeviceSessions, deviceColors[cat] || C.gold, wowDev, prevValue, i % 2 !== 0);\n}).join('');\n\nconst nvrRows = [\n  barRowWoW('New Users',       newUsersRow.totalUsers,  totalNvR, '#2D6A4F', wowNewUsers, parseInt(newUsersPrevRow.totalUsers) || null, false),\n  barRowWoW('Returning Users', returningRow.totalUsers, totalNvR, C.gold,    wowRetUsers, parseInt(retUsersPrevRow.totalUsers) || null, true),\n].join('');\n\nconst pageRows = pages.map((p, i) => {\n  const { wow: w, prevValue } = listWoWWithPrev(p, pagesPrev, 'unifiedScreenName', 'screen_page_view');\n  return listRowWoW(i + 1, p.unifiedScreenName, fmt(p.screen_page_view), w, prevValue, i % 2 !== 0);\n}).join('');\n\nconst referralRows = referrals.map((r, i) => {\n  const { wow: w, prevValue } = listWoWWithPrev(r, referralsPrev, 'sessionSourceMedium', 'sessions');\n  return listRowWoW(i + 1, r.sessionSourceMedium, fmt(r.sessions), w, prevValue, i % 2 !== 0);\n}).join('');\n\nconst eventRows = events.map((e, i) => {\n  const { wow: w, prevValue } = listWoWWithPrev(e, eventsPrev, 'eventName', 'event_count');\n  return listRowWoW(i + 1, e.eventName, fmt(e.event_count), w, prevValue, i % 2 !== 0);\n}).join('');\n\nconst countryRows = countries.map((c, i) => {\n  const { wow: w, prevValue } = listWoWWithPrev(c, countriesPrev, 'country', 'sessions');\n  return listRowWoW(i + 1, c.country, fmt(c.sessions), w, prevValue, i % 2 !== 0);\n}).join('');\n\n// ── 7. COLUMN HEADER DEFINITIONS ─────────────────────────────\n// align MUST match the text-align of the data cell below it\n\nconst hdrsOverview   = [{label:'Metric',align:'left'},{label:'This Week',align:'right'},{label:'Last Week',align:'right'},{label:'Change',align:'right'}];\nconst hdrsAudience   = [{label:'Segment',align:'left'},{label:'Share',align:'left'},{label:'Users',align:'right'},{label:'Change',align:'right'}];\nconst hdrsScreens    = [{label:'#',align:'center'},{label:'Screen / Page',align:'left'},{label:'Views',align:'right'},{label:'Change',align:'right'}];\nconst hdrsTraffic    = [{label:'#',align:'center'},{label:'Source / Medium',align:'left'},{label:'Sessions',align:'right'},{label:'Change',align:'right'}];\nconst hdrsEvents     = [{label:'#',align:'center'},{label:'Event Name',align:'left'},{label:'Count',align:'right'},{label:'Change',align:'right'}];\nconst hdrsGeo        = [{label:'#',align:'center'},{label:'Country',align:'left'},{label:'Sessions',align:'right'},{label:'Change',align:'right'}];\nconst hdrsDevices    = [{label:'Device',align:'left'},{label:'Share',align:'left'},{label:'Sessions',align:'right'},{label:'Change',align:'right'}];\n\n// ── 8. BUILD EMAIL ────────────────────────────────────────────\n\nconst html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n  <title>Weekly Performance Report</title>\n</head>\n<body style=\"margin:0;padding:0;background:${C.bg};font-family:Arial,sans-serif;\">\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:${C.bg};\">\n<tr><td align=\"center\" style=\"padding:32px 16px 40px;\">\n\n  <table width=\"600\" cellpadding=\"0\" cellspacing=\"0\"\n    style=\"max-width:600px;width:100%;background:${C.card};border-radius:4px;overflow:hidden;box-shadow:0 2px 40px rgba(0,0,0,0.09);\">\n\n    <!-- HEADER -->\n    <tr>\n      <td style=\"background:${C.headerBg};padding:40px 32px 36px;\">\n        <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr><td>\n          <div style=\"font-family:Arial,sans-serif;font-size:10px;font-weight:bold;color:${C.gold};letter-spacing:2.5px;text-transform:uppercase;margin-bottom:14px;\">Analytics Report</div>\n          <div style=\"font-family:Georgia,serif;font-size:28px;font-weight:bold;color:#FFFFFF;line-height:1.2;margin-bottom:10px;letter-spacing:-0.5px;\">Weekly Performance<br>Summary</div>\n          <div style=\"font-family:Arial,sans-serif;font-size:13px;color:${C.headerSub};margin-bottom:24px;\">${startLabel} &nbsp;—&nbsp; ${endLabel}</div>\n          <div style=\"height:1px;background:linear-gradient(to right,${C.gold},transparent);margin-bottom:24px;\"></div>\n          <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n            <td style=\"text-align:center;padding:0 12px 0 0;width:25%;border-right:1px solid #2E2E2E;\">\n              <div style=\"font-family:Georgia,serif;font-size:22px;font-weight:bold;color:#FFFFFF;\">${fmt(currentWeek.totalUsers)}</div>\n              <div style=\"font-family:Arial,sans-serif;font-size:10px;color:${C.headerSub};margin-top:4px;text-transform:uppercase;letter-spacing:0.8px;\">Users</div>\n              <div style=\"font-family:Arial,sans-serif;font-size:11px;font-weight:bold;color:${wow.totalUsers.color};margin-top:5px;\">${wow.totalUsers.arrow} ${wow.totalUsers.text}</div>\n            </td>\n            <td style=\"text-align:center;padding:0 12px;width:25%;border-right:1px solid #2E2E2E;\">\n              <div style=\"font-family:Georgia,serif;font-size:22px;font-weight:bold;color:#FFFFFF;\">${fmt(currentWeek.sessions)}</div>\n              <div style=\"font-family:Arial,sans-serif;font-size:10px;color:${C.headerSub};margin-top:4px;text-transform:uppercase;letter-spacing:0.8px;\">Sessions</div>\n              <div style=\"font-family:Arial,sans-serif;font-size:11px;font-weight:bold;color:${wow.sessions.color};margin-top:5px;\">${wow.sessions.arrow} ${wow.sessions.text}</div>\n            </td>\n            <td style=\"text-align:center;padding:0 12px;width:25%;border-right:1px solid #2E2E2E;\">\n              <div style=\"font-family:Georgia,serif;font-size:22px;font-weight:bold;color:#FFFFFF;\">${fmtPct(currentWeek.bounce_rate)}</div>\n              <div style=\"font-family:Arial,sans-serif;font-size:10px;color:${C.headerSub};margin-top:4px;text-transform:uppercase;letter-spacing:0.8px;\">Bounce</div>\n              <div style=\"font-family:Arial,sans-serif;font-size:11px;font-weight:bold;color:${wow.bounceRate.color};margin-top:5px;\">${wow.bounceRate.arrow} ${wow.bounceRate.text}</div>\n            </td>\n            <td style=\"text-align:center;padding:0 0 0 12px;width:25%;\">\n              <div style=\"font-family:Georgia,serif;font-size:22px;font-weight:bold;color:#FFFFFF;\">${formatDuration(currentWeek.average_session_dur)}</div>\n              <div style=\"font-family:Arial,sans-serif;font-size:10px;color:${C.headerSub};margin-top:4px;text-transform:uppercase;letter-spacing:0.8px;\">Duration</div>\n              <div style=\"font-family:Arial,sans-serif;font-size:11px;font-weight:bold;color:${wow.avgDuration.color};margin-top:5px;\">${wow.avgDuration.arrow} ${wow.avgDuration.text}</div>\n            </td>\n          </tr></table>\n        </td></tr></table>\n      </td>\n    </tr>\n\n    <!-- BODY -->\n    <tr><td style=\"padding:0;background:${C.bg};\">\n      <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n\n        ${sectionGap}\n\n        <!-- AI SUMMARY -->\n        ${formattedAiSummary ? `\n        <tr><td colspan=\"5\" style=\"padding:0;background:${C.card};\">\n          <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n            <tr><td style=\"padding:20px 24px 14px;border-bottom:2px solid ${C.gold};\">\n              <span style=\"font-family:Georgia,serif;font-size:15px;font-weight:bold;color:${C.sectionText};letter-spacing:0.3px;\">AI Executive Summary</span>\n              <span style=\"font-family:Arial,sans-serif;font-size:10px;color:${C.headerSub};margin-left:10px;text-transform:uppercase;letter-spacing:0.8px;\">Generated by Gemini</span>\n            </td></tr>\n            <tr><td style=\"padding:20px 24px 24px;background:#FDFCFB;\">${formattedAiSummary}</td></tr>\n          </table>\n        </td></tr>\n        ${sectionGap}` : ''}\n\n        <!-- OVERVIEW -->\n        <tr><td colspan=\"5\" style=\"padding:0;background:${C.card};\">\n          <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n            ${sectionHeader('Overview', `vs ${prevStartLabel} – ${prevEndLabel}`)}\n            ${colHeaders(hdrsOverview)}\n            ${overviewRow('Total Users',           fmt(currentWeek.totalUsers),                     fmt(previousWeek.totalUsers),                     wow.totalUsers,  false)}\n            ${overviewRow('Sessions',              fmt(currentWeek.sessions),                       fmt(previousWeek.sessions),                       wow.sessions,    true)}\n            ${overviewRow('New Users',             fmt(currentWeek.new_users),                      fmt(previousWeek.new_users),                      wow.newUsers,    false)}\n            ${overviewRow('Bounce Rate',           fmtPct(currentWeek.bounce_rate),                 fmtPct(previousWeek.bounce_rate),                 wow.bounceRate,  true)}\n            ${overviewRow('Avg. Session Duration', formatDuration(currentWeek.average_session_dur), formatDuration(previousWeek.average_session_dur), wow.avgDuration, false)}\n          </table>\n        </td></tr>\n\n        ${sectionGap}\n\n        <!-- AUDIENCE -->\n        <tr><td colspan=\"5\" style=\"padding:0;background:${C.card};\">\n          <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n            ${sectionHeader('Audience', 'New vs Returning · WoW')}\n            ${colHeaders(hdrsAudience)}\n            ${nvrRows}\n          </table>\n        </td></tr>\n\n        ${sectionGap}\n\n        <!-- TOP SCREENS -->\n        <tr><td colspan=\"5\" style=\"padding:0;background:${C.card};\">\n          <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n            ${sectionHeader('Top Screens', 'By views · WoW')}\n            ${colHeaders(hdrsScreens)}\n            ${pageRows}\n          </table>\n        </td></tr>\n\n        ${sectionGap}\n\n        <!-- TRAFFIC SOURCES -->\n        <tr><td colspan=\"5\" style=\"padding:0;background:${C.card};\">\n          <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n            ${sectionHeader('Traffic Sources', 'Top channels · WoW')}\n            ${colHeaders(hdrsTraffic)}\n            ${referralRows}\n          </table>\n        </td></tr>\n\n        ${sectionGap}\n\n        <!-- TOP EVENTS -->\n        ${events.length > 0 ? `\n        <tr><td colspan=\"5\" style=\"padding:0;background:${C.card};\">\n          <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n            ${sectionHeader('Top Events', 'User interactions · WoW')}\n            ${colHeaders(hdrsEvents)}\n            ${eventRows}\n          </table>\n        </td></tr>\n        ${sectionGap}` : ''}\n\n        <!-- GEOGRAPHY -->\n        <tr><td colspan=\"5\" style=\"padding:0;background:${C.card};\">\n          <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n            ${sectionHeader('Geography', 'Top 5 countries · WoW')}\n            ${colHeaders(hdrsGeo)}\n            ${countryRows}\n          </table>\n        </td></tr>\n\n        ${sectionGap}\n\n        <!-- DEVICES -->\n        <tr><td colspan=\"5\" style=\"padding:0;background:${C.card};\">\n          <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n            ${sectionHeader('Devices', 'By device category · WoW')}\n            ${colHeaders(hdrsDevices)}\n            ${deviceRows}\n          </table>\n        </td></tr>\n\n        ${sectionGap}\n\n      </table>\n    </td></tr>\n\n    <!-- FOOTER -->\n    <tr>\n      <td style=\"background:${C.footerBg};padding:32px;\">\n        <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n          <tr><td style=\"padding-bottom:16px;\">\n            <div style=\"height:1px;background:linear-gradient(to right,${C.gold},transparent);margin-bottom:20px;\"></div>\n            <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n              <td>\n                <div style=\"font-family:Georgia,serif;font-size:14px;color:#FFFFFF;font-weight:bold;margin-bottom:4px;\">AppStoneLab Technologies</div>\n                <a href=\"https://appstonelab.com\" style=\"font-family:Arial,sans-serif;font-size:11px;color:${C.gold};text-decoration:none;\">appstonelab.com</a>\n              </td>\n              <td style=\"text-align:right;vertical-align:top;\">\n                <div style=\"font-family:Arial,sans-serif;font-size:10px;color:${C.footerText};line-height:1.8;\">\n                  ${startLabel} — ${endLabel}<br>Google Analytics 4<br>\n                  <a href=\"#\" style=\"color:${C.footerText};text-decoration:underline;\">Unsubscribe</a>\n                </div>\n              </td>\n            </tr></table>\n          </td></tr>\n          <tr><td>\n            <div style=\"font-family:Arial,sans-serif;font-size:10px;color:#3A3A3A;line-height:1.6;\">\n              This report is automatically generated every Monday via n8n workflow automation. Data reflects the 7-day period ending ${endLabel}.\n            </div>\n          </td></tr>\n        </table>\n      </td>\n    </tr>\n\n  </table>\n</td></tr>\n</table>\n</body>\n</html>`;\n\n// ── 9. OUTPUT ─────────────────────────────────────────────────\n\nreturn [{\n  json: {\n    subject: `Weekly Performance Report — ${startLabel} to ${endLabel}`,\n    html,\n    recipients: 'user@example.com',\n    _debug: {\n      currentWeekUsers: currentWeek.totalUsers,\n      previousWeekUsers: previousWeek.totalUsers,\n      pagesReturned: pages.length,\n      eventsReturned: events.length,\n      devicesReturned: devices.length,\n      aiSummaryLength: aiSummary.length,\n    }\n  }\n}];"},"typeVersion":2},{"id":"a50596dc-72d1-4efa-8d73-d9f9ed96d5c2","name":"Send Weekly Report","type":"n8n-nodes-base.emailSend","position":[1248,112],"webhookId":"f77564d3-ff52-473e-b420-140288505d49","parameters":{"html":"={{ $json.html }}","options":{},"subject":"={{ $json.subject }}","toEmail":"={{ $json.recipients }}, jignesh.sanghani@dev.appstonelab.com","fromEmail":"={{ $json.recipients }}"},"credentials":{"smtp":{"id":"credential-id","name":"SMTP account"}},"typeVersion":2.1},{"id":"402d7cd9-f814-49f5-9ae9-985d2ae86492","name":"GA4 - Top 5 Pages Previous Week","type":"n8n-nodes-base.googleAnalytics","position":[-320,-272],"parameters":{"limit":5,"endDate":"={{$now.minus({days: 8}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 14}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"name":"screen_page_view","listName":"custom","expression":"screenPageViews"},{"name":"average_session_duration","listName":"custom","expression":"averageSessionDuration"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"name":"unifiedScreenName","listName":"other"}]},"additionalFields":{}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"98a79977-3d6d-4200-ba26-4b2f59868ada","name":"GA4 - Top 5 Referrals Previous Week","type":"n8n-nodes-base.googleAnalytics","position":[-320,-80],"parameters":{"limit":5,"endDate":"={{$now.minus({days: 8}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 14}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"listName":"sessions"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"name":"sessionSourceMedium","listName":"other"}]},"additionalFields":{"orderByUI":{"metricOrderBy":[{"desc":true,"metricName":"sessions"}]}}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"cec80406-03d0-4592-9646-e489216e32a7","name":"GA4 - Top 5 Events Previous Week","type":"n8n-nodes-base.googleAnalytics","position":[-320,112],"parameters":{"limit":5,"endDate":"={{$now.minus({days: 8}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 14}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"name":"event_count","listName":"custom","expression":"eventCount"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"name":"eventName","listName":"other"}]},"additionalFields":{"orderByUI":{"metricOrderBy":[{"desc":true,"metricName":"=event_count"}]}}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"31a96f65-3ff9-47da-b6ea-62e059d636f3","name":"GA4 - Top 5 Countries Previous Week","type":"n8n-nodes-base.googleAnalytics","position":[-320,304],"parameters":{"limit":5,"endDate":"={{$now.minus({days: 8}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 14}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"listName":"sessions"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"listName":"country"}]},"additionalFields":{"orderByUI":{"metricOrderBy":[{"desc":true,"metricName":"sessions"}]}}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"7ac51236-fc7e-4e90-9ebc-44c4a897697e","name":"GA4 - Device Breakdown Previous Week","type":"n8n-nodes-base.googleAnalytics","position":[-320,496],"parameters":{"endDate":"={{$now.minus({days: 8}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 14}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"listName":"sessions"}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"listName":"deviceCategory"}]},"additionalFields":{"orderByUI":{"metricOrderBy":[{"desc":true,"metricName":"sessions"}]}}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"814f5d15-8fef-4db4-a676-deece444adb0","name":"GA4 - New vs Returning Previous Week","type":"n8n-nodes-base.googleAnalytics","position":[-320,688],"parameters":{"endDate":"={{$now.minus({days: 8}).startOf('day').toFormat('yyyy-MM-dd')}}","dateRange":"custom","startDate":"={{$now.minus({days: 14}).startOf('day').toFormat('yyyy-MM-dd')}}","metricsGA4":{"metricValues":[{"listName":"sessions"},{}]},"propertyId":{"__rl":true,"mode":"id","value":"481410811"},"dimensionsGA4":{"dimensionValues":[{"name":"newVsReturning","listName":"other"}]},"additionalFields":{}},"credentials":{"googleAnalyticsOAuth2":{"id":"credential-id","name":"Google Analytics account"}},"executeOnce":true,"typeVersion":2},{"id":"7b51be87-b503-40e8-8516-3965f3ae013d","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-1888,-48],"parameters":{"color":6,"width":544,"height":480,"content":"## 📊 Weekly GA4 Business Intelligence Report\n\nAutomatically runs **every Monday at 8:00 AM** and delivers a fully formatted HTML performance report to stakeholders via email.\n\n**What this workflow does:**\n- Fetches 14 separate reports from Google Analytics 4 (Current & Previous week data for 7 key metrics)\n- Generates an AI executive summary analyzing Week-over-Week (WoW) trends using Gemini\n- Calculates WoW % changes for ALL metrics (Overview, Pages, Referrals, Events, Countries, Devices, New vs Returning)\n- Builds a premium HTML email and delivers it via Gmail/SMTP\n\n**Before using this template:**\n1. Set your GA4 Property ID in all 14 GA4 nodes\n2. Connect your Google Analytics OAuth2 credential\n3. Connect your Gemini API credential\n4. Connect your Gmail / SMTP credential\n5. Set your recipient email(s) in the Email node or Code node\n6. Set your timezone in workflow settings"},"typeVersion":1},{"id":"3b842436-77b7-4605-8969-a9b187cbcff2","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[-1248,-464],"parameters":{"color":3,"width":688,"height":448,"content":"## 📡 Google Analytics 4 — Data Nodes\n\nFetches data for 7 key categories, running both **Current Week** and **Previous Week** nodes to generate deep Week-over-Week (WoW) comparisons.\n\n**⚠️ Required: Set your Property ID**\nOpen each of the 14 nodes → Property ID field → replace {YOUR_PROPERTY_ID} with your GA4 numeric property ID.\nFound at: GA4 Admin → Property Settings → Property ID\n\n**Node breakdown (Each has a Current & Previous week version):**\n- **Overview** — totals for sessions, users, bounce rate, etc.\n- **Top 5 Pages** — screens/pages by views\n- **Top 5 Referrals** — traffic sources by sessions\n- **Top 5 Events** — user interactions by count\n- **Top 5 Countries** — geographic breakdown by sessions\n- **Device Breakdown** — mobile / desktop / tablet split\n- **New vs Returning** — audience loyalty breakdown\n\n**All nodes have Execute Once = ON** to prevent duplicate rows."},"typeVersion":1},{"id":"7e2f82c1-abb2-45ff-bfdb-d876182d3495","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[-1328,48],"parameters":{"color":7,"width":512,"height":288,"content":"## ⏰ Schedule Trigger\n\nFires every **Monday at 8:00 AM IST**.\n\n\n\n\n\n\n\n\n**To change timezone or time:**\nOpen workflow settings → change the Timezone to your preferred one.\n\nDefault timezone: Your Local Timezone"},"typeVersion":1},{"id":"d05075f0-2f6d-49ac-978f-fa03e5c90715","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[-48,-272],"parameters":{"color":4,"width":304,"height":624,"content":"## 🔀 Merge\n\nWaits for **all GA4 data chains (14 nodes total)** to complete before passing data forward.\n\nWithout this node, Gemini and Code nodes could trigger before all GA4 data is ready - causing empty or undefined values in the report.\n\nMode: Combine by Position"},"typeVersion":1},{"id":"b4fa1e17-e992-49f6-9264-65a8e2569f47","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[272,-272],"parameters":{"color":5,"width":416,"height":624,"content":"## 🤖 Gemini — AI Executive Summary\n\nGenerates a **3-paragraph executive summary** covering overall performance, audience behaviour, and actionable recommendations. It analyzes the full Week-over-Week dataset from all 14 GA4 nodes to spot trends.\n\n**Credential required:** Google Gemini API\nGet your key at: aistudio.google.com\n\n**Model:** gemini-3.1-flash-lite-preview\n\nIf Gemini fails or returns empty, the report still sends normally — the AI section is hidden from the email automatically."},"typeVersion":1},{"id":"130b47cc-f010-46a4-b926-340d8237ec7f","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[704,-272],"parameters":{"color":4,"width":432,"height":624,"content":"## 🧮 Code Node — Data Processing + Email Builder\n\nDoes 4 things:\n1. Pulls data from all 14 GA4 nodes and Gemini by node name.\n2. Calculates week-over-week % changes for **every single section** (Overview, Pages, Sources, Events, Countries, Devices, New vs Returning).\n3. Builds the full HTML email with inline CSS and alignment fixes.\n4. Outputs subject, html, and recipients for the Email node.\n\n**To change recipients:**\nFind `recipients:` near the bottom of the code → update the email address.\nMultiple: `'email1@gmail.com,email2@gmail.com'`\n\n\n\n\n\n\n\n\n\n\n\n\n**To change client name in footer:**\nFind `AppStoneLab Technologies` in the HTML and replace with your client's brand name."},"typeVersion":1},{"id":"8c2b8818-4b6f-460b-9896-5d13240bd3fb","name":"Sticky Note6","type":"n8n-nodes-base.stickyNote","position":[1152,-272],"parameters":{"color":2,"width":288,"height":624,"content":"## 📧 Send Email\n\nSends the HTML report to all recipients defined in the Code node.\n\n**Fields mapped from Code node output:**\n- To → {{ $json.recipients }}\n- Subject → {{ $json.subject }}\n- HTML Body → {{ $json.html }}\n\n**Credential required:** Gmail OAuth2 or SMTP\nFor SMTP: update host, port, and login credentials in the credential settings."},"typeVersion":1}],"active":false,"pinData":{},"settings":{"binaryMode":"separate","availableInMCP":false,"executionOrder":"v1"},"versionId":"6ee0e082-565e-46a8-b079-623ec18623df","connections":{"GA4 - Top 5 Pages":{"main":[[{"node":"GA4 - Top 5 Pages Previous Week","type":"main","index":0}]]},"GA4 - Top 5 Events":{"main":[[{"node":"GA4 - Top 5 Events Previous Week","type":"main","index":0}]]},"Generate AI Summary":{"main":[[{"node":"Build Report & Email HTML","type":"main","index":0}]]},"GA4 - Top 5 Countries":{"main":[[{"node":"GA4 - Top 5 Countries Previous Week","type":"main","index":0}]]},"GA4 - Top 5 Referrals":{"main":[[{"node":"GA4 - Top 5 Referrals Previous Week","type":"main","index":0}]]},"Wait for All GA4 Data":{"main":[[{"node":"Generate AI Summary","type":"main","index":0}]]},"Weekly Monday Trigger":{"main":[[{"node":"GA4 - Overview Current Week","type":"main","index":0},{"node":"GA4 - Top 5 Pages","type":"main","index":0},{"node":"GA4 - Top 5 Referrals","type":"main","index":0},{"node":"GA4 - Top 5 Events","type":"main","index":0},{"node":"GA4 - Top 5 Countries","type":"main","index":0},{"node":"GA4 - Device Breakdown","type":"main","index":0},{"node":"New vs Returning Users Breakdown","type":"main","index":0}]]},"GA4 - Device Breakdown":{"main":[[{"node":"GA4 - Device Breakdown Previous Week","type":"main","index":0}]]},"Build Report & Email HTML":{"main":[[{"node":"Send Weekly Report","type":"main","index":0}]]},"GA4 - Overview Current Week":{"main":[[{"node":"GA4 - Overview Previous Week","type":"main","index":0}]]},"GA4 - Overview Previous Week":{"main":[[{"node":"Wait for All GA4 Data","type":"main","index":0}]]},"GA4 - Top 5 Pages Previous Week":{"main":[[{"node":"Wait for All GA4 Data","type":"main","index":1}]]},"GA4 - Top 5 Events Previous Week":{"main":[[{"node":"Wait for All GA4 Data","type":"main","index":3}]]},"New vs Returning Users Breakdown":{"main":[[{"node":"GA4 - New vs Returning Previous Week","type":"main","index":0}]]},"GA4 - Top 5 Countries Previous Week":{"main":[[{"node":"Wait for All GA4 Data","type":"main","index":4}]]},"GA4 - Top 5 Referrals Previous Week":{"main":[[{"node":"Wait for All GA4 Data","type":"main","index":2}]]},"GA4 - Device Breakdown Previous Week":{"main":[[{"node":"Wait for All GA4 Data","type":"main","index":5}]]},"GA4 - New vs Returning Previous Week":{"main":[[{"node":"Wait for All GA4 Data","type":"main","index":6}]]}}},"lastUpdatedBy":1,"workflowInfo":{"nodeCount":26,"nodeTypes":{"n8n-nodes-base.code":{"count":1},"n8n-nodes-base.merge":{"count":1},"n8n-nodes-base.emailSend":{"count":1},"n8n-nodes-base.stickyNote":{"count":7},"n8n-nodes-base.googleAnalytics":{"count":14},"n8n-nodes-base.scheduleTrigger":{"count":1},"@n8n/n8n-nodes-langchain.googleGemini":{"count":1}}},"status":"published","readyToDemo":null,"user":{"name":"AppStoneLab Technologies LLP","username":"appstonelab","bio":"Dive into the digital era with a trusted partner. From idea to execution, we transform visions into interactive realities. Let's shape the future, together.","verified":true,"links":["https://appstonelab.com"],"avatar":"https://gravatar.com/avatar/2e16a1d332e0382df4f5e3263c822b0ef2c3b51db77e18f737999755913724f6?r=pg&d=retro&size=200"},"nodes":[{"id":11,"icon":"fa:envelope","name":"n8n-nodes-base.emailSend","codex":{"data":{"alias":["SMTP","email","human","form","wait","hitl","approval"],"resources":{"generic":[{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/build-your-own-virtual-assistant-with-n8n-a-step-by-step-guide/","icon":"👦","label":"Build your own virtual assistant with n8n: A step by step guide"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.sendemail/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/sendemail/"}]},"categories":["Communication","HITL","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"HITL":["Human in the Loop"]}}},"group":"[\"output\"]","defaults":{"name":"Send Email","color":"#00bb88"},"iconData":{"icon":"envelope","type":"icon"},"displayName":"Send Email","typeVersion":2,"nodeCategories":[{"id":6,"name":"Communication"},{"id":9,"name":"Core Nodes"},{"id":28,"name":"HITL"}]},{"id":24,"icon":"file:merge.svg","name":"n8n-nodes-base.merge","codex":{"data":{"alias":["Join","Concatenate","Wait"],"resources":{"generic":[{"url":"https://n8n.io/blog/how-to-sync-data-between-two-systems/","icon":"🏬","label":"How to synchronize data between two systems (one-way vs. two-way sync"},{"url":"https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/","icon":"🎫","label":"Supercharging your conference registration process with n8n"},{"url":"https://n8n.io/blog/migrating-community-metrics-to-orbit-using-n8n/","icon":"📈","label":"Migrating Community Metrics to Orbit using n8n"},{"url":"https://n8n.io/blog/build-your-own-virtual-assistant-with-n8n-a-step-by-step-guide/","icon":"👦","label":"Build your own virtual assistant with n8n: A step by step guide"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.merge/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Merge"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTc3XzUxOCkiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgNDhDMCAyMS40OTAzIDIxLjQ5MDMgMCA0OCAwSDExMkMxMzguNTEgMCAxNjAgMjEuNDkwMyAxNjAgNDhWNTZIMTk2LjI1MkMyNDAuNDM1IDU2IDI3Ni4yNTIgOTEuODE3MiAyNzYuMjUyIDEzNlYxOTJDMjc2LjI1MiAyMTQuMDkxIDI5NC4xNjEgMjMyIDMxNi4yNTIgMjMySDM1MlYyMjRDMzUyIDE5Ny40OSAzNzMuNDkgMTc2IDQwMCAxNzZINDY0QzQ5MC41MSAxNzYgNTEyIDE5Ny40OSA1MTIgMjI0VjI4OEM1MTIgMzE0LjUxIDQ5MC41MSAzMzYgNDY0IDMzNkg0MDBDMzczLjQ5IDMzNiAzNTIgMzE0LjUxIDM1MiAyODhWMjgwSDMxNi4yNTJDMjk0LjE2MSAyODAgMjc2LjI1MiAyOTcuOTA5IDI3Ni4yNTIgMzIwVjM3NkMyNzYuMjUyIDQyMC4xODMgMjQwLjQzNSA0NTYgMTk2LjI1MiA0NTZIMTYwVjQ2NEMxNjAgNDkwLjUxIDEzOC41MSA1MTIgMTEyIDUxMkg0OEMyMS40OTAzIDUxMiAwIDQ5MC41MSAwIDQ2NFY0MDBDMCAzNzMuNDkgMjEuNDkwMyAzNTIgNDggMzUySDExMkMxMzguNTEgMzUyIDE2MCAzNzMuNDkgMTYwIDQwMFY0MDhIMTk2LjI1MkMyMTMuOTI1IDQwOCAyMjguMjUyIDM5My42NzMgMjI4LjI1MiAzNzZWMzIwQzIyOC4yNTIgMjk0Ljc4NCAyMzguODU5IDI3Mi4wNDQgMjU1Ljg1MyAyNTZDMjM4Ljg1OSAyMzkuOTU2IDIyOC4yNTIgMjE3LjIxNiAyMjguMjUyIDE5MlYxMzZDMjI4LjI1MiAxMTguMzI3IDIxMy45MjUgMTA0IDE5Ni4yNTIgMTA0SDE2MFYxMTJDMTYwIDEzOC41MSAxMzguNTEgMTYwIDExMiAxNjBINDhDMjEuNDkwMyAxNjAgMCAxMzguNTEgMCAxMTJWNDhaTTEwNCA0OEMxMDguNDE4IDQ4IDExMiA1MS41ODE3IDExMiA1NlYxMDRDMTEyIDEwOC40MTggMTA4LjQxOCAxMTIgMTA0IDExMkg1NkM1MS41ODE3IDExMiA0OCAxMDguNDE4IDQ4IDEwNFY1NkM0OCA1MS41ODE3IDUxLjU4MTcgNDggNTYgNDhIMTA0Wk00NTYgMjI0QzQ2MC40MTggMjI0IDQ2NCAyMjcuNTgyIDQ2NCAyMzJWMjgwQzQ2NCAyODQuNDE4IDQ2MC40MTggMjg4IDQ1NiAyODhINDA4QzQwMy41ODIgMjg4IDQwMCAyODQuNDE4IDQwMCAyODBWMjMyQzQwMCAyMjcuNTgyIDQwMy41ODIgMjI0IDQwOCAyMjRINDU2Wk0xMTIgNDA4QzExMiA0MDMuNTgyIDEwOC40MTggNDAwIDEwNCA0MDBINTZDNTEuNTgxNyA0MDAgNDggNDAzLjU4MiA0OCA0MDhWNDU2QzQ4IDQ2MC40MTggNTEuNTgxNyA0NjQgNTYgNDY0SDEwNEMxMDguNDE4IDQ2NCAxMTIgNDYwLjQxOCAxMTIgNDU2VjQwOFoiIGZpbGw9IiM1NEI4QzkiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTc3XzUxOCI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Merge","typeVersion":3,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":435,"icon":"file:analytics.svg","name":"n8n-nodes-base.googleAnalytics","codex":{"data":{"resources":{"generic":[{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googleanalytics/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"}]},"categories":["Analytics"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"transform\"]","defaults":{"name":"Google Analytics"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOTIiIGhlaWdodD0iMTkyIj48cGF0aCBmaWxsPSJub25lIiBkPSJNMCAwaDE5MnYxOTJIMHoiLz48cGF0aCBmaWxsPSIjRjlBQjAwIiBkPSJNMTMwIDI5djEzMmMwIDE0Ljc3IDEwLjE5IDIzIDIxIDIzIDEwIDAgMjEtNyAyMS0yM1YzMGMwLTEzLjU0LTEwLTIyLTIxLTIycy0yMSA5LjMzLTIxIDIxIi8+PHBhdGggZmlsbD0iI0UzNzQwMCIgZD0iTTc1IDk2djY1YzAgMTQuNzcgMTAuMTkgMjMgMjEgMjMgMTAgMCAyMS03IDIxLTIzVjk3YzAtMTMuNTQtMTAtMjItMjEtMjJzLTIxIDkuMzMtMjEgMjEiLz48Y2lyY2xlIGN4PSI0MSIgY3k9IjE2MyIgcj0iMjEiIGZpbGw9IiNFMzc0MDAiLz48L3N2Zz4="},"displayName":"Google Analytics","typeVersion":2,"nodeCategories":[{"id":10,"name":"Analytics"}]},{"id":565,"icon":"fa:sticky-note","name":"n8n-nodes-base.stickyNote","codex":{"data":{"alias":["Comments","Notes","Sticky"],"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"input\"]","defaults":{"name":"Sticky Note","color":"#FFD233"},"iconData":{"icon":"sticky-note","type":"icon"},"displayName":"Sticky Note","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":834,"icon":"file:code.svg","name":"n8n-nodes-base.code","codex":{"data":{"alias":["cpde","Javascript","JS","Python","Script","Custom Code","Function"],"details":"The Code node allows you to execute JavaScript in your workflow.","resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Code"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTcxXzQ0MSkiPgo8cGF0aCBkPSJNMTcwLjI4MyA0OEgxOTYuNUMyMDMuMTI3IDQ4IDIwOC41IDQyLjYyNzQgMjA4LjUgMzZWMTJDMjA4LjUgNS4zNzI1OCAyMDMuMTI3IDAgMTk2LjUgMEgxNzAuMjgzQzEyNi4xIDAgOTAuMjgzIDM1LjgxNzIgOTAuMjgzIDgwVjE3NkM5MC4yODMgMjA2LjkyOCA2NS4yMTA5IDIzMiAzNC4yODMgMjMySDIzQzE2LjM3MjYgMjMyIDExIDIzNy4zNzIgMTEgMjQ0VjI2OEMxMSAyNzQuNjI3IDE2LjM3MjQgMjgwIDIyLjk5OTYgMjgwTDM0LjI4MyAyODBDNjUuMjEwOSAyODAgOTAuMjgzIDMwNS4wNzIgOTAuMjgzIDMzNlY0NDBDOTAuMjgzIDQ3OS43NjQgMTIyLjUxOCA1MTIgMTYyLjI4MyA1MTJIMTk2LjVDMjAzLjEyNyA1MTIgMjA4LjUgNTA2LjYyNyAyMDguNSA1MDBWNDc2QzIwOC41IDQ2OS4zNzMgMjAzLjEyNyA0NjQgMTk2LjUgNDY0SDE2Mi4yODNDMTQ5LjAyOCA0NjQgMTM4LjI4MyA0NTMuMjU1IDEzOC4yODMgNDQwVjMzNkMxMzguMjgzIDMwOS4wMjIgMTI4LjAxMSAyODQuNDQzIDExMS4xNjQgMjY1Ljk2MUMxMDYuMTA5IDI2MC40MTYgMTA2LjEwOSAyNTEuNTg0IDExMS4xNjQgMjQ2LjAzOUMxMjguMDExIDIyNy41NTcgMTM4LjI4MyAyMDIuOTc4IDEzOC4yODMgMTc2VjgwQzEzOC4yODMgNjIuMzI2OSAxNTIuNjEgNDggMTcwLjI4MyA0OFoiIGZpbGw9IiNGRjk5MjIiLz4KPHBhdGggZD0iTTMwNSAzNkMzMDUgNDIuNjI3NCAzMTAuMzczIDQ4IDMxNyA0OEgzNDIuOTc5QzM2MC42NTIgNDggMzc0Ljk3OCA2Mi4zMjY5IDM3NC45NzggODBWMTc2QzM3NC45NzggMjAyLjk3OCAzODUuMjUxIDIyNy41NTcgNDAyLjA5OCAyNDYuMDM5QzQwNy4xNTMgMjUxLjU4NCA0MDcuMTUzIDI2MC40MTYgNDAyLjA5OCAyNjUuOTYxQzM4NS4yNTEgMjg0LjQ0MyAzNzQuOTc4IDMwOS4wMjIgMzc0Ljk3OCAzMzZWNDMyQzM3NC45NzggNDQ5LjY3MyAzNjAuNjUyIDQ2NCAzNDIuOTc5IDQ2NEgzMTdDMzEwLjM3MyA0NjQgMzA1IDQ2OS4zNzMgMzA1IDQ3NlY1MDBDMzA1IDUwNi42MjcgMzEwLjM3MyA1MTIgMzE3IDUxMkgzNDIuOTc5QzM4Ny4xNjEgNTEyIDQyMi45NzggNDc2LjE4MyA0MjIuOTc4IDQzMlYzMzZDNDIyLjk3OCAzMDUuMDcyIDQ0OC4wNTEgMjgwIDQ3OC45NzkgMjgwSDQ5MEM0OTYuNjI3IDI4MCA1MDIgMjc0LjYyOCA1MDIgMjY4VjI0NEM1MDIgMjM3LjM3MyA0OTYuNjI4IDIzMiA0OTAgMjMyTDQ3OC45NzkgMjMyQzQ0OC4wNTEgMjMyIDQyMi45NzggMjA2LjkyOCA0MjIuOTc4IDE3NlY4MEM0MjIuOTc4IDM1LjgxNzIgMzg3LjE2MSAwIDM0Mi45NzkgMEgzMTdDMzEwLjM3MyAwIDMwNSA1LjM3MjU4IDMwNSAxMlYzNloiIGZpbGw9IiNGRjk5MjIiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTcxXzQ0MSI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Code","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":839,"icon":"fa:clock","name":"n8n-nodes-base.scheduleTrigger","codex":{"data":{"alias":["Time","Scheduler","Polling","Cron","Interval"],"resources":{"generic":[],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.scheduletrigger/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"trigger\",\"schedule\"]","defaults":{"name":"Schedule Trigger","color":"#31C49F"},"iconData":{"icon":"clock","type":"icon"},"displayName":"Schedule Trigger","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":1309,"icon":"file:gemini.svg","name":"@n8n/n8n-nodes-langchain.googleGemini","codex":{"data":{"alias":["LangChain","video","document","audio","transcribe","assistant"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-langchain.googlegemini/"}]},"categories":["AI","Langchain"],"subcategories":{"AI":["Agents","Miscellaneous","Root Nodes"]}}},"group":"[\"transform\"]","defaults":{"name":"Google Gemini"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iX2Zvb3RlclNwYXJrXzk4dWR0XzE1MSIgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNjQgNjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTU3LjA2NjcgMjguNjEwM0M1Mi4xMzU5IDI2LjQ4NzggNDcuODIxNyAyMy41NzYgNDQuMTIyMyAxOS44Nzg0QzQwLjQyNDcgMTYuMTgwOCAzNy41MTI4IDExLjg2NDkgMzUuMzkwMiA2LjkzNDJDMzQuNTc1NCA1LjA0NDQ5IDMzLjkyMDYgMy4xMDIwNCAzMy40MTg2IDEuMTEwNDlDMzMuMjU0OSAwLjQ1OTM2OCAzMi42NzExIDAuMDAxMDM3NiAzMiAwLjAwMTAzNzZDMzEuMzI4OCAwLjAwMTAzNzYgMzAuNzQ1IDAuNDU5MzY4IDMwLjU4MTMgMS4xMTA0OUMzMC4wNzkzIDMuMTAyMDQgMjkuNDI0NiA1LjA0MjY3IDI4LjYwOTcgNi45MzQyQzI2LjQ4NzIgMTEuODY0OSAyMy41NzUzIDE2LjE4MDggMTkuODc3NyAxOS44Nzg0QzE2LjE4IDIzLjU3NiAxMS44NjQgMjYuNDg3OCA2LjkzMzI3IDI4LjYxMDNDNS4wNDM1MyAyOS40MjUxIDMuMTAxMDUgMzAuMDc5OSAxLjEwOTQ3IDMwLjU4MTlDMC40NTgzMzggMzAuNzQ1NiAwIDMxLjMyOTQgMCAzMi4wMDA1QzAgMzIuNjcxNiAwLjQ1ODMzOCAzMy4yNTU1IDEuMTA5NDcgMzMuNDE5MUMzLjEwMTA1IDMzLjkyMTEgNS4wNDE3MiAzNC41NzU5IDYuOTMzMjcgMzUuMzkwN0MxMS44NjQgMzcuNTEzMiAxNi4xNzgyIDQwLjQyNTEgMTkuODc3NyA0NC4xMjI2QzIzLjU3NzEgNDcuODIwMiAyNi40ODcyIDUyLjEzNjEgMjguNjA5NyA1Ny4wNjY4QzI5LjQyNDYgNTguOTU2NSAzMC4wNzkzIDYwLjg5OSAzMC41ODEzIDYyLjg5MDVDMzAuNzQ1IDYzLjU0MTYgMzEuMzI4OCA2NCAzMiA2NEMzMi42NzExIDY0IDMzLjI1NDkgNjMuNTQxNiAzMy40MTg2IDYyLjg5MDVDMzMuOTIwNiA2MC44OTkgMzQuNTc1NCA1OC45NTgzIDM1LjM5MDIgNTcuMDY2OEMzNy41MTI4IDUyLjEzNjEgNDAuNDI0NyA0Ny44MjIgNDQuMTIyMyA0NC4xMjI2QzQ3LjgxOTkgNDAuNDI1MSA1Mi4xMzU5IDM3LjUxMzIgNTcuMDY2NyAzNS4zOTA3QzU4Ljk1NjQgMzQuNTc1OSA2MC44OTg5IDMzLjkyMTEgNjIuODkwNSAzMy40MTkxQzYzLjU0MTYgMzMuMjU1NSA2NCAzMi42NzE2IDY0IDMyLjAwMDVDNjQgMzEuMzI5NCA2My41NDE2IDMwLjc0NTYgNjIuODkwNSAzMC41ODE5QzYwLjg5ODkgMzAuMDc5OSA1OC45NTgyIDI5LjQyNTEgNTcuMDY2NyAyOC42MTAzWiIgZmlsbD0id2hpdGUiPjwvcGF0aD48bWFzayBpZD0ibWFzazBfMTA4NTlfNDg5NCIgc3R5bGU9Im1hc2stdHlwZTphbHBoYSIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iMCIgeT0iMCIgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0Ij48cGF0aCBkPSJNMzIgMEMzMi42NzExIDEuMTQ0ZS0wNSAzMy4yNTUzIDAuNDU4MjYzIDMzLjQxODkgMS4xMDkzOEMzMy45MjA5IDMuMTAwOTMgMzQuNTc1OCA1LjA0Mzg5IDM1LjM5MDYgNi45MzM1OUMzNy41MTMxIDExLjg2MzkgNDAuNDI0NyAxNi4xNzk2IDQ0LjEyMjEgMTkuODc3QzQ3LjgyMTUgMjMuNTc0NSA1Mi4xMzU3IDI2LjQ4NjkgNTcuMDY2NCAyOC42MDk0QzU4Ljk1OCAyOS40MjQyIDYwLjg5OSAzMC4wNzkxIDYyLjg5MDYgMzAuNTgxMUM2My41NDE1IDMwLjc0NDggNjMuOTk5OCAzMS4zMjgxIDY0IDMxLjk5OUM2NCAzMi42NzAxIDYzLjU0MTcgMzMuMjU0MiA2Mi44OTA2IDMzLjQxOEM2MC44OTkgMzMuOTE5OSA1OC45NTYxIDM0LjU3NDggNTcuMDY2NCAzNS4zODk2QzUyLjEzNTggMzcuNTEyMSA0Ny44MTk2IDQwLjQyMzcgNDQuMTIyMSA0NC4xMjExQzQwLjQyNDYgNDcuODIwNCAzNy41MTMxIDUyLjEzNDkgMzUuMzkwNiA1Ny4wNjU0QzM0LjU3NTggNTguOTU3IDMzLjkyMDkgNjAuODk4MSAzMy40MTg5IDYyLjg4OTZDMzMuMjU1MiA2My41NDA3IDMyLjY3MTEgNjMuOTk5IDMyIDYzLjk5OUMzMS4zMjg5IDYzLjk5OSAzMC43NDQ4IDYzLjU0MDcgMzAuNTgxMSA2Mi44ODk2QzMwLjA3OTEgNjAuODk4MSAyOS40MjQyIDU4Ljk1NTEgMjguNjA5NCA1Ny4wNjU0QzI2LjQ4NjkgNTIuMTM0OSAyMy41NzczIDQ3LjgxODYgMTkuODc3OSA0NC4xMjExQzE2LjE3ODYgNDAuNDIzNyAxMS44NjQyIDM3LjUxMjEgNi45MzM1OSAzNS4zODk2QzUuMDQyMDQgMzQuNTc0OCAzLjEwMDk2IDMzLjkxOTkgMS4xMDkzOCAzMy40MThDMC40NTgzMDkgMzMuMjU0MiAwIDMyLjY3MDEgMCAzMS45OTlDMC4wMDAyMDE1NDggMzEuMzI4MSAwLjQ1ODQ2MyAzMC43NDQ4IDEuMTA5MzggMzAuNTgxMUMzLjEwMDk2IDMwLjA3OTEgNS4wNDM4NiAyOS40MjQyIDYuOTMzNTkgMjguNjA5NEMxMS44NjQzIDI2LjQ4NjkgMTYuMTgwNCAyMy41NzQ1IDE5Ljg3NzkgMTkuODc3QzIzLjU3NTMgMTYuMTc5NiAyNi40ODY5IDExLjg2MzkgMjguNjA5NCA2LjkzMzU5QzI5LjQyNDIgNS4wNDIwNyAzMC4wNzkxIDMuMTAwOTMgMzAuNTgxMSAxLjEwOTM4QzMwLjc0NDggMC40NTgyNiAzMS4zMjg5IDAgMzIgMFoiIGZpbGw9ImJsYWNrIj48L3BhdGg+PHBhdGggZD0iTTMyIDBDMzIuNjcxMSAxLjE0NGUtMDUgMzMuMjU1MyAwLjQ1ODI2MyAzMy40MTg5IDEuMTA5MzhDMzMuOTIwOSAzLjEwMDkzIDM0LjU3NTggNS4wNDM4OSAzNS4zOTA2IDYuOTMzNTlDMzcuNTEzMSAxMS44NjM5IDQwLjQyNDcgMTYuMTc5NiA0NC4xMjIxIDE5Ljg3N0M0Ny44MjE1IDIzLjU3NDUgNTIuMTM1NyAyNi40ODY5IDU3LjA2NjQgMjguNjA5NEM1OC45NTggMjkuNDI0MiA2MC44OTkgMzAuMDc5MSA2Mi44OTA2IDMwLjU4MTFDNjMuNTQxNSAzMC43NDQ4IDYzLjk5OTggMzEuMzI4MSA2NCAzMS45OTlDNjQgMzIuNjcwMSA2My41NDE3IDMzLjI1NDIgNjIuODkwNiAzMy40MThDNjAuODk5IDMzLjkxOTkgNTguOTU2MSAzNC41NzQ4IDU3LjA2NjQgMzUuMzg5NkM1Mi4xMzU4IDM3LjUxMjEgNDcuODE5NiA0MC40MjM3IDQ0LjEyMjEgNDQuMTIxMUM0MC40MjQ2IDQ3LjgyMDQgMzcuNTEzMSA1Mi4xMzQ5IDM1LjM5MDYgNTcuMDY1NEMzNC41NzU4IDU4Ljk1NyAzMy45MjA5IDYwLjg5ODEgMzMuNDE4OSA2Mi44ODk2QzMzLjI1NTIgNjMuNTQwNyAzMi42NzExIDYzLjk5OSAzMiA2My45OTlDMzEuMzI4OSA2My45OTkgMzAuNzQ0OCA2My41NDA3IDMwLjU4MTEgNjIuODg5NkMzMC4wNzkxIDYwLjg5ODEgMjkuNDI0MiA1OC45NTUxIDI4LjYwOTQgNTcuMDY1NEMyNi40ODY5IDUyLjEzNDkgMjMuNTc3MyA0Ny44MTg2IDE5Ljg3NzkgNDQuMTIxMUMxNi4xNzg2IDQwLjQyMzcgMTEuODY0MiAzNy41MTIxIDYuOTMzNTkgMzUuMzg5NkM1LjA0MjA0IDM0LjU3NDggMy4xMDA5NiAzMy45MTk5IDEuMTA5MzggMzMuNDE4QzAuNDU4MzA5IDMzLjI1NDIgMCAzMi42NzAxIDAgMzEuOTk5QzAuMDAwMjAxNTQ4IDMxLjMyODEgMC40NTg0NjMgMzAuNzQ0OCAxLjEwOTM4IDMwLjU4MTFDMy4xMDA5NiAzMC4wNzkxIDUuMDQzODYgMjkuNDI0MiA2LjkzMzU5IDI4LjYwOTRDMTEuODY0MyAyNi40ODY5IDE2LjE4MDQgMjMuNTc0NSAxOS44Nzc5IDE5Ljg3N0MyMy41NzUzIDE2LjE3OTYgMjYuNDg2OSAxMS44NjM5IDI4LjYwOTQgNi45MzM1OUMyOS40MjQyIDUuMDQyMDcgMzAuMDc5MSAzLjEwMDkzIDMwLjU4MTEgMS4xMDkzOEMzMC43NDQ4IDAuNDU4MjYgMzEuMzI4OSAwIDMyIDBaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMTA4NTlfNDg5NCkiPjwvcGF0aD48L21hc2s+PGcgbWFzaz0idXJsKCNtYXNrMF8xMDg1OV80ODk0KSI+PGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjBfZl8xMDg1OV80ODk0KSI+PGVsbGlwc2UgY3g9IjE0LjIwODQiIGN5PSIxNi43MTY0IiByeD0iMTQuMjA4NCIgcnk9IjE2LjcxNjQiIHRyYW5zZm9ybT0ibWF0cml4KDAuOTQyMzQzIDAuMzM0NjQ5IC0wLjMzNDY1NiAwLjk0MjM0IC03Ljk3OSAxMy43NzM1KSIgZmlsbD0iI0ZGRTQzMiI+PC9lbGxpcHNlPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyMV9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iMjcuMDU0MyIgY3k9IjIuNTUxMTQiIHJ4PSIxOC4zOTQ0IiByeT0iMTguNzk4NSIgZmlsbD0iI0ZDNDEzRCI+PC9lbGxpcHNlPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyMl9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iMTkuMjI0NSIgY3k9IjI0LjkwNDIiIHJ4PSIxOS4yMjQ1IiByeT0iMjQuOTA0MiIgdHJhbnNmb3JtPSJtYXRyaXgoMC45OTg4MDcgLTAuMDQ4ODI1NCAwLjA0ODgyNjYgMC45OTg4MDcgLTEuNzI3NzggMzIuNjU3MykiIGZpbGw9IiMwMEI5NUMiPjwvZWxsaXBzZT48L2c+PGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjNfZl8xMDg1OV80ODk0KSI+PGVsbGlwc2UgY3g9IjE5LjIyNDUiIGN5PSIyNC45MDQyIiByeD0iMTkuMjI0NSIgcnk9IjI0LjkwNDIiIHRyYW5zZm9ybT0ibWF0cml4KDAuOTk4ODA3IC0wLjA0ODgyNTQgMC4wNDg4MjY2IDAuOTk4ODA3IC0xLjcyNzc4IDMyLjY1NzMpIiBmaWxsPSIjMDBCOTVDIj48L2VsbGlwc2U+PC9nPjxnIGZpbHRlcj0idXJsKCNmaWx0ZXI0X2ZfMTA4NTlfNDg5NCkiPjxlbGxpcHNlIGN4PSIxOC44NDI5IiBjeT0iMjAuNzQ0MSIgcng9IjE4Ljg0MjkiIHJ5PSIyMC43NDQxIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjg1NDMwMSAtMC41MTk3NzkgMC41MTk3OSAwLjg1NDI5NCAtNy4xMzU3NCA0Ny41MDc4KSIgZmlsbD0iIzAwQjk1QyI+PC9lbGxpcHNlPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyNV9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iNjYuNDYxNyIgY3k9IjI0Ljk3NyIgcng9IjE4LjA5MzMiIHJ5PSIxNy40MjI5IiBmaWxsPSIjMzE4NkZGIj48L2VsbGlwc2U+PC9nPjxnIGZpbHRlcj0idXJsKCNmaWx0ZXI2X2ZfMTA4NTlfNDg5NCkiPjxlbGxpcHNlIGN4PSIyMC45MjkyIiBjeT0iMjIuMDc1MiIgcng9IjIwLjkyOTIiIHJ5PSIyMi4wNzUyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjc5NTk5IDAuNjA1MzEgLTAuNjA1MzIgMC43OTU5ODIgLTIuODE4ODUgLTcuNDMzMjMpIiBmaWxsPSIjRkJCQzA0Ij48L2VsbGlwc2U+PC9nPjxnIGZpbHRlcj0idXJsKCNmaWx0ZXI3X2ZfMTA4NTlfNDg5NCkiPjxlbGxpcHNlIGN4PSIyNC4xMzExIiBjeT0iMjIuMjkxOSIgcng9IjI0LjEzMTEiIHJ5PSIyMi4yOTE5IiB0cmFuc2Zvcm09Im1hdHJpeCgwLjgyNDAzNyAwLjU2NjUzNiAtMC41NjY1NDYgMC44MjQwMyAzOS42MzM4IDAuMzEwNjA4KSIgZmlsbD0iIzMxODZGRiI+PC9lbGxpcHNlPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyOF9mXzEwODU5XzQ4OTQpIj48cGF0aCBkPSJNNTQuMjI1NSAtMi4zMDQwM0M1Ny4wMTk1IDEuNDk0NjIgNTMuNDI5NCA4Ljg4MDQgNDYuMjA2OCAxNC4xOTI2QzM4Ljk4NDIgMTkuNTA0OCAzMC44NjQyIDIwLjczMTggMjguMDcwMiAxNi45MzMxQzI1LjI3NjIgMTMuMTM0NSAyOC44NjYzIDUuNzQ4NjcgMzYuMDg4OSAwLjQzNjQ4NkM0My4zMTE1IC00Ljg3NTcgNTEuNDMxNSAtNi4xMDI2NyA1NC4yMjU1IC0yLjMwNDAzWiIgZmlsbD0iIzc0OUJGRiI+PC9wYXRoPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyOV9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iMjcuNTg1MyIgY3k9IjE3LjE0NzgiIHJ4PSIyNy41ODUzIiByeT0iMTcuMTQ3OCIgdHJhbnNmb3JtPSJtYXRyaXgoMC43MzMxNjYgLTAuNjgwMDQ5IDAuNjgwMDYxIDAuNzMzMTU1IC0xMi4yNTgzIDkuNDk2OTUpIiBmaWxsPSIjRkM0MTNEIj48L2VsbGlwc2U+PC9nPjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIxMF9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iMTQuNzgxOSIgY3k9IjguNTk2MzciIHJ4PSIxNC43ODE5IiByeT0iOC41OTYzNyIgdHJhbnNmb3JtPSJtYXRyaXgoMC44MTMxODYgMC41ODIwMDQgLTAuNTgyMDE2IDAuODEzMTc3IDYuMzc4NDIgMzAuNTExKSIgZmlsbD0iI0ZGRUU0OCI+PC9lbGxpcHNlPjwvZz48L2c+PGRlZnM+PGZpbHRlciBpZD0iZmlsdGVyMF9mXzEwODU5XzQ4OTQiIHg9Ii0xOS42MTgiIHk9IjEyLjkwMjciIHdpZHRoPSIzOC44NjgxIiBoZWlnaHQ9IjQyLjc1NjIiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCI+PC9mZUZsb29kPjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9InNoYXBlIj48L2ZlQmxlbmQ+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMi40NTk2NSIgcmVzdWx0PSJlZmZlY3QxX2ZvcmVncm91bmRCbHVyXzEwODU5XzQ4OTQiPjwvZmVHYXVzc2lhbkJsdXI+PC9maWx0ZXI+PGZpbHRlciBpZD0iZmlsdGVyMV9mXzEwODU5XzQ4OTQiIHg9Ii0xNS4xMjIzIiB5PSItNDAuMDI5NiIgd2lkdGg9Ijg0LjM1MzMiIGhlaWdodD0iODUuMTYxNSIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ij48L2ZlRmxvb2Q+PGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0ic2hhcGUiPjwvZmVCbGVuZD48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxMS44OTExIiByZXN1bHQ9ImVmZmVjdDFfZm9yZWdyb3VuZEJsdXJfMTA4NTlfNDg5NCI+PC9mZUdhdXNzaWFuQmx1cj48L2ZpbHRlcj48ZmlsdGVyIGlkPSJmaWx0ZXIyX2ZfMTA4NTlfNDg5NCIgeD0iLTIwLjc2ODIiIHk9IjExLjQ4MzUiIHdpZHRoPSI3OC45MTYxIiBoZWlnaHQ9IjkwLjIxOTYiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCI+PC9mZUZsb29kPjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9InNoYXBlIj48L2ZlQmxlbmQ+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMTAuMTA4NiIgcmVzdWx0PSJlZmZlY3QxX2ZvcmVncm91bmRCbHVyXzEwODU5XzQ4OTQiPjwvZmVHYXVzc2lhbkJsdXI+PC9maWx0ZXI+PGZpbHRlciBpZD0iZmlsdGVyM19mXzEwODU5XzQ4OTQiIHg9Ii0yMC43NjgyIiB5PSIxMS40ODM1IiB3aWR0aD0iNzguOTE2MSIgaGVpZ2h0PSI5MC4yMTk2IiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjEwLjEwODYiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjRfZl8xMDg1OV80ODk0IiB4PSItMTkuODUiIHk9IjE0Ljk2NjQiIHdpZHRoPSI3OS4xODg2IiBoZWlnaHQ9IjgwLjkzNzgiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCI+PC9mZUZsb29kPjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9InNoYXBlIj48L2ZlQmxlbmQ+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMTAuMTA4NiIgcmVzdWx0PSJlZmZlY3QxX2ZvcmVncm91bmRCbHVyXzEwODU5XzQ4OTQiPjwvZmVHYXVzc2lhbkJsdXI+PC9maWx0ZXI+PGZpbHRlciBpZD0iZmlsdGVyNV9mXzEwODU5XzQ4OTQiIHg9IjI5LjE1NjEiIHk9Ii0xMS42NTgyIiB3aWR0aD0iNzQuNjExMSIgaGVpZ2h0PSI3My4yNzAzIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjkuNjA2MTMiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjZfZl8xMDg1OV80ODk0IiB4PSItMzguMjkxIiB5PSItMTYuMjY4NyIgd2lkdGg9Ijc3LjUzOCIgaGVpZ2h0PSI3OC4xNTEzIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjguNzA1OTEiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjdfZl8xMDg1OV80ODk0IiB4PSI3Ljc4MDM4IiB5PSItNi4wOTgxIiB3aWR0aD0iNzguMjE4MSIgaGVpZ2h0PSI3Ni44OTgyIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjcuNzc0NzMiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjhfZl8xMDg1OV80ODk0IiB4PSIxMy4yMDgyIiB5PSItMTguNDI1IiB3aWR0aD0iNTUuODc5MyIgaGVpZ2h0PSI1MS40NzkxIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjYuOTU2OTQiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjlfZl8xMDg1OV80ODk0IiB4PSItMTUuNDczOSIgeT0iLTMxLjAyNzIiIHdpZHRoPSI3MC4yMDM0IiBoZWlnaHQ9IjY4LjY3MzUiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCI+PC9mZUZsb29kPjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9InNoYXBlIj48L2ZlQmxlbmQ+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iNS44NzU5OCIgcmVzdWx0PSJlZmZlY3QxX2ZvcmVncm91bmRCbHVyXzEwODU5XzQ4OTQiPjwvZmVHYXVzc2lhbkJsdXI+PC9maWx0ZXI+PGZpbHRlciBpZD0iZmlsdGVyMTBfZl8xMDg1OV80ODk0IiB4PSItMTQuMTczIiB5PSIyMC40NzQiIHdpZHRoPSI1NS4xMzczIiBoZWlnaHQ9IjUxLjI2MSIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ij48L2ZlRmxvb2Q+PGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0ic2hhcGUiPjwvZmVCbGVuZD48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSI3LjI3MjUzIiByZXN1bHQ9ImVmZmVjdDFfZm9yZWdyb3VuZEJsdXJfMTA4NTlfNDg5NCI+PC9mZUdhdXNzaWFuQmx1cj48L2ZpbHRlcj48bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMTA4NTlfNDg5NCIgeDE9IjE4LjE5MzEiIHkxPSI0Mi44MjEiIHgyPSI1MS40MzM1IiB5Mj0iMTQuNzk1OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiM0ODkzRkMiPjwvc3RvcD48c3RvcCBvZmZzZXQ9IjAuMjciIHN0b3AtY29sb3I9IiM0ODkzRkMiPjwvc3RvcD48c3RvcCBvZmZzZXQ9IjAuNzc2OTgxIiBzdG9wLWNvbG9yPSIjOTY5REZGIj48L3N0b3A+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjQkQ5OUZFIj48L3N0b3A+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PC9zdmc+Cg=="},"displayName":"Google Gemini","typeVersion":1,"nodeCategories":[{"id":25,"name":"AI"},{"id":26,"name":"Langchain"}]}],"categories":[{"id":32,"name":"Market Research"},{"id":49,"name":"AI Summarization"}],"image":[]}}