@eazo/sdk 0.11.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/PROTOCOL.md +7 -1
  2. package/README.md +78 -7
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/internal/auth-primitive/EazoAuthClient.js +1 -1
  8. package/dist/internal/auth-primitive/EazoAuthClient.js.map +1 -1
  9. package/dist/internal/banner-ui/app-info.d.ts +78 -0
  10. package/dist/internal/banner-ui/app-info.d.ts.map +1 -0
  11. package/dist/internal/banner-ui/app-info.js +62 -0
  12. package/dist/internal/banner-ui/app-info.js.map +1 -0
  13. package/dist/internal/banner-ui/icons.d.ts +18 -4
  14. package/dist/internal/banner-ui/icons.d.ts.map +1 -1
  15. package/dist/internal/banner-ui/icons.js +57 -4
  16. package/dist/internal/banner-ui/icons.js.map +1 -1
  17. package/dist/internal/banner-ui/index.d.ts +16 -3
  18. package/dist/internal/banner-ui/index.d.ts.map +1 -1
  19. package/dist/internal/banner-ui/index.js +336 -41
  20. package/dist/internal/banner-ui/index.js.map +1 -1
  21. package/dist/internal/banner-ui/initial-info.d.ts +4 -0
  22. package/dist/internal/banner-ui/initial-info.d.ts.map +1 -0
  23. package/dist/internal/banner-ui/initial-info.js +22 -0
  24. package/dist/internal/banner-ui/initial-info.js.map +1 -0
  25. package/dist/internal/banner-ui/qr.d.ts +22 -0
  26. package/dist/internal/banner-ui/qr.d.ts.map +1 -0
  27. package/dist/internal/banner-ui/qr.js +95 -0
  28. package/dist/internal/banner-ui/qr.js.map +1 -0
  29. package/dist/internal/banner-ui/store-links.d.ts +38 -0
  30. package/dist/internal/banner-ui/store-links.d.ts.map +1 -1
  31. package/dist/internal/banner-ui/store-links.js +49 -0
  32. package/dist/internal/banner-ui/store-links.js.map +1 -1
  33. package/dist/internal/banner-ui/styles.d.ts +4 -2
  34. package/dist/internal/banner-ui/styles.d.ts.map +1 -1
  35. package/dist/internal/banner-ui/styles.js +578 -66
  36. package/dist/internal/banner-ui/styles.js.map +1 -1
  37. package/dist/internal/bridge/protocol.d.ts +6 -0
  38. package/dist/internal/bridge/protocol.d.ts.map +1 -1
  39. package/dist/internal/bridge/protocol.js +5 -1
  40. package/dist/internal/bridge/protocol.js.map +1 -1
  41. package/dist/internal/capabilities/ai.js +1 -1
  42. package/dist/internal/capabilities/ai.js.map +1 -1
  43. package/dist/internal/capabilities/auth.d.ts +10 -5
  44. package/dist/internal/capabilities/auth.d.ts.map +1 -1
  45. package/dist/internal/capabilities/auth.js +29 -13
  46. package/dist/internal/capabilities/auth.js.map +1 -1
  47. package/dist/internal/capabilities/device.d.ts +0 -1
  48. package/dist/internal/capabilities/device.d.ts.map +1 -1
  49. package/dist/internal/capabilities/device.js +2 -6
  50. package/dist/internal/capabilities/device.js.map +1 -1
  51. package/dist/internal/capabilities/memory.d.ts +10 -15
  52. package/dist/internal/capabilities/memory.d.ts.map +1 -1
  53. package/dist/internal/capabilities/memory.js +9 -26
  54. package/dist/internal/capabilities/memory.js.map +1 -1
  55. package/dist/internal/capabilities/notifications.d.ts +41 -0
  56. package/dist/internal/capabilities/notifications.d.ts.map +1 -0
  57. package/dist/internal/capabilities/notifications.js +61 -0
  58. package/dist/internal/capabilities/notifications.js.map +1 -0
  59. package/dist/internal/capabilities/share.d.ts +4 -2
  60. package/dist/internal/capabilities/share.d.ts.map +1 -1
  61. package/dist/internal/capabilities/share.js +13 -9
  62. package/dist/internal/capabilities/share.js.map +1 -1
  63. package/dist/internal/capabilities/storage.d.ts +7 -11
  64. package/dist/internal/capabilities/storage.d.ts.map +1 -1
  65. package/dist/internal/capabilities/storage.js +5 -13
  66. package/dist/internal/capabilities/storage.js.map +1 -1
  67. package/dist/internal/config.d.ts +9 -12
  68. package/dist/internal/config.d.ts.map +1 -1
  69. package/dist/internal/config.js +53 -22
  70. package/dist/internal/config.js.map +1 -1
  71. package/dist/internal/store.d.ts.map +1 -1
  72. package/dist/internal/store.js +0 -1
  73. package/dist/internal/store.js.map +1 -1
  74. package/dist/react.d.ts +34 -5
  75. package/dist/react.d.ts.map +1 -1
  76. package/dist/react.js +26 -5
  77. package/dist/react.js.map +1 -1
  78. package/dist/react.server.d.ts +22 -0
  79. package/dist/react.server.d.ts.map +1 -0
  80. package/dist/react.server.js +118 -0
  81. package/dist/react.server.js.map +1 -0
  82. package/dist/server.d.ts +29 -0
  83. package/dist/server.d.ts.map +1 -1
  84. package/dist/server.js +158 -0
  85. package/dist/server.js.map +1 -1
  86. package/dist/types.d.ts +0 -1
  87. package/dist/types.d.ts.map +1 -1
  88. package/package.json +7 -2
