PageSourceSearch

https://myechoboard.com/

html myechoboard.com collected 2026-10-02 00:17:33 UTC 113,572 bytes, 1,203 lines download raw bytes

1<!doctype html>
2<html data-echo-prerender="landing" lang="en" class="notranslate" translate="no">
3
4<head>
5    <meta name="echo-app" content="true" />
6  <!-- Machine translators replace React-owned text nodes with <font> wrappers,
7       which breaks React's insertBefore/removeChild invariants on live feeds. -->
8  <meta name="google" content="notranslate">
9  <meta name="apple-itunes-app" content="app-id=6760357340">
10  <!-- CRITICAL: Apply cached theme IMMEDIATELY to prevent flash -->
11  <!-- This script MUST run synchronously before any rendering -->
12  
12<script>
13    (function () {
14      try {
15        // Some Android System WebView versions used by installed Echo builds do
16        // not provide Array.prototype.at. App code and web-vitals both use
17        // `.at(-1)`, so install the compatibility method before any module runs.
18        if (typeof Array.prototype.at !== 'function') {
19          Object.defineProperty(Array.prototype, 'at', {
20            configurable: true,
21            writable: true,
22            value: function (index) {
23              var length = Number(this.length) || 0;
24              var integer = Number(index) || 0;
25              integer = integer < 0 ? Math.ceil(integer) : Math.floor(integer);
26              var resolvedIndex = integer < 0 ? length + integer : integer;
27              return resolvedIndex < 0 || resolvedIndex >= length
28                ? undefined
29                : this[resolvedIndex];
30            }
31          });
32        }
33
34        var launchSurfaceColor = '#FCF6F5';
35        var root = document.documentElement;
36        var bootStart = typeof performance !== 'undefined' && typeof performance.now === 'function'
37          ? performance.now()
38          : Date.now();
39        var userAgent = navigator.userAgent || '';
40        var supabaseUrl = 'https://lacgasmxkbfxyfybpeop.supabase.co';
41        var supabaseAnonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxhY2dhc214a2JmeHlmeWJwZW9wIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjY2NjgyNTMsImV4cCI6MjA4MjI0NDI1M30.R5LoArlO4HEJpKpYt7u4R-I8TSfHv_CMeeWvPzeF8js';
42        var isNativeAndroidShell =
43          userAgent.indexOf('Echo/') !== -1 && userAgent.indexOf('(Android)') !== -1;
44        var isNativeIosShell =
45          (userAgent.indexOf('Echo/') !== -1 && userAgent.indexOf('(iOS)') !== -1) ||
46          window.location.protocol === 'capacitor:' ||
47          window.location.protocol === 'ionic:';
48        var isNativeShell =
49          isNativeAndroidShell ||
50          isNativeIosShell;
51        var normalizedBootPath = window.location.pathname.replace(/\/+$/, '') || '/';
52
53        // The public landing page is real build-time HTML. Suppress it before
54        // first paint when the SPA shell is serving an authenticated, native,
55        // callback, or unrelated route; the app clears this guard on mount.
56        if (root.hasAttribute('data-echo-prerender')) {
57          var skipLandingPrerender = isNativeShell ||
58            (root.getAttribute('data-echo-prerender') === 'editorial'
59              ? !/^\/(blog|alternatives)(\/[^/]+)?$/.test(normalizedBootPath)
60              : (normalizedBootPath !== '/' && normalizedBootPath !== '/frontpage'));
61          var landingCallbackParams = new URLSearchParams(
62            window.location.search + '&' + window.location.hash.replace(/^#/, '')
63          );
64          skipLandingPrerender = skipLandingPrerender ||
65            ['code', 'access_token', 'refresh_token', 'error', 'error_code'].some(function (key) {
66              return landingCallbackParams.has(key);
67            });
68          try {
69            var prerenderProjectRef = 'lacgasmxkbfxyfybpeop'.trim().toLowerCase();
70            if (!/^[a-z0-9]+$/.test(prerenderProjectRef)) {
71              prerenderProjectRef = new URL(supabaseUrl).hostname.split('.')[0];
72            }
73            ['localStorage', 'sessionStorage'].forEach(function (storageName) {
74              try {
75                var storedSession = JSON.parse(window[storageName].getItem('sb-' + prerenderProjectRef + '-auth-token') || 'null');
76                if (storedSession && typeof storedSession.access_token === 'string' && storedSession.access_token.length > 0 &&
77                    typeof storedSession.refresh_token === 'string' && storedSession.refresh_token.length > 0) {
78                  skipLandingPrerender = true;
79                }
80              } catch (_) { /* An unreadable session is not an auth hint. */ }
81            });
82          } catch (_) { /* Match the application's no-session fallback. */ }
83          if (skipLandingPrerender) root.setAttribute('data-echo-prerender-skip', 'true');
84        }
85
86        // The signed-out landing hero is static and otherwise cannot be
87        // discovered until the full React shell and lazy LandingPage chunk
88        // have evaluated. Preload the exact responsive candidate used by the
89        // center rail card so image transfer overlaps JavaScript startup.
90        if (!isNativeShell && (normalizedBootPath === '/' || normalizedBootPath === '/frontpage')) {
91          try {
92            var landingProjectRef = new URL(supabaseUrl).hostname.split('.')[0];
93            var landingSessionKey = 'sb-' + landingProjectRef + '-auth-token';
94            var hasLandingSession = Boolean(window.localStorage.getItem(landingSessionKey));
95            var landingR2Base = String('https://cdn.myechoboard.com' || '').replace(/\/+$/, '');
96            if (!hasLandingSession && landingR2Base) {
97              var landingHeroPath = '74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg';
98              var landingHeroMobile = window.matchMedia('(max-width: 640px)').matches;
99              var landingHeroWidths = landingHeroMobile ? [200, 400] : [400, 800, 1200];
100              var landingHeroQuality = landingHeroMobile ? 70 : 80;
101              var landingHeroUrl = function (width) {
102                return landingR2Base + '/cdn-cgi/image/width=' + width +
103                  ',quality=' + landingHeroQuality + ',format=auto/' + landingHeroPath;
104              };
105              var landingHeroPreload = document.createElement('link');
106              landingHeroPreload.rel = 'preload';
107              landingHeroPreload.as = 'image';
108              landingHeroPreload.href = landingHeroUrl(landingHeroMobile ? 400 : 800);
109              landingHeroPreload.setAttribute(
110                'imagesrcset',
111                landingHeroWidths.map(function (width) {
112                  return landingHeroUrl(width) + ' ' + width + 'w';
113                }).join(', ')
114              );
115              landingHeroPreload.setAttribute('imagesizes', '(max-width: 640px) 72vw, 340px');
116              landingHeroPreload.setAttribute('fetchpriority', 'high');
117              landingHeroPreload.setAttribute('data-echo-landing-lcp', 'true');
118              document.head.appendChild(landingHeroPreload);
119            }
120          } catch (landingHeroPreloadError) {
121            // A preload hint is optional and must never interfere with boot.
122          }
123        }
124
125        // Start the signed-out Explore data request directly from the parsed
126        // HTML. Keeping this ahead of the module graph lets the first visible
127        // images download while the application JavaScript is still loading.
128        // The React feed adopts this exact promise, so this does not duplicate
129        // the request or change the rendered data.
130        var exploreBootParams = new URLSearchParams(window.location.search);
131        var exploreHasOnlyDiagnosticParams = Array.from(exploreBootParams.keys()).every(function (key) {
132          return key === '_r' || key === '_cb' || key === '_ra';
133        });
134        if (!isNativeShell && normalizedBootPath === '/explore' && exploreHasOnlyDiagnosticParams) {
135          try {
136            var supabaseProjectRef = new URL(supabaseUrl).hostname.split('.')[0];
137            var supabaseSessionKey = 'sb-' + supabaseProjectRef + '-auth-token';
138            var hasSupabaseSession = Boolean(window.localStorage.getItem(supabaseSessionKey));
139            if (!hasSupabaseSession && !window.__ECHO_DIRECT_EXPLORE_WARMUP__) {
140              var exploreWarmupController = new AbortController();
141              var exploreWarmupTimeout = window.setTimeout(function () {
142                exploreWarmupController.abort();
143              }, 6500);
144              var exploreWarmupParams = new URLSearchParams({
145                apikey: supabaseAnonKey,
146                limit_count: '16',
147                p_offset: '0',
148                p_time_range: 'all_time',
149                p_ai_filters: '{}'
150              });
151              var exploreWarmupRequest = fetch(
152                supabaseUrl +
153                  '/rest/v1/rpc/get_public_explore_echo_feed_new_fast?' +
154                  exploreWarmupParams.toString(),
155                {
156                  method: 'GET',
157                  signal: exploreWarmupController.signal
158                }
159              );
160
161              window.__ECHO_DIRECT_EXPLORE_WARMUP__ = exploreWarmupRequest
162                .then(function (response) {
163                  if (!response.ok) {
164                    throw new Error('Explore bootstrap feed failed: ' + response.status);
165                  }
166                  return response.json();
167                })
168                .then(function (value) {
169                  var rows = Array.isArray(value) ? value : [];
170                  var r2Base = String('https://cdn.myechoboard.com' || '').replace(/\/+$/, '');
171                  var firstRowLimit = window.matchMedia('(max-width: 767px)').matches ? 1 : 2;
172                  var seenImageUrls = {};
173                  var preloadedCount = 0;
174
175                  rows.some(function (row) {
176                    var metadata = row && row.metadata && typeof row.metadata === 'object'
177                      ? row.metadata
178                      : {};
179                    var source = String(metadata.thumbnail_url || (row && row.image_url) || '').trim();
180                    if (!source) return false;
181                    var imageUrl = source;
182                    if (
183                      r2Base &&
184                      source.indexOf(r2Base + '/') === 0 &&
185                      source.indexOf('/cdn-cgi/image/') === -1
186                    ) {
187                      imageUrl = r2Base +
188                        '/cdn-cgi/image/width=400,quality=70,format=auto/' +
189                        source.slice(r2Base.length).replace(/^\/+/, '');
190                    }
191                    if (seenImageUrls[imageUrl]) return false;
192                    seenImageUrls[imageUrl] = true;
193
194                    var preload = document.createElement('link');
195                    preload.rel = 'preload';
196                    preload.as = 'image';
197                    preload.href = imageUrl;
198                    if (r2Base && source.indexOf(r2Base + '/') === 0 && source.indexOf('/cdn-cgi/image/') === -1) {
199                      var imagePath = source.slice(r2Base.length).replace(/^\/+/, '');
200                      preload.setAttribute('imagesrcset',
201                        r2Base + '/cdn-cgi/image/width=200,quality=65,format=auto/' + imagePath + ' 200w, ' +
202                        r2Base + '/cdn-cgi/image/width=360,quality=65,format=auto/' + imagePath + ' 360w, ' +
203                        imageUrl + ' 400w'
204                      );
205                      // Cap anonymous mobile thumbnails near 2x CSS density.
206                      // A full 3x candidate adds bytes without a visible card-
207                      // grid benefit on throttled first paint.
208                      preload.setAttribute('imagesizes', '(max-width: 700px) 33vw, 244px');
209                    }
210                    preload.setAttribute('fetchpriority', 'high');
211                    preload.setAttribute('data-echo-direct-explore-image', imageUrl);
212                    document.head.appendChild(preload);
213                    preloadedCount += 1;
214                    return preloadedCount >= firstRowLimit;
215                  });
216                  return rows;
217                })
218                .finally(function () {
219                  window.clearTimeout(exploreWarmupTimeout);
220                });
221              window.__ECHO_DIRECT_EXPLORE_WARMUP__.catch(function () {});
222            }
223          } catch (exploreWarmupError) {
224            // Speculative startup work must never block normal application boot.
225          }
226        }
227        var bootDebugEnabled =
228          window.location.search.indexOf('echoBootDebug=1') !== -1;
229        try {
230          bootDebugEnabled =
231            bootDebugEnabled ||
232            window.localStorage.getItem('echo:debug:startup') === 'true';
233        } catch (error) {
234          // Startup tracing stays disabled when storage is unavailable.
235        }
236        if (bootDebugEnabled) {
237          root.setAttribute('data-echo-startup-debug', 'true');
238        }
239
240        window.__ECHO_BOOT_LOGS__ = window.__ECHO_BOOT_LOGS__ || [];
241        window.__ECHO_NATIVE_AUTH_QUEUE__ = window.__ECHO_NATIVE_AUTH_QUEUE__ || [];
242        window.__ECHO_PUSH_BOOT_LOG__ = function (label, detail) {
243          if (!bootDebugEnabled) return;
244          try {
245            var now = typeof performance !== 'undefined' && typeof performance.now === 'function'
246              ? performance.now()
247              : Date.now();
248            var entry = {
249              t: Math.max(0, Math.round(now - bootStart)),
250              label: String(label || 'event'),
251              detail: detail ? String(detail) : ''
252            };
253            window.__ECHO_BOOT_LOGS__.push(entry);
254
255            console.log('[boot][' + entry.t + 'ms]', entry.label, entry.detail || '');
256          } catch (e) {
257            console.log('[boot]', label, detail || '');
258          }
259        };
260
261        window.__ECHO_PUSH_BOOT_LOG__('inline-bootstrap-start');
262
263        if (isNativeShell) {
264          root.setAttribute('data-native-bootstrap', 'true');
265          root.setAttribute('data-native-platform', isNativeAndroidShell ? 'android' : 'ios');
266          root.classList.add(isNativeAndroidShell ? 'native-android' : 'native-ios');
267          window.__ECHO_PUSH_BOOT_LOG__('native-shell-detected', window.location.pathname);
268
269          var nativeAuthHandler =
270            window.webkit &&
271            window.webkit.messageHandlers &&
272            window.webkit.messageHandlers.echoNativeAuth;
273
274          if (nativeAuthHandler) {
275            var storageKey = (function () {
276              try {
277                return 'sb-' + new URL(supabaseUrl).hostname.split('.')[0] + '-auth-token';
278              } catch (error) {
279                return 'sb-native-auth-token';
280              }
281            })();
282
283            function postNativeAuth(payload) {
284              try {
285                nativeAuthHandler.postMessage(payload);
286              } catch (error) {
287                console.warn('[native-auth-inline] Failed to post native payload', error);
288              }
289            }
290
291            function persistNativeSession(response) {
292              if (!response || !response.access_token || !response.refresh_token) return false;
293
294              var session = {
295                access_token: response.access_token,
296                refresh_token: response.refresh_token,
297                token_type: response.token_type,
298                expires_in: response.expires_in,
299                expires_at: response.expires_at || Math.floor(Date.now() / 1000) + Number(response.expires_in || 3600),
300                user: response.user || null
301              };
302
303              localStorage.setItem(storageKey, JSON.stringify(session));
304
305              if (response.user) {
306                localStorage.setItem(storageKey + '-user', JSON.stringify({ user: response.user }));
307              }
308
309              return true;
310            }
311
312            function finishNativeAuthToApp() {
313              try {
314                window.__ECHO_FORCE_FULL_APP__ = true;
315                window.__ECHO_PUSH_BOOT_LOG__('native-auth-inline-finish-to-app');
316                window.location.replace('/?nativeAuth=' + Date.now());
317              } catch (error) {
318                window.location.href = '/';
319              }
320            }
321
322            async function handleInlineNativeAuth(payload) {
323              var command = typeof payload === 'string' ? JSON.parse(payload) : payload;
324              var headers = {
325                'Content-Type': 'application/json',
326                'apikey': supabaseAnonKey
327              };
328
329              try {
330                if (command.type === 'social') {
331                  // Queue social login until the module bridge is ready. It owns
332                  // the PKCE verifier and one-time callback transaction; this
333                  // early shell must never start an implicit token flow.
334                  return false;
335                }
336
337                if (command.type === 'signIn') {
338                  var signInResponse = await fetch(supabaseUrl + '/auth/v1/token?grant_type=password', {
339                    method: 'POST',
340                    headers: headers,
341                    body: JSON.stringify({
342                      email: String(command.email || '').trim(),
343                      password: String(command.password || '')
344                    })
345                  });
346                  var signInJson = await signInResponse.json();
347
348                  if (!signInResponse.ok) {
349                    throw new Error(signInJson.msg || signInJson.error_description || signInJson.error || 'Authentication failed.');
350                  }
351
352                  persistNativeSession(signInJson);
353                  postNativeAuth({ type: 'command-result', success: true });
354                  postNativeAuth({ type: 'session-state', event: 'SIGNED_IN', authenticated: true });
355                  finishNativeAuthToApp();
356                  return true;
357                }
358
359                if (command.type === 'signUp') {
360                  var signUpResponse = await fetch(supabaseUrl + '/auth/v1/signup', {
361                    method: 'POST',
362                    headers: headers,
363                    body: JSON.stringify({
364                      email: String(command.email || '').trim(),
365                      password: String(command.password || ''),
366                      data: {
367                        full_name: String(command.fullName || '').trim()
368                      }
369                    })
370                  });
371                  var signUpJson = await signUpResponse.json();
372
373                  if (!signUpResponse.ok) {
374                    throw new Error(signUpJson.msg || signUpJson.error_description || signUpJson.error || 'Could not create account.');
375                  }
376
377                  var authenticated = persistNativeSession(signUpJson);
378                  postNativeAuth({
379                    type: 'command-result',
380                    success: true,
381                    message: authenticated
382                      ? 'Account created.'
383                      : 'Account created. Check your email to verify your address.'
384                  });
385                  postNativeAuth({
386                    type: 'session-state',
387                    event: authenticated ? 'SIGNED_IN' : 'SIGNED_OUT',
388                    authenticated: authenticated
389                  });
390                  if (authenticated) {
391                    finishNativeAuthToApp();
392                  }
393                  return true;
394                }
395              } catch (authError) {
396                postNativeAuth({
397                  type: 'command-result',
398                  success: false,
399                  message: authError && authError.message ? authError.message : 'Authentication failed.'
400                });
401                return true;
402              }
403
404              return false;
405            }
406
407            window.__ECHO_NATIVE_AUTH_RUN__ = function (payload) {
408              try {
409                handleInlineNativeAuth(payload).then(function (handled) {
410                  if (!handled) {
411                    window.__ECHO_NATIVE_AUTH_QUEUE__.push(payload);
412                  }
413                });
414                window.__ECHO_PUSH_BOOT_LOG__('native-auth-command-queued');
415                return true;
416              } catch (queueError) {
417                console.warn('[native-auth-queue] Failed to queue payload', queueError);
418                return false;
419              }
420            };
421
422            try {
423              postNativeAuth({ type: 'bridge-ready', stage: 'inline-shell' });
424              window.__ECHO_PUSH_BOOT_LOG__('native-auth-inline-bridge-ready');
425              if (window.location.pathname === '/auth' || window.location.pathname === '/auth2') {
426                postNativeAuth({ type: 'present-native-auth', stage: 'inline-auth-route' });
427                window.__ECHO_PUSH_BOOT_LOG__('native-auth-inline-present-requested');
428              }
429            } catch (bridgeError) {
430              console.warn('[native-auth-queue] Failed to notify native shell', bridgeError);
431            }
432          }
433        }
434
435        var isNativeAuthRoute = isNativeShell && (
436          window.location.pathname === '/' ||
437          window.location.pathname === '/auth' ||
438          window.location.pathname === '/auth2'
439        );
440
441        function loadGtm() {
442          if (window.location.pathname === '/__feed-performance') return;
443          if (window.__echoGtmLoaded) return;
444          window.__echoGtmLoaded = true;
445          window.dataLayer = window.dataLayer || [];
446          window.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
447          var f = document.getElementsByTagName('script')[0];
448          var j = document.createElement('script');
449          var dl = 'dataLayer' !== 'dataLayer' ? '&l=dataLayer' : '';
450          j.async = true;
451          j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WLQS285X' + dl;
452          f.parentNode.insertBefore(j, f);
453        }
454
455        function scheduleGtm() {
456          if (window.__echoGtmScheduled || window.__echoGtmLoaded) return;
457          window.__echoGtmScheduled = true;
458
459          var fallbackTimer = null;
460          var interactionEvents = ['pointerdown', 'touchstart', 'keydown'];
461          var startGtm = function (event) {
462            interactionEvents.forEach(function (eventName) {
463              window.removeEventListener(eventName, startGtm);
464            });
465            if (fallbackTimer !== null) {
466              window.clearTimeout(fallbackTimer);
467              fallbackTimer = null;
468            }
469            // Do not make the first user gesture contend with vendor parsing.
470            window.setTimeout(loadGtm, event ? 1000 : 0);
471          };
472          var scheduleFallback = function () {
473            // Keep analytics and tag-manager vendors out of the route/LCP
474            // critical path. dataLayer events queue until GTM is available.
475            fallbackTimer = window.setTimeout(startGtm, 30000);
476          };
477
478          interactionEvents.forEach(function (eventName) {
479            window.addEventListener(eventName, startGtm, {
480              once: true,
481              passive: true
482            });
483          });
484
485          if (document.readyState === 'complete') {
486            scheduleFallback();
487          } else {
488            window.addEventListener('load', scheduleFallback, { once: true });
489          }
490        }
491
492        function loadGoogleFonts() {
493          if (window.__echoFontsLoaded) return;
494          window.__echoFontsLoaded = true;
495          var href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Crimson+Pro:ital,wght@0,400;0,700;1,400&display=swap';
496          var preconnectApi = document.createElement('link');
497          preconnectApi.rel = 'preconnect';
498          preconnectApi.href = 'https://fonts.googleapis.com';
499          document.head.appendChild(preconnectApi);
500
501          var preconnectStatic = document.createElement('link');
502          preconnectStatic.rel = 'preconnect';
503          preconnectStatic.href = 'https://fonts.gstatic.com';
504          preconnectStatic.crossOrigin = 'anonymous';
505          document.head.appendChild(preconnectStatic);
506
507          var preload = document.createElement('link');
508          preload.rel = 'preload';
509          preload.as = 'style';
510          preload.href = href;
511          document.head.appendChild(preload);
512
513          var stylesheet = document.createElement('link');
514          stylesheet.rel = 'stylesheet';
515          stylesheet.href = href;
516          stylesheet.media = 'print';
517          stylesheet.onload = function () { stylesheet.media = 'all'; };
518          document.head.appendChild(stylesheet);
519        }
520
521        function loadDeferredHeadAssets() {
522          if (isNativeShell) {
523            return;
524          }
525
526          loadGoogleFonts();
527          scheduleGtm();
528        }
529
530        if (isNativeShell) {
531          window.addEventListener('echo:first-frame', function onFirstFrame() {
532            window.removeEventListener('echo:first-frame', onFirstFrame);
533            window.__ECHO_FIRST_FRAME_COMPLETE__ = true;
534            window.__ECHO_PUSH_BOOT_LOG__('first-frame-event-received');
535            window.setTimeout(loadDeferredHeadAssets, 600);
536          });
537        } else {
538          loadDeferredHeadAssets();
539        }
540
541        var cache = isNativeAuthRoute ? null : (localStorage.getItem('echo_theme_cache') || localStorage.getItem('echo_mood_theme'));
542        var isDarkFlag = isNativeAuthRoute ? false : localStorage.getItem('echo_theme_is_dark') === 'true';
543
544        // Helper to check if a hex color is dark
545        function isDarkColor(hex) {
546          if (!hex || hex.length < 7) return false;
547          hex = hex.replace(/^#/, '');
548          var r = parseInt(hex.substring(0, 2), 16);
549          var g = parseInt(hex.substring(2, 4), 16);
550          var b = parseInt(hex.substring(4, 6), 16);
551          return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 < 0.5;
552        }
553
554        var theme = null;
555        if (cache) {
556          theme = JSON.parse(cache);
557        }
558
559        window.__ECHO_PUSH_BOOT_LOG__('theme-cache-check', theme ? 'hit' : 'miss');
560
561        // 1. Apply dark class FIRST (before any styles)
562        var shouldBeDark = isDarkFlag || (theme && theme.is_dark) || (theme && theme.canvas_tone && isDarkColor(theme.canvas_tone));
563        if (shouldBeDark) {
564          root.classList.add('dark');
565        }
566
567        // 2. Build and inject critical CSS + Mark as applied
568        if (theme) {
569          // Mark theme as applied so CSS doesn't override
570          root.setAttribute('data-theme-applied', 'true');
571
572          var css = '';
573
574          // Background color - most critical
575          if (theme.canvas_tone) {
576            root.style.backgroundColor = theme.canvas_tone;
577            css += 'html, body { background-color: ' + theme.canvas_tone + ' !important; }';
578          }
579
580          // Font
581          var font = theme.font_family || localStorage.getItem('echo_last_font_family');
582          if (font) {
583            var fontStack = font === 'Manrope' ? '"Manrope", "Manrope Fallback", sans-serif' : '"' + font + '", sans-serif';
584            css += 'html, body, button, input, select, textarea { font-family: var(--font-mood, ' + fontStack + ') !important; }';
585          }
586
587          // Inject the style tag
588          if (css) {
589            var style = document.createElement('style');
590            style.id = 'echo-critical-css';
591            style.textContent = css;
592            document.head.appendChild(style);
593          }
594        } else {
595          // No cache - keep auth/native startup on the warm launch color to avoid dark flashes.
596          root.style.backgroundColor = isNativeShell ? launchSurfaceColor : '#ffffff';
597        }
598      } catch (e) {
599        // Fail silently, React will handle it
600        document.documentElement.style.backgroundColor = '#FCF6F5';
601        if (window.__ECHO_PUSH_BOOT_LOG__) {
602          window.__ECHO_PUSH_BOOT_LOG__('inline-bootstrap-error', e && e.message ? e.message : 'unknown');
603        }
604      }
605    })();
606  </script>
606
607  <meta charset="UTF-8" />
608  <meta name="viewport"
609    content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
610  <meta name="author" content="Echo" />
611
612  <!-- Native App Meta Tags -->
613  <meta name="apple-mobile-web-app-capable" content="yes" />
614  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
615  <meta name="apple-mobile-web-app-title" content="Echo" />
616  <meta name="theme-color" content="#FCF6F5" media="(prefers-color-scheme: dark)" />
617  <meta name="theme-color" content="#FCF6F5" media="(prefers-color-scheme: light)" />
618  <meta name="format-detection" content="telephone=no" />
619  <meta name="mobile-web-app-capable" content="yes" />
620
621  <!-- Performance: Preconnect to Supabase for faster image loading -->
622  <link rel="preconnect" href="https://lacgasmxkbfxyfybpeop.supabase.co" crossorigin>
623  <link rel="dns-prefetch" href="https://lacgasmxkbfxyfybpeop.supabase.co">
624  <!-- Performance: Preconnect to R2 CDN for faster image loading -->
625  <link rel="preconnect" href="https://cdn.myechoboard.com" crossorigin>
626  <link rel="dns-prefetch" href="https://cdn.myechoboard.com">
627
628  <link rel="icon" type="image/x-icon" href="/favicon.ico" sizes="any" />
629  <link rel="icon" type="image/png" href="/favicon.png" sizes="512x512" />
630  <link rel="apple-touch-icon" href="/favicon.png" />
631
632  <!-- Open Graph / Facebook -->
633  <!-- Twitter -->
634  <style>
635    :root {
636      --echo-launch-surface: #FCF6F5;
637    }
638
639    /* 
640     * Z-INDEX LAYERS:
641     * html = background canvas (z-auto)
642     * #star-container = z-2 (stars above background)
643     * #root = z-3 (content above stars, must be transparent)
644     */
645    #star-container {
646      position: fixed;
647      inset: 0;
648      pointer-events: none;
649      z-index: 2;
650    }
651
652    html,
653    body {
654      background-color: var(--echo-launch-surface);
655      /* Lean auth/startup routes do not load the full application stylesheet. */
656      overscroll-behavior: none;
657    }
658
659    #root {
660      position: relative;
661      z-index: 3;
662      background-color: transparent;
663    }
664
665    html[data-echo-prerender-skip="true"] #root[data-echo-prerender] {
666      display: none;
667    }
668
669    /* The rendered public page is usable without JavaScript. */
670    html[data-echo-prerender] noscript .echo-boot-recovery {
671      display: none;
672    }
673
674    .echo-boot-recovery {
675      box-sizing: border-box;
676      position: fixed;
677      inset: 0;
678      z-index: 2147483647;
679      display: flex;
680      align-items: center;
681      justify-content: center;
682      padding: 24px;
683      background: rgba(252, 246, 245, 0.92);
684      background: color-mix(in srgb, var(--echo-launch-surface) 88%, transparent);
685      color: #211f2a;
686      font-family: Manrope, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
687    }
688
689    .echo-boot-recovery__card {
690      width: min(100%, 420px);
691      border: 1px solid rgba(76, 65, 108, 0.16);
692      border-radius: 24px;
693      padding: 28px;
694      background: rgba(255, 255, 255, 0.82);
695      box-shadow: 0 24px 70px rgba(55, 43, 86, 0.12);
696      text-align: center;
697    }
698
699    .echo-boot-recovery__card h1 {
700      margin: 0 0 10px;
701      font-size: 24px;
702      line-height: 1.2;
703    }
704
705    .echo-boot-recovery__card p {
706      margin: 0 0 20px;
707      color: #625d70;
708      line-height: 1.55;
709    }
710
711    .echo-boot-recovery__card button {
712      -webkit-appearance: none;
713         -moz-appearance: none;
714              appearance: none;
715      border: 0;
716      border-radius: 999px;
717      padding: 12px 20px;
718      background: #6d52a3;
719      color: #fff;
720      font: inherit;
721      font-weight: 700;
722      cursor: pointer;
723    }
724
725    .echo-boot-recovery__card button:disabled {
726      cursor: wait;
727      opacity: 0.72;
728    }
729
730    .star {
731      position: absolute;
732      border-radius: 50%;
733      background-color: var(--star-color, #ffffff);
734      animation-name: twinkle;
735      animation-timing-function: ease-in-out;
736      animation-iteration-count: infinite;
737      animation-direction: alternate;
738    }
739
740    @keyframes twinkle {
741      0% {
742        transform: scale(1);
743        opacity: 0.3;
744      }
745
746      100% {
747        transform: scale(1);
748        opacity: 1;
749      }
750    }
751  </style>
752  
752<script type="module" crossorigin src="/assets/app-_uSXIk0p.js"></script>
752
753  <link rel="modulepreload" crossorigin href="/assets/runtime-preload-CFP917pd.js">
754  <link rel="modulepreload" crossorigin href="/assets/startup-core-CGeUO7uJ.js">
755  <link rel="modulepreload" crossorigin href="/assets/startup-platform-OWIpYyjW.js">
756  <link rel="modulepreload" crossorigin href="/assets/startup-diagnostics-bridge-BB8dzHvV.js">
757<link rel="manifest" href="/manifest.webmanifest"><title>Echo — The Ad-Free Pinterest Alternative</title>
758<meta name="description" content="Looking for a Pinterest alternative? Discover inspiration, organize ideas into boards, and import Pinterest boards with Echo. Ad-free and private by default." data-echo-seo-managed="true">
759<link rel="canonical" href="https://myechoboard.com/" data-echo-seo-managed="true">
760<meta property="og:type" content="website" data-echo-seo-managed="true">
761<meta property="og:title" content="Echo — A Pinterest alternative without ads" data-echo-seo-managed="true">
762<meta property="og:description" content="Discover ideas you love. Save them into beautiful boards. Your inspiration, organized and ad-free." data-echo-seo-managed="true">
763<meta property="og:url" content="https://myechoboard.com/" data-echo-seo-managed="true">
764<meta property="og:image" content="https://myechoboard.com/og/echo-pinterest-alternative.jpg" data-echo-seo-managed="true">
765<meta property="og:image:secure_url" content="https://myechoboard.com/og/echo-pinterest-alternative.jpg" data-echo-seo-managed="true">
766<meta property="og:image:alt" content="Echo — Keep what inspires you. A preview of an interior inspiration board in Echo." data-echo-seo-managed="true">
767<meta property="og:image:width" content="1200" data-echo-seo-managed="true">
768<meta property="og:image:height" content="630" data-echo-seo-managed="true">
769<meta property="og:site_name" content="Echo" data-echo-seo-managed="true">
770<meta property="og:locale" content="en_US" data-echo-seo-managed="true">
771<meta name="twitter:card" content="summary_large_image" data-echo-seo-managed="true">
772<meta name="twitter:title" content="Echo — A Pinterest alternative without ads" data-echo-seo-managed="true">
773<meta name="twitter:description" content="Discover ideas you love. Save them into beautiful boards. Your inspiration, organized and ad-free." data-echo-seo-managed="true">
774<meta name="twitter:image" content="https://myechoboard.com/og/echo-pinterest-alternative.jpg" data-echo-seo-managed="true">
775<meta name="twitter:image:alt" content="Echo — Keep what inspires you. A preview of an interior inspiration board in Echo." data-echo-seo-managed="true">
776<meta name="twitter:url" content="https://myechoboard.com/" data-echo-seo-managed="true">
777<link rel="stylesheet" crossorigin href="/assets/index-Dx_MVqAM.css">
778<script type="application/ld+json" data-echo-seo-managed="true">{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id":"https://myechoboard.com/#website","name":"Echo","url":"https://myechoboard.com/","description":"Echo is an ad-free Pinterest alternative for discovering inspiration and organizing ideas. Available on web, iOS, and Android, it lets you save images, links, and notes into boards. Personal collections are private by default, with public boards available for sharing.","publisher":{"@id":"https://myechoboard.com/#organization"}},{"@type":"WebPage","@id":"https://myechoboard.com/#webpage","name":"Echo — The Ad-Free Pinterest Alternative","url":"https://myechoboard.com/","description":"Echo is an ad-free Pinterest alternative for discovering inspiration and organizing ideas. Available on web, iOS, and Android, it lets you save images, links, and notes into boards. Personal collections are private by default, with public boards available for sharing.","isPartOf":{"@id":"https://myechoboard.com/#website"},"about":{"@id":"https://myechoboard.com/#application"}},{"@type":"Organization","@id":"https://myechoboard.com/#organization","name":"Echo","url":"https://myechoboard.com/","logo":"https://myechoboard.com/logo.png","description":"Echo builds a private-by-default platform for collecting inspiration, organizing boards, and rediscovering ideas over time."},{"@type":"SoftwareApplication","@id":"https://myechoboard.com/#application","name":"Echo","applicationCategory":"CreativeWorkApplication","operatingSystem":"Web, iOS, Android","url":"https://myechoboard.com/","publisher":{"@id":"https://myechoboard.com/#organization"},"description":"Echo is an ad-free Pinterest alternative for discovering inspiration and organizing ideas. Available on web, iOS, and Android, it lets you save images, links, and notes into boards. Personal collections are private by default, with public boards available for sharing.","featureList":["Private visual feed without ads","Save images, links, quotes, notes, and ideas","Organize inspiration in folders, boards, sections, and subsections","Explore public inspiration boards","Filter inspiration by color, material, aesthetic, subject, and mood","Hide community-flagged AI content","Share public boards","Import public Pinterest boards, with Chrome extension saving for private boards"]}]}</script>
778
779</head>
780
781<body>
782  <style>
783    #echo-native-launch { display: none; }
784    html[data-native-bootstrap] #echo-native-launch {
785      position: fixed; inset: 0; z-index: 9999; display: grid;
786      place-items: center; background: #fcf6f5; pointer-events: none;
787    }
788    #echo-native-launch img { display: block; width: 84px; height: 84px; }
789    @media (prefers-color-scheme: dark) {
790      html[data-native-bootstrap] #echo-native-launch { background: #0a090d; }
791    }
792  </style>
793  <div id="echo-native-launch" role="status" aria-label="Opening Echo" style="display:none;position:fixed;inset:0;z-index:9999;place-items:center;pointer-events:none;background:#fcf6f5">
794    <img id="echo-native-launch-logo" width="84" height="84" alt="" fetchpriority="high" />
795  </div>
796  
796<script>
797    if (document.documentElement.hasAttribute('data-native-bootstrap')) {
798      // Keep the cover hidden until its geometry is usable. WebKit can expose
799      // the parsed image before the preceding style block has been applied.
800      var nativeLaunchCover = document.getElementById('echo-native-launch');
801      nativeLaunchCover.style.backgroundColor = window.matchMedia('(prefers-color-scheme: dark)').matches ? '#0a090d' : '#fcf6f5';
802      var launchLogo = document.getElementById('echo-native-launch-logo');
803      launchLogo.srcset = '/logo-168.png 2x, /logo-252.png 3x';
804      launchLogo.src = '/logo-168.png';
805      nativeLaunchCover.style.display = 'grid';
806    }
807  </script>
807
808  <!-- Full theme application after DOM is available -->
809  
809<script>
810    (function () {
811      if (!window.__ECHO_PUSH_BOOT_LOG__) return;
812
813      window.__ECHO_PUSH_BOOT_LOG__('body-ready');
814
815      document.addEventListener('DOMContentLoaded', function () {
816        window.__ECHO_PUSH_BOOT_LOG__('dom-content-loaded');
817      }, { once: true });
818
819      window.addEventListener('load', function () {
820        window.__ECHO_PUSH_BOOT_LOG__('window-load');
821      }, { once: true });
822    })();
823  </script>
823
824  
824<script>
825    (function () {
826      try {
827        // Helper: Convert hex to HSL
828        function hexToHsl(hex) {
829          hex = hex.replace(/^#/, '');
830          var r = parseInt(hex.substring(0, 2), 16) / 255;
831          var g = parseInt(hex.substring(2, 4), 16) / 255;
832          var b = parseInt(hex.substring(4, 6), 16) / 255;
833          var max = Math.max(r, g, b), min = Math.min(r, g, b);
834          var h = 0, s, l = (max + min) / 2;
835          if (max !== min) {
836            var d = max - min;
837            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
838            switch (max) {
839              case r: h = (g - b) / d + (g < b ? 6 : 0); break;
840              case g: h = (b - r) / d + 2; break;
841              case b: h = (r - g) / d + 4; break;
842            }
843            h /= 6;
844          } else { s = 0; }
845          return Math.round(h * 360) + ' ' + Math.round(s * 100) + '% ' + Math.round(l * 100) + '%';
846        }
847
848        // Helper: Check if color is dark
849        function isDarkColor(hex) {
850          hex = hex.replace(/^#/, '');
851          var r = parseInt(hex.substring(0, 2), 16);
852          var g = parseInt(hex.substring(2, 4), 16);
853          var b = parseInt(hex.substring(4, 6), 16);
854          return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 < 0.5;
855        }
856
857        // Apply cached theme (priority: ThemeContext > MoodContext)
858        var cached = localStorage.getItem('echo_theme_cache') || localStorage.getItem('echo_mood_theme');
859        if (cached) {
860          var theme = JSON.parse(cached);
861          var root = document.documentElement;
862
863          // Colors - Apply as CSS variables for Tailwind
864          if (theme.primary_color) {
865            var primaryHsl = hexToHsl(theme.primary_color);
866            root.style.setProperty('--primary', primaryHsl);
867            root.style.setProperty('--ring', primaryHsl);
868            // CRITICAL: Set border to match primary for themed borders
869            root.style.setProperty('--border', primaryHsl);
870            root.style.setProperty('--border-hsl', primaryHsl);
871
872            // Primary foreground based on primary color darkness
873            // Same rule as Themes.tsx getReadableTextColor so editor and app agree
874            var isPrimaryDark = (function(h){h=String(h||'').replace(/^#/,'');var b=(parseInt(h.slice(0,2),16)*299+parseInt(h.slice(2,4),16)*587+parseInt(h.slice(4,6),16)*114)/1000;return !(b>160);})(theme.primary_color);
875            root.style.setProperty('--primary-foreground', isPrimaryDark ? '0 0% 100%' : '222.2 47.4% 11.2%');
876          }
877          if (theme.canvas_tone) {
878            root.style.setProperty('--background', hexToHsl(theme.canvas_tone));
879            // Also set direct style for immediate effect
880            root.style.backgroundColor = theme.canvas_tone;
881            document.body.style.backgroundColor = theme.canvas_tone;
882
883            // CRITICAL: Set foreground/text colors based on background darkness
884            var isBgDark = isDarkColor(theme.canvas_tone);
885            if (isBgDark) {
886              root.style.setProperty('--foreground', '0 0% 98%');
887              root.style.setProperty('--muted-foreground', '0 0% 70%');
888              root.style.setProperty('--card-foreground', '210 40% 98%');
889              // Extract hue from primary for muted color
890              if (theme.primary_color) {
891                var hue = hexToHsl(theme.primary_color).split(' ')[0];
892                root.style.setProperty('--muted', hue + ' 15% 20%');
893              }
894            } else {
895              root.style.setProperty('--foreground', '222.2 84% 4.9%');
896              root.style.setProperty('--muted-foreground', '215.4 16.3% 46.9%');
897              root.style.setProperty('--card-foreground', '222.2 84% 4.9%');
898              root.style.setProperty('--muted', '210 40% 96.1%');
899            }
900          }
901          if (theme.surface_color) {
902            var surfaceHsl = hexToHsl(theme.surface_color);
903            // For dark themes, ensure surface is dark even if theme.surface_color is light
904            var isBgDark = theme.canvas_tone ? isDarkColor(theme.canvas_tone) : false;
905            var useSurface = isBgDark && !isDarkColor(theme.surface_color) ? '240 10% 8%' : surfaceHsl;
906            root.style.setProperty('--card', useSurface);
907            root.style.setProperty('--surface', useSurface);
908            root.style.setProperty('--popover', useSurface);
909          }
910          if (theme.accent_color) {
911            root.style.setProperty('--accent', hexToHsl(theme.accent_color));
912          }
913
914          // Font - Mood always controls font, even if theme is active
915          var moodCached = localStorage.getItem('echo_mood_theme');
916          var fontToApply = localStorage.getItem('echo_last_font_family');
917
918          if (moodCached) {
919            try {
920              var moodTheme = JSON.parse(moodCached);
921              if (moodTheme.font_family) fontToApply = moodTheme.font_family;
922            } catch (e) { }
923          }
924
925          if (fontToApply) {
926            var fontStackToApply = fontToApply === 'Manrope' ? '"Manrope", "Manrope Fallback", sans-serif' : '"' + fontToApply + '", sans-serif';
927            root.style.setProperty('--font-mood', fontStackToApply, 'important');
928
929            // Nuclear style injection for head-start
930            var existingStyle = document.getElementById('echo-font-overrider-pre');
931            if (!existingStyle) {
932              var style = document.createElement('style');
933              style.id = 'echo-font-overrider-pre';
934              style.textContent = 'html, body, button, input, select, textarea { font-family: var(--font-mood, ' + fontStackToApply + ') !important; }';
935              document.head.appendChild(style);
936            }
937          }
938
939          // Glass & Material
940          if (theme.glass_blur !== undefined) {
941            root.style.setProperty('--glass-blur', theme.glass_blur + 'px');
942          }
943          if (theme.glass_transparency !== undefined) {
944            root.style.setProperty('--glass-transparency', theme.glass_transparency + '%');
945          }
946          if (theme.corner_radius !== undefined) {
947            root.style.setProperty('--card-radius', theme.corner_radius + 'px');
948          }
949          if (theme.shadow_depth !== undefined) {
950            root.style.setProperty('--card-shadow-depth', theme.shadow_depth + 'px');
951          }
952          if (theme.border_weight !== undefined) {
953            root.style.setProperty('--card-border-weight', theme.border_weight + 'px');
954          }
955
956          // Dark mode - Ensure it's applied
957          var isBgDark = theme.canvas_tone ? isDarkColor(theme.canvas_tone) : false;
958          if (theme.is_dark || isBgDark) {
959            root.classList.add('dark');
960          }
961
962          // Starry Night: Instant Native Generation
963          var isNativeShell = root.hasAttribute('data-native-bootstrap');
964          var isStarry = !isNativeShell && (
965            (theme.background_effects && theme.background_effects.includes('blinking_stars')) ||
966            theme.background_effect === 'blinking_stars'
967          );
968          var container = document.getElementById('star-container');
969
970          if (isStarry) {
971            root.classList.add('starry-active');
972            var starColor = (theme.is_dark || isBgDark) ? '#ffffff' : '#444444';
973            root.style.setProperty('--star-color', starColor);
974            // React's deferred StarryBackground owns decorative DOM after the
975            // first route is usable. Avoid creating 150 animated nodes in the
976            // synchronous body bootstrap.
977          } else {
978            root.classList.remove('starry-active');
979            if (container) container.innerHTML = '';
980          }
981        }
982      } catch (e) { console.warn('Theme preload failed:', e); }
983    })();
984  </script>
984
985  <div id="root" data-echo-prerender="landing"><div class="relative min-h-screen overflow-x-hidden bg-[#030303] text-white"><nav aria-hidden="false" class="fixed inset-x-0 top-4 z-50 px-4 transition-all duration-500 ease-out translate-y-0 opacity-100"><div class="mx-auto flex w-full max-w-5xl items-center justify-between gap-3 rounded-[1.75rem] px-4 py-3 sm:px-5 isolate border border-white/10 bg-[linear-gradient(180deg,rgba(12,15,23,0.92),rgba(7,9,15,0.88))] shadow-[0_16px_50px_rgba(0,0,0,0.30)] backdrop-blur-xl [backface-visibility:hidden] [contain:paint] [transform:translateZ(0)]"><div class="flex items-center gap-2 sm:gap-3"><a class="flex items-center gap-3 text-left transition-opacity hover:opacity-90" href="/" data-discover="true"><div class="h-[3.15rem] w-[3.15rem] overflow-hidden sm:h-[3.4rem] sm:w-[3.4rem]"><img src="/logo-128.png" width="128" height="125" alt="Echo logo" class="h-full w-full object-cover"/></div><div class="hidden sm:block"><p class="text-base font-semibold tracking-tight text-white">Echo</p><p class="text-[10px] uppercase tracking-[0.22em] text-white/70">Keep what inspires you.</p></div></a><a class="items-center justify-center gap-2 whitespace-nowrap transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 h-10 py-2 rounded-full bg-rose-600 px-4 text-sm font-semibold text-white shadow-[0_16px_34px_rgba(244,63,94,0.22)] hover:bg-rose-700 sm:px-5 hidden sm:inline-flex" href="/community" data-discover="true"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users mr-2 h-4 w-4"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>Community</a></div><div class="flex items-center gap-2"><div class="hidden items-center gap-2 lg:flex" role="group" aria-label="Get the Echo browser extension"><a href="https://chromewebstore.google.com/detail/echo-save-to-your-feed/bafdokbhgelomohejghkkhgmmecfpbpi" target="_blank" rel="noopener noreferrer" aria-label="Get Echo extension for Chrome" class="inline-flex h-10 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-3 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-500 focus-visible:ring-offset-2 bg-white text-black hover:bg-white/90"><img src="/landing/chrome-web-store-logo.webp" alt="" class="h-5 w-5 shrink-0" decoding="async"/>Chrome</a><a href="https://addons.mozilla.org/en-US/firefox/addon/echo-save-to-your-feed/" target="_blank" rel="noopener noreferrer" aria-label="Get Echo extension for Firefox" class="inline-flex h-10 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-3 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-500 focus-visible:ring-offset-2 bg-white text-black hover:bg-white/90"><img src="/landing/firefox-browser-logo.svg" alt="" class="h-5 w-5 shrink-0" decoding="async"/>Firefox</a></div><a class="items-center justify-center gap-2 whitespace-nowrap transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 bg-transparent h-10 py-2 hidden rounded-full px-4 text-sm font-medium sm:inline-flex text-white hover:bg-white/10 hover:text-white" href="/auth" data-discover="true">Log in</a><a class="inline-flex items-center justify-center gap-2 whitespace-nowrap transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 h-10 py-2 rounded-full bg-rose-600 font-semibold text-white shadow-[0_16px_34px_rgba(244,63,94,0.22)] hover:bg-rose-700 px-3 text-xs sm:px-5 sm:text-sm" href="/auth?mode=signup" data-discover="true">Create your feed</a></div></div></nav><main class="relative z-10 overflow-hidden pb-0"><section class="mx-auto max-w-none px-0 pt-0"><div class="relative overflow-hidden bg-[#030303] px-0 pb-0"><div class="absolute inset-0 bg-[radial-gradient(circle_at_50%_30%,rgba(255,255,255,0.12)_0%,rgba(255,255,255,0.04)_34%,rgba(3,3,3,0)_66%)]"></div><div class="relative z-20 mx-auto flex min-h-[calc(100svh-2rem)] max-w-[122rem] flex-col justify-end px-5 pb-0 pt-36 text-center sm:px-8 sm:pt-40 lg:px-10 lg:pt-44"><div class="mx-auto w-full max-w-[94rem]"><p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-white/58 sm:text-xs">An ad-free Pinterest alternative</p><h1 class="mx-auto mt-7 max-w-[12ch] text-[clamp(3.2rem,7.5vw,7.5rem)] font-semibold leading-[0.95] tracking-tight text-white">Keep what inspires you.</h1><p class="mx-auto mt-7 max-w-[50rem] text-base leading-7 text-white/76 sm:text-xl sm:leading-8">Discover inspiration, organize your ideas, and bring your Pinterest boards with you. Private by default. Always room for your next idea.<
985/p><div class="mt-10 flex flex-col items-center justify-center gap-3 sm:flex-row"><a class="inline-flex items-center justify-center gap-2 whitespace-nowrap transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 h-[3.25rem] rounded-full bg-white px-7 text-sm font-semibold text-black shadow-[0_18px_40px_rgba(255,255,255,0.12)] hover:bg-white/90 sm:h-14 sm:px-8 sm:text-base" href="/auth?mode=signup" data-discover="true">Create your first board<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-right ml-2 h-4 w-4"><path d="M5 12h14"></path><path d="m12 5 7 7-7 7"></path></svg></a><a href="#trending-boards" class="inline-flex items-center justify-center gap-2 whitespace-nowrap transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 h-[3.25rem] rounded-full bg-primary px-7 text-sm font-semibold text-primary-foreground shadow-[0_18px_40px_hsl(var(--primary)/0.24)] hover:bg-primary/90 sm:h-14 sm:px-8 sm:text-base">See what&#x27;s trending</a></div><p class="mt-6 text-sm leading-6 text-white/65">Already have a collection?<!-- --> <a class="inline-flex min-h-11 items-center text-white underline decoration-white/40 underline-offset-4 hover:decoration-white" href="/import-pinterest-boards" data-discover="true">Bring your Pinterest boards<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-right ml-1.5 h-3.5 w-3.5" aria-hidden="true"><path d="M5 12h14"></path><path d="m12 5 7 7-7 7"></path></svg></a></p><div class="mx-auto mt-6 flex flex-col items-center justify-center gap-3 sm:flex-row sm:flex-wrap"><a href="https://apps.apple.com/app/echo-your-feed/id6760357340" target="_blank" rel="noreferrer" aria-label="Download Echo on the App Store" class="inline-flex transition-opacity hover:opacity-82"><img src="/landing/app-store-badge-320.png" alt="Download on the App Store" width="320" height="111" class="h-12 w-auto max-w-[14rem] sm:h-14 sm:max-w-[16rem]" loading="lazy" decoding="async"/></a><a href="https://play.google.com/store/apps/details?id=app.myechoboard.echo" target="_blank" rel="noreferrer" aria-label="Get Echo on Google Play" class="inline-flex transition-opacity hover:opacity-82"><img src="/landing/google-play-store-badge-320.png" alt="Get it on Google Play" width="320" height="111" class="h-12 w-auto max-w-[14rem] sm:h-14 sm:max-w-[16rem]" loading="lazy" decoding="async"/></a></div></div><div class="pointer-events-none relative z-10 mt-14 overflow-hidden px-0 sm:mt-16 lg:mt-20"><div class="h-[390px] overflow-hidden sm:h-[540px] lg:h-[620px] xl:h-[700px]"><div class="flex h-full items-end gap-3 sm:gap-4 lg:gap-5 w-max animate-hero-rail-marquee-reverse pr-3 sm:pr-4 lg:pr-5"><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rounded-[1.65rem] h-[74%] opacity-75"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="lazy" fetchpriority="auto" decoding="async" class="h-full w-full object-cover"/>
985</div><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rounded-[1.65rem] h-[86%] opacity-95"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820412589-5dda8170-6512-4176-a78c-5e0d3d796d79.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820412589-5dda8170-6512-4176-a78c-5e0d3d796d79.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820412589-5dda8170-6512-4176-a78c-5e0d3d796d79.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820412589-5dda8170-6512-4176-a78c-5e0d3d796d79.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="lazy" fetchpriority="auto" decoding="async" class="h-full w-full object-cover"/></div><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rounded-[1.65rem] h-full"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="eager" fetchpriority="high" decoding="async" class="h-full w-full object-cover"/></div><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rounded-[1.65rem] h-[86%] opacity-95"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820022450-47cf14e8-f3c8-4c17-829a-2437fe108cfe.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820022450-47cf14e8-f3c8-4c17-829a-2437fe108cfe.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820022450-47cf14e8-f3c8-4c17-829a-2437fe108cfe.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820022450-47cf14e8-f3c8-4c17-829a-2437fe108cfe.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="lazy" fetchpriority="auto" decoding="async" class="h-full w-full object-cover"/></div><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rounded-[1.65rem] h-[74%] opacity-75"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770819586119-d7e09fd8-7d8f-4106-8022-547f0d6c2509.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770819586119-d7e09fd8-7d8f-4106-8022-547f0d6c2509.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770819586119-d7e09fd8-7d8f-4106-8022-547f0d6c2509.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770819586119-d7e09fd8-7d8f-4106-8022-547f0d6c2509.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="lazy" fetchpriority="auto" decoding="async" class="h-full w-full object-cover"/></div><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rou
985nded-[1.65rem] h-[74%] opacity-75"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="lazy" fetchpriority="auto" decoding="async" class="h-full w-full object-cover"/></div><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rounded-[1.65rem] h-[86%] opacity-95"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820412589-5dda8170-6512-4176-a78c-5e0d3d796d79.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820412589-5dda8170-6512-4176-a78c-5e0d3d796d79.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820412589-5dda8170-6512-4176-a78c-5e0d3d796d79.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820412589-5dda8170-6512-4176-a78c-5e0d3d796d79.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="lazy" fetchpriority="auto" decoding="async" class="h-full w-full object-cover"/></div><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rounded-[1.65rem] h-full"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820854971-4ca7e03e-c001-4579-b5b0-f17d0d15d942.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="lazy" fetchpriority="auto" decoding="async" class="h-full w-full object-cover"/></div><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rounded-[1.65rem] h-[86%] opacity-95"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820022450-47cf14e8-f3c8-4c17-829a-2437fe108cfe.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820022450-47cf14e8-f3c8-4c17-829a-2437fe108cfe.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820022450-47cf14e8-f3c8-4c17-829a-2437fe108cfe.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770820022450-47cf14e8-f3c8-4c17-829a-2437fe108cfe.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="lazy" fetchpriority="auto" decoding="async" class="h-full w-full object-cover"/></div><div style="aspect-ratio:0.78" class="relative shrink-0 overflow-hidden rounded-[1.25rem] shadow-[0_30px_80px_rgba(0,0,0,0.56)] sm:rounded-[1.65rem] h-[74%] opacity-75"><img src="https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770819586119-d7e09fd8-7d8f-4106-8022-547f0d6c2509.jpg" srcSet="https://cdn.myechoboard.com/cdn-cgi/image/width=400,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770819586119-d7e09fd8-7d8f-4106-8022-547f0d6c2509.jpg 400w, https://cdn.myechoboard.com/cdn-cgi/image/width=800,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770819586119-d7e09fd8-7d8f-4106-8022-547f0d6c2509.jpg 800w, https://cdn.myechoboard.com/cdn-cgi/image/width=1200,quality=80,format=auto/74c3ce13-29a1-4e45-ac73-d73b5b26718b/1770819586119-d7e09fd8-7d8f-4106-8022-547f0d6c2509.jpg 1200w" sizes="(max-width: 640px) 72vw, 340px" alt="" loading="lazy" fetchpriority="auto" decoding="async" class="h-full w-full object-cover"/>
985</div></div></div></div></div></div></section><div class="relative z-10"><section class="relative bg-[#030303] text-white border-t border-white/10"><div class="mx-auto w-full max-w-7xl px-5 py-24 sm:px-6 lg:px-8 lg:py-28"><div class="mx-auto max-w-6xl text-center"><p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-white/52">Curated by Echo</p><h2 class="mx-auto mt-7 max-w-[17ch] text-[clamp(2.8rem,7vw,6.8rem)] font-semibold leading-[0.9] text-white">Boards worth<!-- --> <span class="bg-white/10 px-2 font-serif italic text-white/72">exploring.</span></h2><p class="mx-auto mt-7 max-w-3xl text-base leading-8 text-white/64 sm:text-lg">A hand-picked set of public boards from Echo to start exploring visual ideas.</p></div><div class="mt-14 grid gap-x-4 gap-y-8 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5"><a class="group block text-left" href="/u/sebbe/beauty" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Beauty preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">B</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Beauty</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/food-drinks" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Food &amp; Drinks preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">F</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Food &amp; Drinks</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/interior-design" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Interior Design preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">I</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Interior Design</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/stars" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Stars preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">S</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Stars</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/sunrise-x-sunset" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Sunrise x Sunset preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">S</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Sunrise x Sunset</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/sketch-ideas" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Sketch Ideas preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">S</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Sketch Ideas</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/garden-outdoors" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Garden &amp; Outdoors preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">G</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Garden &amp; Outdoors</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/cozy-places" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Cozy Places preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">C</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Cozy Places</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/aesthetics" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Aesthetics preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">A</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Aesthetics</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/best-of-echo" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden animate-pulse" role="status" aria-label="Loading Best of Echo preview"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">B</div><div class="absolute inset-x-6 bottom-6 h-2 rounded-full bg-white/10"></div></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Best of Echo</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a></div></div></section><section id="trending-boards" class="relative bg-[#030303] text-white border-t border-white/10"><div class="mx-auto w-full max-w-7xl px-5 py-24 sm:px-6 lg:px-8 lg:py-28"><div class="mx-auto max-w-6xl text-center"><p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-white/52">Trending Boards</p><h2 class="mx-auto mt-7 max-w-[17ch] text-[clamp(2.8rem,7vw,6.8rem)] font-semibold leading-[0.9] text-white">What people are<!-- --> <span class="bg-white/10 px-2 font-serif italic text-white/72">saving now.</span></h2><p class="mx-auto mt-7 max-w-3xl text-base leading-8 text-white/64 sm:text-lg">A live look at public boards gathering attention across Echo.</p></div><div class="mt-14 grid gap-x-4 gap-y-8 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5"><a class="group block text-left" href="/u/sebbe/beauty" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden" role="img" aria-label="Beauty preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">B</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Beauty</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/food-drinks" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden" role="img" aria-label="Food &amp; Drinks preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">F</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Food &amp; Drinks</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/interior-design" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-fu
985ll overflow-hidden" role="img" aria-label="Interior Design preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">I</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Interior Design</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/stars" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden" role="img" aria-label="Stars preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">S</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Stars</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/sunrise-x-sunset" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden" role="img" aria-label="Sunrise x Sunset preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">S</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Sunrise x Sunset</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/sketch-ideas" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden" role="img" aria-label="Sketch Ideas preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">S</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Sketch Ideas</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/garden-outdoors" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden" role="img" aria-label="Garden &amp; Outdoors preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">G</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Garden &amp; Outdoors</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/cozy-places" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden" role="img" aria-label="Cozy Places preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">C</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Cozy Places</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/aesthetics" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden" role="img" aria-label="Aesthetics preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">A</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Aesthetics</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a><a class="group block text-left" href="/u/sebbe/best-of-echo" data-discover="true"><div class="overflow-hidden rounded-[1rem] bg-white/[0.04]"><div class="aspect-[1.05] bg-white/[0.04]"><div class="relative col-span-2 row-span-2 h-full overflow-hidden" role="img" aria-label="Best of Echo preview unavailable"><div aria-hidden="true" class="absolute inset-0 grid place-items-center bg-[radial-gradient(circle_at_28%_22%,rgba(255,255,255,0.13),transparent_34%),linear-gradient(145deg,rgba(239,68,68,0.28),rgba(124,58,237,0.22)_55%,rgba(15,23,42,0.9))] text-4xl font-semibold text-white/70">B</div><span class="absolute inset-x-3 bottom-4 text-center text-[11px] font-medium uppercase tracking-[0.14em] text-white/48">Preview unavailable</span></div></div></div><div class="mt-3 flex items-start gap-2.5"><span class="relative flex shrink-0 overflow-hidden rounded-full h-7 w-7 bg-white/10"><span class="flex h-full w-full items-center justify-center rounded-full bg-white/10 text-[11px] font-semibold text-white/64">S</span></span><div class="min-w-0"><p class="truncate text-sm font-semibold leading-tight text-white transition-colors group-hover:text-white/82">Best of Echo</p><p class="mt-1 truncate text-xs text-white/54">@<!-- -->sebbe</p></div></div></a></div></div></section><section class="relative bg-[#030303] text-white border-t border-white/10"><div class="mx-auto flex w-full max-w-5xl flex-col items-center px-5 py-24 text-center sm:px-6 lg:px-8 lg:py-28"><p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-white/52">Start Your Space</p><h2 class="mt-7 max-w-[12ch] text-[clamp(3.2rem,8vw,7rem)] font-semibold leading-[0.88] text-white">Create your<!-- --> <span class="bg-white/10 px-2 font-serif italic text-white/72">feed.</span></h2><p class="mx-auto mt-7 max-w-2xl text-base leading-8 text-white/66 sm:text-lg">Build a private visual home for the references, ideas, and fragments you keep coming back to.</p><div class="mt-10 flex flex-col items-center justify-center gap-3 sm:flex-row"><a class="inline-flex items-center justify-center gap-2 whitespace-nowrap transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 h-[3.25rem] rounded-full bg-white px-7 text-sm font-semibold text-black shadow-[0_18px_40px_rgba(255,255,255,0.12)] hover:bg-white/90 sm:h-14 sm:px-8 sm:text-base" href="/auth?mode=signup" data-discover="true">Create your feed<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-right ml-2 h-4 w-4"><path d="M5 12h14"></path><path d="m12 5 7 7-7 7"></path></svg></a></div></div></section><section class="relative bg-[#030303] text-white border-t border-white/10"><div class="mx-auto w-full max-w-7xl px-5 py-24 sm:px-6 lg:px-8 lg:py-28"><div class="mx-auto max-w-5xl text-center"><p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-white/52">Our Top 10 Features</p><h2 class="mt-7 text-[clamp(3rem,7vw,6.8rem)] font-semibold leading-[0.9] text-white">What users love about Echo</h2></div><ol class="mx-auto mt-16 max-w-5xl divide-y divide-white/10 border-y border-white/10"><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">01</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl">No ads</span></li><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">02</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl">Your own private feed</span></li><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">03</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl">Filters for everything and anything: colors, materials, aesthetics, objects, subjects, humans, mood, and aspect ratio</span></li><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">04</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl">Customizable themes</span></li><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">05</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl">Customizable feed design</span></li><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">06</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl">Organize ideas in folders, boards, sections, and infinite subsections</span></li><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">07</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl">Save images, links, quotes, notes, and ideas in one visual space</span></li><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">08</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl">Create public boards when you want to share</span></li><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">09</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl">Rediscover old saves when they become useful again</span></li><li class="grid gap-4 py-5 sm:grid-cols-[4rem_1fr] sm:items-center sm:py-6"><span class="font-serif text-3xl italic leading-none text-white/36 sm:text-4xl">10</span><span class="text-xl font-semibold leading-tight text-white sm:text-2xl"><span class="inline"><span class="inline-flex items-baseline gap-2.5 align-baseline"><span class="inline-flex h-[1.35em] w-[1.35em] translate-y-[0.23em] items-center justify-center overflow-hidden"><img src="/logo-128.png" width="128" height="125" alt="Echo logo" loading="lazy" decoding="async" class="h-full w-full object-cover"/>
985</span><span>Echo</span></span> <!-- -->is built on <strong class="font-black">your feedback</strong>. Just email<!-- --> <a href="/cdn-cgi/l/email-protection#431026212230372a222d032e3a26202b2c212c2231276d202c2e" class="text-[#ff6f86] underline decoration-current/50 underline-offset-4 transition-colors hover:text-[#ff91a2]"><span class="__cf_email__" data-cfemail="663503040715120f0708260b1f03050e0904090714024805090b">[email&#160;protected]</span></a> <!-- -->if you have requests, ideas, issues, or just want to say hi!</span></span></li></ol></div></section><section aria-labelledby="landing-questions" class="border-t border-white/10 bg-[#030303] text-white"><div class="mx-auto grid max-w-7xl gap-12 px-5 py-24 sm:px-6 lg:grid-cols-[0.8fr_1.2fr] lg:gap-20 lg:px-8 lg:py-28"><div><p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-white/52">A little more about Echo</p><h2 id="landing-questions" class="mt-7 text-4xl font-semibold leading-tight tracking-tight sm:text-5xl">Your inspiration.<br/><span class="font-serif italic text-white/72">Your way.</span></h2><p class="mt-6 max-w-sm text-base leading-7 text-white/66">For designers collecting references, creatives building moodboards, and anyone planning a home, a trip, or their next idea.</p><a class="mt-7 inline-flex min-h-11 items-center text-sm text-white underline decoration-white/40 underline-offset-4 hover:decoration-white" href="/about" data-discover="true">Meet Echo</a></div><div class="divide-y divide-white/10 [&amp;_a]:underline [&amp;_a]:decoration-white/40 [&amp;_a]:underline-offset-4 hover:[&amp;_a]:decoration-white"><div class="pb-7"><h3 class="text-lg font-semibold">What is Echo?</h3><p class="mt-3 text-base leading-7 text-white/70">Echo is an ad-free Pinterest alternative for discovering inspiration and organizing ideas. Available on web, iOS, and Android, it lets you save images, links, and notes into boards. Personal collections are private by default, with public boards available for sharing.</p></div><div class="py-7"><h3 class="text-lg font-semibold">Can I use Echo as a Pinterest alternative?</h3><p class="mt-3 text-base leading-7 text-white/70">Yes. Echo combines public inspiration with a private-by-default library, without ads. Organize saves in folders, boards, sections, and nested subsections. You can <a href="/import-pinterest-boards" data-discover="true">import Pinterest boards</a> from a public board URL, or use the Echo Chrome extension for private boards and browser saving. See the <a href="/blog/pinterest-vs-echo" data-discover="true">Pinterest and Echo comparison</a> to choose what fits your workflow.</p></div><div class="py-7"><h3 class="text-lg font-semibold">How do I find and organize inspiration?</h3><p class="mt-3 text-base leading-7 text-white/70">Explore public boards and filter visual inspiration by color, material, aesthetic, object, subject, mood, and aspect ratio. AI-assisted image metadata helps make these filters searchable. Collect references for a project in your own boards, or explore an <a href="/u/sebbe/interior-design" data-discover="true">interior design board</a> to see how a collection looks.</p></div><div class="py-7"><h3 class="text-lg font-semibold">Can I hide AI-generated content?</h3><p class="mt-3 text-base leading-7 text-white/70">Echo lets you hide community-flagged AI content in Content Preferences. The filter uses the majority of community votes, and content you personally mark as AI is hidden for you immediately. It does not guarantee that every AI-generated image is identified.</p></div><div class="py-7"><h3 class="text-lg font-semibold">Are my boards private?</h3><p class="mt-3 text-base leading-7 text-white/70">Your personal feed is private. Keep your inspiration to yourself, or create public boards when you want to share. Read Echo’s <a href="/privacy" data-discover="true">privacy policy</a> for details about how your data is handled.</p></div><div class="pt-7"><h3 class="text-lg font-semibold">Can I use Echo on my phone?</h3><p class="mt-3 text-base leading-7 text-white/70">Echo is available on the web, iOS, and Android. Use the App Store and Google Play links on this page to get the mobile app. For help or product feedback, <a href="/support" data-discover="true">contact Echo support</a>.</p></div></div></div></section></div></main><footer class="relative z-10 border-t border-white/10 bg-[linear-gradient(180deg,rgba(12,15,23,0.96)_0%,rgba(7,9,15,0.98)_100%)] text-white"><div class="mx-auto grid w-full max-w-7xl gap-x-8 gap-y-12 px-5 py-16 text-sm text-white sm:px-6 md:grid-cols-2 lg:grid-cols-[1.5fr_0.8fr_0.95fr_0.8fr_0.9fr] lg:px-8 [&amp;_a]:inline-flex [&amp;_a]:min-h-6 [&amp;_a]:items-center"><div class="lg:pr-10"><p class="text-base font-semibold tracking-tight text-white">
985Echo</p><p class="mt-3 max-w-md text-[15px] leading-7 text-white/88">Keep what inspires you. An ad-free Pinterest alternative for discovering and organizing ideas.</p><p class="mt-6 max-w-sm whitespace-pre-line text-sm leading-6 text-white/80">🇸🇪
986Designed &amp; Engineered in Ljungby, Sweden</p></div><div><p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-white/70">Company</p><div class="mt-5 flex flex-col gap-3.5"><a class="transition-colors hover:text-white" href="/about" data-discover="true">About</a><a class="transition-colors hover:text-white" href="/news" data-discover="true">News</a><a class="transition-colors hover:text-white" href="/community" data-discover="true">Community</a><a class="transition-colors hover:text-white" href="/ai" data-discover="true">AI Index</a><a class="transition-colors hover:text-white" href="/privacy" data-discover="true">Privacy</a><a class="transition-colors hover:text-white" href="/terms" data-discover="true">Terms</a><a class="transition-colors hover:text-white" href="/support" data-discover="true">Support</a></div></div><div><p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-white/70">Compare</p><div class="mt-5 flex flex-col gap-3.5"><a class="transition-colors hover:text-white" href="/blog" data-discover="true">Journal &amp; guides</a><a class="transition-colors hover:text-white" href="/alternatives/arena" data-discover="true">Are.na Alternatives</a><a class="transition-colors hover:text-white" href="/alternatives/cosmos" data-discover="true">Cosmos Alternatives</a><a class="transition-colors hover:text-white" href="/alternatives/eagle" data-discover="true">Eagle Alternatives</a><a class="transition-colors hover:text-white" href="/alternatives/raindrop" data-discover="true">Raindrop.io Alternatives</a><a class="transition-colors hover:text-white" href="/alternatives/pinterest" data-discover="true">Pinterest Alternatives</a></div></div><div><p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-white/70">Get Started</p><div class="mt-5 flex flex-col gap-3.5"><a class="font-medium text-white transition-colors hover:text-white" href="/auth?mode=signup" data-discover="true">Create account</a><a class="transition-colors hover:text-white" href="/auth" data-discover="true">Log in</a><a class="transition-colors hover:text-white" href="/explore" data-discover="true">Explore</a></div></div></div></footer></div></div>
vendor: 111 bytes, lines 986-987
986
987  <script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
987<script>
988    (function () {
989      var cover = document.getElementById('echo-native-launch');
990      if (!document.documentElement.hasAttribute('data-native-bootstrap')) {
991        cover.remove();
992        return;
993      }
994      performance.mark('echo-startup:document-ready');
995      var root = document.getElementById('root');
996      var observer = new MutationObserver(function () {
997        if (!root.childElementCount) return;
998        cover.remove();
999        observer.disconnect();
1000      });
1001      observer.observe(root, { childList: true });
1002    })();
1003  </script>
1003
1004  <noscript>
1005    <div class="echo-boot-recovery">
1006      <div class="echo-boot-recovery__card">
1007        <h1>Echo needs JavaScript</h1>
1008        <p>Enable JavaScript for myechoboard.com, then reload this page.</p>
1009      </div>
1010    </div>
1011  </noscript>
1012  
1012<script>
1013    (function () {
1014      if (document.documentElement.hasAttribute('data-native-bootstrap')) return;
1015      if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' || window.location.hostname === '[::1]') return;
1016
1017      var recoveryShown = false;
1018      var reloadStarted = false;
1019      var watchdogTimer = 0;
1020
1021      function recoveryAttempt() {
1022        try {
1023          var url = new URL(window.location.href);
1024          var rawAttempt = url.searchParams.get('_ra');
1025          if (rawAttempt !== null) {
1026            var parsedAttempt = Number(rawAttempt);
1027            if (Number.isSafeInteger(parsedAttempt) && parsedAttempt >= 0) {
1028              return Math.min(parsedAttempt, 99);
1029            }
1030          }
1031          return url.searchParams.has('_r') ? 1 : 0;
1032        } catch (error) {
1033          return 0;
1034        }
1035      }
1036
1037      function clearStaleAppCaches() {
1038        var unregister = Promise.resolve();
1039        var clearCaches = Promise.resolve();
1040
1041        try {
1042          if ('serviceWorker' in navigator) {
1043            unregister = navigator.serviceWorker.getRegistrations().then(function (registrations) {
1044              return Promise.all(registrations.map(function (registration) {
1045                return registration.unregister();
1046              }));
1047            }).catch(function () {});
1048          }
1049        } catch (error) {}
1050
1051        try {
1052          if ('caches' in window) {
1053            clearCaches = window.caches.keys().then(function (cacheNames) {
1054              return Promise.all(cacheNames
1055                .filter(function (cacheName) {
1056                  return cacheName.indexOf('workbox-precache') === 0 || cacheName.indexOf('echo-feature-') === 0;
1057                })
1058                .map(function (cacheName) { return window.caches.delete(cacheName); }));
1059            }).catch(function () {});
1060          }
1061        } catch (error) {}
1062
1063        var cleanup = Promise.all([unregister, clearCaches]);
1064        var timeout = new Promise(function (resolve) { window.setTimeout(resolve, 2000); });
1065        return Promise.race([cleanup, timeout]);
1066      }
1067
1068      function reloadLatest() {
1069        if (reloadStarted || document.documentElement.hasAttribute('data-echo-reload-started')) {
1070          return Promise.resolve();
1071        }
1072        reloadStarted = true;
1073        document.documentElement.setAttribute('data-echo-reload-started', 'true');
1074        var attempt = recoveryAttempt();
1075        var retryDelay = 0;
1076        var url = new URL(window.location.href);
1077        url.searchParams.set('_cb', String(Date.now()));
1078        url.searchParams.set('_r', 'boot-watchdog');
1079        url.searchParams.set('_ra', String(attempt + 1));
1080        var destination = url.toString();
1081        var navigated = false;
1082        var navigate = function () {
1083          if (navigated) return;
1084          navigated = true;
1085          try {
1086            window.location.replace(destination);
1087          } catch (error) {
1088            try { window.location.reload(); } catch (reloadError) {}
1089          }
1090        };
1091
1092        // Recovery must not depend on browser cache APIs settling correctly.
1093        window.setTimeout(navigate, Math.max(2250, retryDelay + 250));
1094        window.setTimeout(function () {
1095          reloadStarted = false;
1096          document.documentElement.removeAttribute('data-echo-reload-started');
1097        }, Math.max(5000, retryDelay + 2500));
1098        var delay = new Promise(function (resolve) { window.setTimeout(resolve, retryDelay); });
1099        return Promise.all([clearStaleAppCaches(), delay]).then(navigate, navigate);
1100      }
1101
1102      function showRecovery(cause) {
1103        if (document.documentElement.hasAttribute('data-echo-app-mounted')) return;
1104        if (recoveryShown) {
1105          if (cause !== 'asset') return;
1106          var previousRecovery = document.getElementById('echo-boot-recovery');
1107          if (previousRecovery) previousRecovery.remove();
1108          recoveryShown = false;
1109        }
1110        recoveryShown = true;
1111
1112        var recovery = document.createElement('div');
1113        recovery.id = 'echo-boot-recovery';
1114        recovery.className = 'echo-boot-recovery';
1115        recovery.setAttribute('role', 'alert');
1116        var card = document.createElement('div');
1117        card.className = 'echo-boot-recovery__card';
1118        var title = document.createElement('h1');
1119        title.textContent = cause === 'asset'
1120          ? 'Echo could not finish loading'
1121          : 'Echo is taking longer than expected';
1122        var message = document.createElement('p');
1123        message.textContent = cause === 'asset'
1124          ? 'A required app file did not load. Echo will make one safe recovery attempt.'
1125          : 'Echo is still loading. You can wait, or retry once without getting stuck in a reload loop.';
1126        var button = document.createElement('button');
1127        button.type = 'button';
1128        button.textContent = cause === 'asset' ? 'Retry Now' : 'Retry Loading';
1129        button.addEventListener('click', function () {
1130          button.disabled = true;
1131          button.textContent = 'Reloading…';
1132          void reloadLatest();
1133          window.setTimeout(function () {
1134            button.disabled = false;
1135            button.textContent = 'Try Again';
1136          }, 5000);
1137        });
1138        card.append(title, message, button);
1139        recovery.appendChild(card);
1140        document.body.appendChild(recovery);
1141
1142        var attempt = recoveryAttempt();
1143        // A timer only proves that startup is slow. Automatically navigating on
1144        // a slow CPU/network resets all progress and can create an endless loop.
1145        // Only a concrete missing-asset signal is eligible for one automatic
1146        // recovery; subsequent attempts remain explicitly user-controlled.
1147        if (cause === 'asset' && attempt < 1) void reloadLatest();
1148      }
1149
1150      function looksLikeBootAssetFailure(value) {
1151        var message = String(value && value.message ? value.message : value || '').toLowerCase();
1152        return message.indexOf('failed to fetch dynamically imported module') !== -1 ||
1153          message.indexOf('error loading dynamically imported module') !== -1 ||
1154          message.indexOf('importing a module script failed') !== -1 ||
1155          message.indexOf('unable to preload css') !== -1 ||
1156          message.indexOf('loading chunk') !== -1;
1157      }
1158
1159      function handleBootError(event) {
1160        if (document.documentElement.hasAttribute('data-echo-app-mounted')) return;
1161        var target = event.target;
1162        var assetUrl = target && (target.src || target.href);
1163        var errorFile = event.filename;
1164        if ((assetUrl && /\/assets\/.+\.(?:js|css)(?:[?#]|$)/i.test(assetUrl)) ||
1165          (errorFile && /\/assets\/.+\.js(?:[?#]|$)/i.test(errorFile)) ||
1166          looksLikeBootAssetFailure(event.error)) {
1167          showRecovery('asset');
1168        }
1169      }
1170
1171      function handleBootRejection(event) {
1172        if (!document.documentElement.hasAttribute('data-echo-app-mounted') && looksLikeBootAssetFailure(event.reason)) {
1173          showRecovery('asset');
1174        }
1175      }
1176
1177      function disarmRecovery() {
1178        if (watchdogTimer) {
1179          window.clearTimeout(watchdogTimer);
1180          watchdogTimer = 0;
1181        }
1182        window.removeEventListener('error', handleBootError, true);
1183        window.removeEventListener('unhandledrejection', handleBootRejection);
1184        var recovery = document.getElementById('echo-boot-recovery');
1185        if (recovery) recovery.remove();
1186        recoveryShown = false;
1187        try { delete window.__ECHO_BOOT_RECOVERY__; } catch (error) {
1188          window.__ECHO_BOOT_RECOVERY__ = undefined;
1189        }
1190      }
1191
1192      window.__ECHO_BOOT_RECOVERY__ = {
1193        show: function () { showRecovery('asset'); },
1194        disarm: disarmRecovery
1195      };
1196      window.addEventListener('error', handleBootError, true);
1197      window.addEventListener('unhandledrejection', handleBootRejection);
1198      watchdogTimer = window.setTimeout(function () { showRecovery('slow'); }, 20000);
1199    })();
1200  </script>
1200
1201<!-- Cloudflare Pages Analytics -->
vendor: 99 bytes, line 1201
1201<script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "
1201b7528674cc3f4c81972d67a23163ce88
vendor: 13 bytes, line 1201
1201"}'></script>
1201<!-- Cloudflare Pages Analytics --></body>
1202
1203</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.