@@ -2,4 +2,42 @@ export declare const APP_STORE_URL = "https://eazo.ai/";
2
2
  export declare const PLAY_STORE_URL = "https://eazo.ai/";
3
3
  export declare const MARKETING_URL = "https://eazo.ai/";
4
4
  export declare function resolveStoreUrl(): string;
5
+ export interface BannerCta {
6
+ /** URL the CTA's `<a>` navigates to. */
7
+ href: string;
8
+ /** Store URL to fall back to when the deeplink doesn't open the app. */
9
+ storeUrl: string;
10
+ /**
11
+ * Whether the caller must run a JS timeout to detect "app not installed"
12
+ * and navigate to `storeUrl` itself. True on iOS (Safari has no native
13
+ * fallback for unhandled custom schemes); false on Android (Chrome's
14
+ * `intent://` URL carries its own `browser_fallback_url`) and desktop
15
+ * (no app-open attempt to fall back from).
16
+ */
17
+ needsTimeoutFallback: boolean;
18
+ }
19
+ /**
20
+ * Per-platform plan for the banner CTA. Uses Eazo Mobile's registered
21
+ * custom scheme `eazo://` (declared in `eazo-mobile/app.json#scheme`),
22
+ * which doesn't require any path-specific AASA / App Links entry.
23
+ *
24
+ * When an appId is configured (the normal case under the `<EazoProvider>`
25
+ * convention), the URL carries `app/<appId>` as its path so the mobile
26
+ * shell knows which app to push — `eazo-mobile/+native-intent.tsx`
27
+ * whitelists this prefix to skip expo-router's default reset, and
28
+ * `RootIntentObserver` then issues `router.push('/app/<appId>')` on top
29
+ * of the existing stack. A missing appId falls back to bare `eazo://`,
30
+ * which just foregrounds the app on its current screen.
31
+ *
32
+ * - **iOS**: `eazo://app/<appId>`. If the app is installed Safari opens
33
+ * it and the page is backgrounded; otherwise Safari shows a "Cannot
34
+ * open" toast and the page stays visible — the caller's JS timeout
35
+ * then navigates to the App Store.
36
+ * - **Android**: Chrome `intent://app/<appId>#Intent;scheme=eazo;…;end`.
37
+ * Chrome opens the app when installed (launch URL becomes
38
+ * `eazo://app/<appId>`) and navigates to `browser_fallback_url`
39
+ * natively when not. No JS timeout needed.
40
+ * - **Desktop**: just the marketing site; no app-open attempt.
41
+ */
42
+ export declare function resolveBannerCta(): BannerCta;
5
43
  //# sourceMappingURL=store-links.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-links.d.ts","sourceRoot":"","sources":["../../../src/internal/banner-ui/store-links.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAChD,eAAO,MAAM,cAAc,qBAAqB,CAAC;AACjD,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAUhD,wBAAgB,eAAe,IAAI,MAAM,CAMxC"}
1
+ {"version":3,"file":"store-links.d.ts","sourceRoot":"","sources":["../../../src/internal/banner-ui/store-links.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAChD,eAAO,MAAM,cAAc,qBAAqB,CAAC;AACjD,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAehD,wBAAgB,eAAe,IAAI,MAAM,CAMxC;AAED,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,IAAI,SAAS,CAmB5C"}
@@ -2,12 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MARKETING_URL = exports.PLAY_STORE_URL = exports.APP_STORE_URL = void 0;
4
4
  exports.resolveStoreUrl = resolveStoreUrl;
5
+ exports.resolveBannerCta = resolveBannerCta;
6
+ const config_1 = require("../config");
5
7
  // TODO(banner): replace placeholders once the iOS App Store ID and Android
6
8
  // package name are confirmed by the mobile team. Until then, all platforms
7
9
  // fall back to the marketing site so users never hit a 404.
8
10
  exports.APP_STORE_URL = "https://eazo.ai/";
9
11
  exports.PLAY_STORE_URL = "https://eazo.ai/";
10
12
  exports.MARKETING_URL = "https://eazo.ai/";
13
+ // Eazo Mobile Android package. Mirrors `android.package` in
14
+ // `eazo-mobile/app.json`. Used to build the Chrome intent URL so the
15
+ // browser tries to open the installed app first.
16
+ const ANDROID_PACKAGE = "ai.eazo.portal";
11
17
  function detectPlatform(ua) {
12
18
  if (/iPhone|iPad|iPod/i.test(ua))
13
19
  return "ios";
@@ -25,4 +31,47 @@ function resolveStoreUrl() {
25
31
  return exports.PLAY_STORE_URL;
26
32
  return exports.MARKETING_URL;
27
33
  }
34
+ /**
35
+ * Per-platform plan for the banner CTA. Uses Eazo Mobile's registered
36
+ * custom scheme `eazo://` (declared in `eazo-mobile/app.json#scheme`),
37
+ * which doesn't require any path-specific AASA / App Links entry.
38
+ *
39
+ * When an appId is configured (the normal case under the `<EazoProvider>`
40
+ * convention), the URL carries `app/<appId>` as its path so the mobile
41
+ * shell knows which app to push — `eazo-mobile/+native-intent.tsx`
42
+ * whitelists this prefix to skip expo-router's default reset, and
43
+ * `RootIntentObserver` then issues `router.push('/app/<appId>')` on top
44
+ * of the existing stack. A missing appId falls back to bare `eazo://`,
45
+ * which just foregrounds the app on its current screen.
46
+ *
47
+ * - **iOS**: `eazo://app/<appId>`. If the app is installed Safari opens
48
+ * it and the page is backgrounded; otherwise Safari shows a "Cannot
49
+ * open" toast and the page stays visible — the caller's JS timeout
50
+ * then navigates to the App Store.
51
+ * - **Android**: Chrome `intent://app/<appId>#Intent;scheme=eazo;…;end`.
52
+ * Chrome opens the app when installed (launch URL becomes
53
+ * `eazo://app/<appId>`) and navigates to `browser_fallback_url`
54
+ * natively when not. No JS timeout needed.
55
+ * - **Desktop**: just the marketing site; no app-open attempt.
56
+ */
57
+ function resolveBannerCta() {
58
+ if (typeof navigator === "undefined") {
59
+ return { href: exports.MARKETING_URL, storeUrl: exports.MARKETING_URL, needsTimeoutFallback: false };
60
+ }
61
+ const platform = detectPlatform(navigator.userAgent);
62
+ const appId = (0, config_1.getAppId)();
63
+ const path = appId ? `app/${encodeURIComponent(appId)}` : "";
64
+ if (platform === "ios") {
65
+ return { href: `eazo://${path}`, storeUrl: exports.APP_STORE_URL, needsTimeoutFallback: true };
66
+ }
67
+ if (platform === "android") {
68
+ const fallback = encodeURIComponent(exports.PLAY_STORE_URL);
69
+ return {
70
+ href: `intent://${path}#Intent;scheme=eazo;package=${ANDROID_PACKAGE};S.browser_fallback_url=${fallback};end`,
71
+ storeUrl: exports.PLAY_STORE_URL,
72
+ needsTimeoutFallback: false,
73
+ };
74
+ }
75
+ return { href: exports.MARKETING_URL, storeUrl: exports.MARKETING_URL, needsTimeoutFallback: false };
76
+ }
28
77
  //# sourceMappingURL=store-links.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-links.js","sourceRoot":"","sources":["../../../src/internal/banner-ui/store-links.ts"],"names":[],"mappings":";;;AAeA,0CAMC;AArBD,2EAA2E;AAC3E,2EAA2E;AAC3E,4DAA4D;AAC/C,QAAA,aAAa,GAAG,kBAAkB,CAAC;AACnC,QAAA,cAAc,GAAG,kBAAkB,CAAC;AACpC,QAAA,aAAa,GAAG,kBAAkB,CAAC;AAIhD,SAAS,cAAc,CAAC,EAAU;IAChC,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,eAAe;IAC7B,IAAI,OAAO,SAAS,KAAK,WAAW;QAAE,OAAO,qBAAa,CAAC;IAC3D,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACrD,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,qBAAa,CAAC;IAC7C,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,sBAAc,CAAC;IAClD,OAAO,qBAAa,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"store-links.js","sourceRoot":"","sources":["../../../src/internal/banner-ui/store-links.ts"],"names":[],"mappings":";;;AAsBA,0CAMC;AAwCD,4CAmBC;AAvFD,sCAAqC;AAErC,2EAA2E;AAC3E,2EAA2E;AAC3E,4DAA4D;AAC/C,QAAA,aAAa,GAAG,kBAAkB,CAAC;AACnC,QAAA,cAAc,GAAG,kBAAkB,CAAC;AACpC,QAAA,aAAa,GAAG,kBAAkB,CAAC;AAEhD,4DAA4D;AAC5D,qEAAqE;AACrE,iDAAiD;AACjD,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAIzC,SAAS,cAAc,CAAC,EAAU;IAChC,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,eAAe;IAC7B,IAAI,OAAO,SAAS,KAAK,WAAW;QAAE,OAAO,qBAAa,CAAC;IAC3D,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACrD,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,qBAAa,CAAC;IAC7C,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,sBAAc,CAAC;IAClD,OAAO,qBAAa,CAAC;AACvB,CAAC;AAiBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAgB,gBAAgB;IAC9B,IAAI,OAAO,SAAS,KAAK,WAAW,EAAE,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,qBAAa,EAAE,QAAQ,EAAE,qBAAa,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC;IACvF,CAAC;IACD,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,IAAA,iBAAQ,GAAE,CAAC;IACzB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,UAAU,IAAI,EAAE,EAAE,QAAQ,EAAE,qBAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC;IACzF,CAAC;IACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,kBAAkB,CAAC,sBAAc,CAAC,CAAC;QACpD,OAAO;YACL,IAAI,EAAE,YAAY,IAAI,+BAA+B,eAAe,2BAA2B,QAAQ,MAAM;YAC7G,QAAQ,EAAE,sBAAc;YACxB,oBAAoB,EAAE,KAAK;SAC5B,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,qBAAa,EAAE,QAAQ,EAAE,qBAAa,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC;AACvF,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export declare const BANNER_HEIGHT_DESKTOP = 52;
2
- export declare const BANNER_HEIGHT_MOBILE = 64;
3
- export declare const BANNER_UI_CSS = "\n.eazo-banner-root {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n z-index: 2147483550;\n display: flex;\n align-items: center;\n gap: 12px;\n height: 52px;\n padding: 0 14px 0 18px;\n background: #f1ebe0;\n color: #11130f;\n font-family: inherit;\n box-sizing: border-box;\n animation: eazo-banner-slide-down 220ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n@keyframes eazo-banner-slide-down {\n from { transform: translateY(-100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n\n.eazo-banner-brand {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n color: #11130f;\n}\n\n.eazo-banner-copy {\n flex: 1;\n min-width: 0;\n font-size: 14px;\n font-weight: 500;\n color: rgba(17, 19, 15, 0.62);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.eazo-banner-cta {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n height: 34px;\n padding: 0 14px;\n border-radius: 12px;\n background: #d4614a;\n color: #ffffff;\n font-size: 13px;\n font-weight: 600;\n text-decoration: none;\n cursor: pointer;\n transition: filter 160ms ease, box-shadow 160ms ease;\n}\n.eazo-banner-cta:hover {\n filter: brightness(1.06);\n box-shadow: 0 8px 18px rgba(212, 97, 74, 0.36);\n}\n\n.eazo-banner-close {\n flex-shrink: 0;\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 0;\n border-radius: 999px;\n background: transparent;\n color: rgba(17, 19, 15, 0.52);\n cursor: pointer;\n padding: 0;\n transition: color 120ms ease, background 120ms ease;\n}\n.eazo-banner-close:hover {\n color: #11130f;\n background: rgba(17, 19, 15, 0.06);\n}\n\n@media (max-width: 480px) {\n .eazo-banner-root {\n height: 64px;\n padding: 0 10px 0 14px;\n gap: 10px;\n }\n .eazo-banner-copy {\n font-size: 12px;\n line-height: 1.25;\n white-space: normal;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n .eazo-banner-cta {\n height: 32px;\n padding: 0 12px;\n font-size: 12px;\n }\n}\n";
2
+ export declare const BANNER_HEIGHT_MOBILE = 56;
3
+ export declare const BOTTOM_HEIGHT_DESKTOP = 72;
4
+ export declare const BOTTOM_HEIGHT_MOBILE = 78;
5
+ export declare const BANNER_UI_CSS = "\n/* The whole handoff UI lives inside ONE fixed-positioned container that\n * fills the viewport and flex-columns its three children: top banner +\n * overlay (which holds the modal) + bottom banner. This replaces the\n * earlier design where each piece was independently position:fixed\n * with hand-tuned top:52px / bottom:60px insets \u2014 that scheme broke\n * any time an ancestor of the SDK mount established a containing block\n * (transform / filter / backdrop-filter / contain on <body>, a wrapper,\n * etc.), at which point position:fixed becomes relative to that\n * ancestor and the math goes wrong. Flex layout makes the overlay\n * genuinely between the banners by structure, not by pixel math.\n *\n * The root is pointer-events:none so the user's page underneath stays\n * interactive in transparent regions (there shouldn't be any when the\n * overlay's modal is up, but it's the right default). Each visual child\n * (banners + overlay dim) opts back in with pointer-events:auto. */\n.eazo-handoff-root {\n \n --eazo-cream: #f1ebe0;\n --eazo-paper: #faf6ee;\n --eazo-ink: #11130f;\n --eazo-ink-soft: rgba(17,19,15,0.62);\n --eazo-ink-faint: rgba(17,19,15,0.32);\n --eazo-hair: rgba(17,19,15,0.10);\n --eazo-coral: #d4614a;\n --eazo-coral-gradient: linear-gradient(180deg, #F47A42 0%, #EE5C2A 100%);\n --eazo-glow: rgba(212,97,74,0.36);\n --eazo-sans: \"Inter\", \"Helvetica Neue\", system-ui, sans-serif;\n --eazo-serif: \"Source Serif 4\", \"GT Sectra\", \"Tiempos\", Georgia, serif;\n --eazo-mono: \"JetBrains Mono\", \"IBM Plex Mono\", ui-monospace, Menlo, monospace;\n\n position: fixed;\n inset: 0;\n z-index: 2147483540;\n display: flex;\n flex-direction: column;\n /* justify-content:space-between keeps the bottom banner pinned even\n * when the user dismisses the modal (the overlay child unmounts) \u2014\n * without it the flex-column would collapse the bottom banner up to\n * sit right under the top one. */\n justify-content: space-between;\n color: var(--eazo-ink);\n font-family: var(--eazo-sans);\n box-sizing: border-box;\n pointer-events: none;\n}\n.eazo-handoff-root *, .eazo-handoff-root *::before, .eazo-handoff-root *::after {\n box-sizing: border-box;\n}\n\n@keyframes eazo-handoff-slide-down {\n from { transform: translateY(-100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n@keyframes eazo-handoff-slide-up {\n from { transform: translateY(100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n@keyframes eazo-handoff-orbit { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n@keyframes eazo-handoff-orbit-rev { from { transform: rotate(360deg); } to { transform: rotate(0deg); } }\n@keyframes eazo-handoff-glow { 0%,100% { opacity: 0.7; } 50% { opacity: 1; } }\n@keyframes eazo-handoff-fade-in { from { opacity: 0; } to { opacity: 1; } }\n@keyframes eazo-handoff-pop-in {\n from { opacity: 0; transform: translateY(12px) scale(0.97); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n}\n\n/* ============ TOP BANNER ============\n *\n * Slim three-piece strip: brand mark, single-line copy, CTA. The\n * underlying app's content sits below this. Non-dismissible.\n */\n.eazo-banner-root {\n /* Flex child of .eazo-handoff-root \u2014 naturally pinned to the top of\n * the viewport-filling container. No position:fixed needed. */\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 12px;\n height: 52px;\n padding: 0 14px 0 18px;\n background: var(--eazo-cream);\n border-bottom: 1px solid var(--eazo-hair);\n pointer-events: auto;\n animation: eazo-handoff-slide-down 240ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n.eazo-banner-brand {\n display: inline-flex; align-items: center;\n flex-shrink: 0;\n color: var(--eazo-ink);\n}\n.eazo-banner-copy {\n flex: 1; min-width: 0;\n font-size: 14px; font-weight: 500;\n color: var(--eazo-ink-soft);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.eazo-banner-cta {\n flex-shrink: 0;\n display: inline-flex; align-items: center; gap: 6px;\n height: 30px; padding: 0 14px; border-radius: 10px;\n background: var(--eazo-coral-gradient); color: #fff;\n font-size: 12px; font-weight: 600; border: 0; cursor: pointer;\n text-decoration: none;\n box-shadow: 0 10px 22px var(--eazo-glow);\n transition: filter 160ms ease, box-shadow 160ms ease;\n}\n.eazo-banner-cta:hover { filter: brightness(1.06); }\n\n/* CTA wrapper anchors the hover/focus popover. position:relative is the\n * coordinate origin for the absolutely-positioned popover below. */\n.eazo-banner-cta-wrap {\n position: relative;\n display: inline-flex;\n flex-shrink: 0;\n}\n\n/* Hover popover holding the page-URL QR. Matches the v5-stagelight\n * design (project/v5-stagelight.jsx:59-85). The CTA's right edge anchors\n * the right edge of the popover so it never spills off the viewport on\n * a banner where the CTA is hugged to the right padding. */\n.eazo-banner-cta-popover {\n position: absolute;\n top: calc(100% + 10px);\n right: 0;\n z-index: 2147483560;\n display: flex; flex-direction: column; align-items: center; gap: 8px;\n min-width: 168px;\n padding: 14px;\n background: #fff;\n border: 1px solid var(--eazo-hair);\n border-radius: 14px;\n box-shadow:\n 0 24px 50px -20px rgba(17,19,15,0.22),\n 0 0 0 1px rgba(17,19,15,0.03);\n animation: eazo-handoff-fade-in 140ms ease-out;\n}\n/* Triangular tail pointing back up at the CTA. Rotated square so it\n * inherits the card's border + background without an extra SVG. */\n.eazo-banner-cta-popover-arrow {\n position: absolute;\n top: -7px; right: 24px;\n width: 12px; height: 12px;\n background: #fff;\n border-top: 1px solid var(--eazo-hair);\n border-left: 1px solid var(--eazo-hair);\n transform: rotate(45deg);\n}\n.eazo-banner-cta-popover-qr {\n padding: 4px;\n background: #fff;\n line-height: 0;\n}\n.eazo-banner-cta-popover-caption {\n font-family: var(--eazo-mono);\n font-size: 11px;\n line-height: 1.4;\n letter-spacing: 0.04em;\n color: var(--eazo-ink-soft);\n text-align: center;\n}\n\n/* ============ OVERLAY (backdrop + spotlight + modal) ============\n *\n * The flex-middle of .eazo-handoff-root. Takes whatever vertical space\n * the top and bottom banners don't claim \u2014 i.e. it IS the inter-banner\n * area by structure, not by pixel math. overflow:hidden clips any\n * oversized modal at this seam; the modal's own max-height:100% plus\n * the overlay's flex centering keeps it inside.\n */\n.eazo-handoff-overlay {\n flex: 1;\n min-height: 0; /* allow the flex item to shrink below content size */\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n padding: 16px;\n pointer-events: auto;\n animation: eazo-handoff-fade-in 320ms ease-out;\n}\n.eazo-handoff-overlay-dim {\n position: absolute; inset: 0;\n background: rgba(241,235,224,0.78);\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n}\n.eazo-handoff-overlay-spot {\n position: absolute; inset: 0;\n background: radial-gradient(ellipse at 50% 50%, rgba(212,97,74,0.22) 0%, rgba(212,97,74,0.06) 30%, transparent 58%);\n pointer-events: none;\n}\n\n.eazo-modal {\n /* Natural flex centering by the overlay parent \u2014 no absolute\n * positioning. This keeps the modal inside the overlay's banner-\n * constrained box even when its content is tall, so it never bleeds\n * into the top or bottom banner area. If the modal is taller than the\n * overlay, the inner content scrolls. */\n position: relative;\n width: min(540px, 100%);\n max-height: 100%;\n overflow-y: auto;\n padding: 32px 32px 28px;\n background: rgba(255,255,255,0.92);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid var(--eazo-hair);\n border-radius: 24px;\n color: var(--eazo-ink);\n box-shadow:\n 0 60px 100px -40px rgba(17,19,15,0.28),\n inset 0 1px 0 rgba(255,255,255,0.7),\n 0 0 60px var(--eazo-glow);\n display: flex; flex-direction: column; align-items: center; gap: 18px;\n animation: eazo-handoff-pop-in 360ms cubic-bezier(0.16, 1, 0.3, 1) both;\n}\n/* Close button \u2014 sits in the modal's top-right corner. The top + bottom\n * Eazo banners stay visible after the user dismisses the modal; only\n * this center \"strong CTA\" goes away (per-tab via sessionStorage). */\n.eazo-modal-close {\n position: absolute;\n top: 12px; right: 12px;\n width: 30px; height: 30px;\n display: inline-flex; align-items: center; justify-content: center;\n border: 0; padding: 0;\n border-radius: 999px;\n background: rgba(17,19,15,0.04);\n color: var(--eazo-ink-soft);\n cursor: pointer;\n transition: background 140ms ease, color 140ms ease;\n}\n.eazo-modal-close:hover {\n background: rgba(17,19,15,0.08);\n color: var(--eazo-ink);\n}\n.eazo-modal-close:focus-visible {\n outline: 2px solid var(--eazo-coral);\n outline-offset: 2px;\n}\n\n/* ============ ORBITING CAPABILITIES + APP MONOLITH ============\n *\n * Geometry runs in a 280-unit coordinate space (matches the V5 design\n * canvas). The rings SVG uses a viewBox so its content scales to whatever\n * pixel size the .eazo-orbit container is in CSS (280 desktop, 220\n * mobile). The capability nodes position via percentage left/top on\n * the rotating track, then use negative margins to center on that point\n * \u2014 margins do not fight the track rotate animation the way a\n * transform: translate(-50%, -50%) would.\n */\n.eazo-orbit {\n position: relative;\n width: 280px; height: 280px;\n display: grid; place-items: center;\n}\n.eazo-orbit-rings {\n position: absolute; inset: 0;\n width: 100%; height: 100%;\n opacity: 0.95;\n}\n.eazo-orbit-track {\n position: absolute; inset: 0; width: 100%; height: 100%;\n animation: eazo-handoff-orbit 30s linear infinite;\n}\n.eazo-orbit-node {\n position: absolute;\n width: 36px; height: 36px;\n margin: -18px 0 0 -18px;\n border-radius: 10px;\n background: #fff; border: 1px solid var(--eazo-hair);\n display: grid; place-items: center;\n box-shadow: 0 10px 22px -10px rgba(17,19,15,0.15);\n animation: eazo-handoff-orbit-rev 30s linear infinite;\n color: var(--eazo-coral);\n}\n.eazo-monolith {\n width: 96px; height: 96px; border-radius: 22px;\n /* Default fallback background \u2014 visible behind emoji icons and the\n * typographic initials fallback. URL icons render as a child <img>\n * that covers this completely. Eazo coral gradient (same as primary\n * CTAs) so the empty state reads as a clear Eazo-brand placeholder. */\n background: var(--eazo-coral-gradient);\n display: grid; place-items: center;\n position: relative;\n color: #ffffff;\n font-family: var(--eazo-serif); font-weight: 500;\n font-size: 42px; letter-spacing: -0.02em;\n box-shadow:\n 0 30px 60px -20px var(--eazo-glow),\n inset 0 1px 0 rgba(255,255,255,0.30),\n 0 0 0 1px rgba(255,255,255,0.14);\n overflow: hidden;\n}\n.eazo-monolith img {\n width: 100%; height: 100%; object-fit: cover; display: block;\n}\n\n.eazo-modal-eyebrow {\n font-family: var(--eazo-mono); font-size: 10px;\n letter-spacing: 0.18em; text-transform: uppercase;\n color: var(--eazo-ink-faint);\n text-align: center;\n}\n.eazo-modal-title {\n margin: 0; font-family: var(--eazo-serif); font-weight: 500;\n font-size: 32px; line-height: 1.15; letter-spacing: -0.02em;\n text-align: center; max-width: 360px;\n /* Clamp at 2 lines so an unusually long app name doesn't blow up the\n * modal height. Ellipsis takes over for the overflow. */\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n word-break: break-word;\n}\n.eazo-modal-sub {\n margin: 0; font-size: 13px; line-height: 1.5;\n color: var(--eazo-ink-soft);\n text-align: center; max-width: 360px;\n /* Same idea \u2014 long taglines clamp to 3 lines to keep the QR + CTA\n * visible without scrolling. */\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n overflow: hidden;\n word-break: break-word;\n}\n\n/* Skeleton blocks shown while public app info is in flight. The modal\n * frame appears immediately so the user sees Eazo's commitment to the\n * handoff; the name / tagline swap in once the fetch resolves. */\n.eazo-skel {\n display: inline-block;\n vertical-align: middle;\n background: linear-gradient(90deg,\n rgba(17,19,15,0.05) 0%,\n rgba(17,19,15,0.12) 50%,\n rgba(17,19,15,0.05) 100%);\n background-size: 200% 100%;\n border-radius: 8px;\n animation: eazo-skel-shimmer 1.4s linear infinite;\n}\n.eazo-skel-title { width: 60%; height: 36px; }\n.eazo-skel-sub-1 { width: 80%; height: 13px; margin-top: 8px; }\n.eazo-skel-sub-2 { width: 55%; height: 13px; margin-top: 6px; }\n.eazo-skel-stat { width: 28px; height: 11px; border-radius: 4px; }\n@keyframes eazo-skel-shimmer {\n from { background-position: 200% 0; }\n to { background-position: -200% 0; }\n}\n\n/* Monolith-tuned shimmer \u2014 sweeps a brighter band over the dark navy\n * gradient. Used while public app info is still loading, and as the\n * placeholder behind an <img> until it decodes. */\n.eazo-monolith-skel {\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg,\n rgba(255,255,255,0.00) 0%,\n rgba(255,255,255,0.18) 50%,\n rgba(255,255,255,0.00) 100%);\n background-size: 200% 100%;\n animation: eazo-skel-shimmer 1.4s linear infinite;\n pointer-events: none;\n}\n.eazo-monolith-img {\n width: 100%; height: 100%;\n object-fit: cover; display: block;\n opacity: 0;\n transition: opacity 220ms ease-out;\n}\n.eazo-monolith-img.is-loaded { opacity: 1; }\n\n/* ============ QR + CTA ROW ============ */\n.eazo-cta-row {\n width: 100%; display: flex; gap: 12px; align-items: stretch; margin-top: 6px;\n}\n.eazo-qr-tile {\n padding: 8px; border-radius: 10px;\n background: #fff; border: 1px solid var(--eazo-hair);\n display: grid; place-items: center;\n}\n.eazo-cta-body {\n flex: 1; min-width: 0;\n display: flex; flex-direction: column; justify-content: space-between; gap: 8px;\n}\n.eazo-cta-headline {\n font-size: 12px; font-weight: 600;\n}\n.eazo-cta-fine {\n font-size: 10px; color: var(--eazo-ink-faint); margin-top: 4px;\n font-family: var(--eazo-mono); letter-spacing: 0.04em; line-height: 1.5;\n}\n.eazo-cta-primary {\n display: inline-flex; align-items: center; justify-content: center; gap: 8px;\n height: 40px; border-radius: 10px;\n background: var(--eazo-coral-gradient); color: #fff;\n font-size: 13px; font-weight: 600; border: 0; cursor: pointer;\n text-decoration: none;\n box-shadow: 0 14px 26px var(--eazo-glow);\n transition: filter 160ms ease;\n}\n.eazo-cta-primary:hover { filter: brightness(1.06); }\n\n/* ============ BOTTOM BANNER ============\n *\n * Per V5 / M5 design: two prominent stats on the left (heart + chat,\n * each rendered as a tinted icon-tile with a stacked value-over-label\n * column) separated by a thin hair-divider, and a coral \"Remix\" pill\n * on the right that reuses the top-banner CTA handoff. A small\n * \"eazo.ai \u2197\" mark sits to the left of the pill on desktop only \u2014\n * on phone widths (\u2264480px) it drops out so the Remix pill keeps its\n * thumb-zone weight.\n */\n.eazo-bottom-root {\n /* Flex child of .eazo-handoff-root \u2014 naturally pinned to the bottom of\n * the viewport-filling container. No position:fixed needed. */\n flex-shrink: 0;\n display: flex; align-items: center; justify-content: space-between;\n gap: 16px;\n height: 72px;\n padding: 0 22px 0 26px;\n background: #fff;\n border-top: 1px solid var(--eazo-hair);\n pointer-events: auto;\n animation: eazo-handoff-slide-up 240ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n.eazo-bottom-stats {\n display: inline-flex; align-items: center; gap: 22px;\n min-width: 0; color: var(--eazo-ink);\n}\n.eazo-bottom-stat {\n display: inline-flex; align-items: center; gap: 9px;\n font-family: var(--eazo-sans);\n flex-shrink: 0;\n}\n/* Tinted square tile that frames each stat icon \u2014 coral-on-cream for\n * filled glyphs (heart), neutral-on-cream for line glyphs (chat). */\n.eazo-bottom-stat-icon {\n display: inline-flex; align-items: center; justify-content: center;\n width: 30px; height: 30px; border-radius: 8px;\n background: rgba(212,97,74,0.10);\n color: var(--eazo-coral);\n flex-shrink: 0;\n}\n.eazo-bottom-stat-icon.is-line {\n background: rgba(17,19,15,0.05);\n color: var(--eazo-ink);\n}\n.eazo-bottom-stat-text {\n display: inline-flex; flex-direction: column; line-height: 1.05;\n}\n.eazo-bottom-stat-value {\n font-family: var(--eazo-sans);\n font-size: 16px; font-weight: 600; letter-spacing: -0.01em;\n}\n.eazo-bottom-stat-label {\n font-family: var(--eazo-sans);\n font-size: 11px; font-weight: 500;\n color: var(--eazo-ink-faint);\n margin-top: 1px;\n}\n.eazo-bottom-stat-divider {\n width: 1px; height: 28px;\n background: var(--eazo-hair);\n flex-shrink: 0;\n}\n.eazo-bottom-skel {\n display: inline-block; vertical-align: middle;\n width: 32px; height: 18px; border-radius: 4px;\n background: linear-gradient(90deg,\n rgba(17,19,15,0.05) 0%,\n rgba(17,19,15,0.12) 50%,\n rgba(17,19,15,0.05) 100%);\n background-size: 200% 100%;\n animation: eazo-skel-shimmer 1.4s linear infinite;\n}\n\n.eazo-bottom-actions {\n display: inline-flex; align-items: center; gap: 14px;\n flex-shrink: 0;\n}\n.eazo-bottom-site {\n display: inline-flex; align-items: center; gap: 4px;\n color: var(--eazo-ink-soft);\n text-decoration: none;\n font-family: var(--eazo-sans); font-size: 12px; font-weight: 500;\n white-space: nowrap;\n transition: color 140ms ease;\n}\n.eazo-bottom-site:hover { color: var(--eazo-ink); }\n.eazo-bottom-site b { color: var(--eazo-ink); font-weight: 600; }\n\n/* Primary CTA on the bottom banner. Renders as <a> so it picks up the\n * same iOS-timeout fallback handler as the top-banner CTA via the\n * shared bindCtaClick \u2014 keeps the Remix tap on the same install /\n * deeplink path as the rest of the handoff UX. */\n.eazo-bottom-remix {\n display: inline-flex; align-items: center; justify-content: center; gap: 9px;\n height: 44px; padding: 0 20px 0 18px;\n border: 0; cursor: pointer;\n border-radius: 999px;\n background: var(--eazo-coral-gradient); color: #fff;\n font-family: var(--eazo-sans);\n font-size: 14px; font-weight: 600; letter-spacing: -0.005em;\n white-space: nowrap;\n box-shadow:\n 0 12px 24px var(--eazo-glow),\n inset 0 1px 0 rgba(255,255,255,0.18);\n text-decoration: none;\n transition: transform 140ms ease, box-shadow 140ms ease;\n}\n.eazo-bottom-remix:hover {\n transform: translateY(-1px);\n box-shadow:\n 0 14px 28px var(--eazo-glow),\n inset 0 1px 0 rgba(255,255,255,0.22);\n}\n.eazo-bottom-remix:active { transform: translateY(0); }\n\n/* ============ MOBILE TWEAKS (\u2264480px) ============ */\n@media (max-width: 480px) {\n .eazo-banner-root {\n height: 56px;\n padding: 0 10px 0 14px;\n gap: 10px;\n }\n .eazo-banner-copy {\n font-size: 12px; line-height: 1.25; white-space: normal;\n display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;\n }\n .eazo-banner-cta { height: 28px; padding: 0 10px; font-size: 11px; border-radius: 8px; }\n /* Hover doesn't resolve reliably on touch \u2014 the CTA still works as a\n * plain link, no popover needed. Belt-and-suspenders to the JS check\n * (the popover render is also gated on the 'open' state, which never\n * flips without mouseenter / focus). */\n .eazo-banner-cta-popover { display: none; }\n\n .eazo-modal {\n width: calc(100vw - 32px);\n padding: 24px 20px 20px;\n border-radius: 20px;\n gap: 14px;\n }\n .eazo-orbit { width: 220px; height: 220px; }\n .eazo-monolith {\n width: 76px; height: 76px; border-radius: 18px;\n font-size: 32px;\n }\n .eazo-orbit-node {\n width: 28px; height: 28px; border-radius: 8px;\n margin: -14px 0 0 -14px;\n }\n .eazo-modal-title { font-size: 26px; }\n .eazo-modal-sub { font-size: 12px; }\n\n /* Mobile: the user is already on a phone \u2014 no point showing them a QR\n * to scan with their phone, and the \"Scan to open\" headline + fine\n * print only made sense paired with the QR. Collapse to the primary\n * CTA alone. */\n .eazo-qr-tile { display: none; }\n .eazo-cta-row { flex-direction: column; gap: 10px; }\n .eazo-cta-primary { height: 44px; width: 100%; font-size: 14px; border-radius: 12px; }\n .eazo-cta-headline { display: none; }\n .eazo-cta-fine { display: none; }\n\n .eazo-bottom-root {\n height: 78px;\n padding: 0 16px 0 20px;\n gap: 12px;\n }\n /* Tighter cells per the M5 (390px) spec: smaller icon tile, smaller\n * value, smaller divider. Labels stay \u2014 they're a key part of the\n * visual rhythm in M5. */\n .eazo-bottom-stats { gap: 12px; }\n .eazo-bottom-stat { gap: 7px; }\n .eazo-bottom-stat-icon { width: 26px; height: 26px; border-radius: 7px; }\n .eazo-bottom-stat-value { font-size: 14px; }\n .eazo-bottom-stat-label { font-size: 10px; }\n .eazo-bottom-stat-divider { height: 24px; }\n .eazo-bottom-skel { width: 28px; height: 15px; }\n /* M5 drops the secondary eazo.ai mark on phone widths so the Remix\n * pill keeps unambiguous thumb-zone weight. */\n .eazo-bottom-site { display: none; }\n .eazo-bottom-remix {\n height: 44px; padding: 0 18px 0 16px;\n gap: 8px; font-size: 13px;\n box-shadow:\n 0 10px 22px var(--eazo-glow),\n inset 0 1px 0 rgba(255,255,255,0.18);\n }\n /* Drop the trailing \"this app\" wording on phone widths \u2014 the icon\n * plus the verb is already unambiguous and the pill stays compact. */\n .eazo-bottom-remix-suffix { display: none; }\n}\n";
4
6
  export declare function ensureBannerStylesInjected(): void;
5
7
  //# sourceMappingURL=styles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/internal/banner-ui/styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,eAAO,MAAM,aAAa,slEAsGzB,CAAC;AAEF,wBAAgB,0BAA0B,IAAI,IAAI,CAQjD"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/internal/banner-ui/styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAKvC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAiBvC,eAAO,MAAM,aAAa,8zqBAwkBzB,CAAC;AAEF,wBAAgB,0BAA0B,IAAI,IAAI,CAkBjD"}