@eazo/sdk 0.13.0 → 0.15.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.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/banner-ui/app-info.d.ts +78 -0
- package/dist/internal/banner-ui/app-info.d.ts.map +1 -0
- package/dist/internal/banner-ui/app-info.js +62 -0
- package/dist/internal/banner-ui/app-info.js.map +1 -0
- package/dist/internal/banner-ui/icons.d.ts +18 -4
- package/dist/internal/banner-ui/icons.d.ts.map +1 -1
- package/dist/internal/banner-ui/icons.js +58 -4
- package/dist/internal/banner-ui/icons.js.map +1 -1
- package/dist/internal/banner-ui/index.d.ts +16 -3
- package/dist/internal/banner-ui/index.d.ts.map +1 -1
- package/dist/internal/banner-ui/index.js +377 -41
- package/dist/internal/banner-ui/index.js.map +1 -1
- package/dist/internal/banner-ui/initial-info.d.ts +4 -0
- package/dist/internal/banner-ui/initial-info.d.ts.map +1 -0
- package/dist/internal/banner-ui/initial-info.js +22 -0
- package/dist/internal/banner-ui/initial-info.js.map +1 -0
- package/dist/internal/banner-ui/qr.d.ts +22 -0
- package/dist/internal/banner-ui/qr.d.ts.map +1 -0
- package/dist/internal/banner-ui/qr.js +95 -0
- package/dist/internal/banner-ui/qr.js.map +1 -0
- package/dist/internal/banner-ui/store-links.d.ts +38 -0
- package/dist/internal/banner-ui/store-links.d.ts.map +1 -1
- package/dist/internal/banner-ui/store-links.js +49 -0
- package/dist/internal/banner-ui/store-links.js.map +1 -1
- package/dist/internal/banner-ui/styles.d.ts +4 -2
- package/dist/internal/banner-ui/styles.d.ts.map +1 -1
- package/dist/internal/banner-ui/styles.js +709 -66
- package/dist/internal/banner-ui/styles.js.map +1 -1
- package/dist/react.d.ts +26 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +42 -2
- package/dist/react.js.map +1 -1
- package/dist/react.server.d.ts +22 -0
- package/dist/react.server.d.ts.map +1 -0
- package/dist/react.server.js +118 -0
- package/dist/react.server.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +13 -1
- package/dist/server.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.QrSvg = QrSvg;
|
|
40
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
41
|
+
const React = __importStar(require("react"));
|
|
42
|
+
// `qrcode-generator` is a zero-dep, browser-friendly QR encoder. CJS-only
|
|
43
|
+
// default export — we import via require shim to avoid the ES-interop
|
|
44
|
+
// landmines that come with its UMD shape.
|
|
45
|
+
const qrcode_generator_1 = __importDefault(require("qrcode-generator"));
|
|
46
|
+
/**
|
|
47
|
+
* Renders a real, scannable QR code as inline SVG. Used by the web-only
|
|
48
|
+
* handoff overlay to encode the `eazo://` deep link for the host app so
|
|
49
|
+
* users on a desktop browser can pop their phone over the screen to open
|
|
50
|
+
* the app directly.
|
|
51
|
+
*/
|
|
52
|
+
function QrSvg({ value, size = 88, fg = "#11130f", bg = "#ffffff", radius = 6, }) {
|
|
53
|
+
// Memoize the encode — fairly cheap, but the SVG can re-render often
|
|
54
|
+
// (the parent banner re-renders on resize / state changes) and the
|
|
55
|
+
// encoder is deterministic for a given `value`.
|
|
56
|
+
const matrix = React.useMemo(() => {
|
|
57
|
+
if (!value)
|
|
58
|
+
return null;
|
|
59
|
+
// Type-number 0 = "auto-pick smallest that fits" given the data + ECC.
|
|
60
|
+
// Error-correction level M is a sensible default for promo URLs:
|
|
61
|
+
// resilient to ~15% damage but still compact enough for a 25px-ish
|
|
62
|
+
// module count at 88px box.
|
|
63
|
+
const qr = (0, qrcode_generator_1.default)(0, "M");
|
|
64
|
+
qr.addData(value);
|
|
65
|
+
qr.make();
|
|
66
|
+
const count = qr.getModuleCount();
|
|
67
|
+
const rows = [];
|
|
68
|
+
for (let r = 0; r < count; r += 1) {
|
|
69
|
+
const row = [];
|
|
70
|
+
for (let c = 0; c < count; c += 1) {
|
|
71
|
+
row.push(qr.isDark(r, c));
|
|
72
|
+
}
|
|
73
|
+
rows.push(row);
|
|
74
|
+
}
|
|
75
|
+
return { rows, count };
|
|
76
|
+
}, [value]);
|
|
77
|
+
if (!matrix)
|
|
78
|
+
return null;
|
|
79
|
+
const { rows, count } = matrix;
|
|
80
|
+
// Reserve a 2-module quiet zone on each side so scanners can lock on
|
|
81
|
+
// even when the SVG sits flush against neighboring chrome.
|
|
82
|
+
const quiet = 2;
|
|
83
|
+
const totalCells = count + quiet * 2;
|
|
84
|
+
const cell = size / totalCells;
|
|
85
|
+
const rects = [];
|
|
86
|
+
for (let r = 0; r < count; r += 1) {
|
|
87
|
+
for (let c = 0; c < count; c += 1) {
|
|
88
|
+
if (!rows[r][c])
|
|
89
|
+
continue;
|
|
90
|
+
rects.push((0, jsx_runtime_1.jsx)("rect", { x: (c + quiet) * cell, y: (r + quiet) * cell, width: cell, height: cell, fill: fg }, `${r}-${c}`));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return ((0, jsx_runtime_1.jsx)("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, style: { background: bg, borderRadius: radius, display: "block" }, "aria-label": "QR code", role: "img", children: rects }));
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=qr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qr.js","sourceRoot":"","sources":["../../../src/internal/banner-ui/qr.tsx"],"names":[],"mappings":"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"}
|
|
@@ -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":"
|
|
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":";;;
|
|
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 =
|
|
3
|
-
export declare const
|
|
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/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Host content safe area \u2014 two-layer wrapper\n *\n * Two nested elements wrap the host's children at the EazoProvider level:\n *\n * <div class=\"eazo-app-area\"> \u2190 outer: containing block\n * <div class=\"eazo-app-area-scroller\"> \u2190 inner: scroll container\n * {host children}\n * </div>\n * </div>\n *\n * Both elements are ALWAYS rendered (SSR + CSR markup is static, no\n * hydration mismatch). The effective styles activate only when the host\n * is a plain web browser AND the handoff banners are mounted \u2014 gated on\n * the `eazo-host-web` class on `<html>`, set/cleared by the banner-ui\n * effect. In a mobile WebView or iframe both elements are inert `<div>`s.\n *\n * Why two layers (this is the whole point):\n *\n * A single-element wrapper that combines `transform: translateZ(0)` AND\n * `overflow: auto` LOOKS like it should keep host's `position: fixed;\n * bottom: 0` elements pinned above the SDK banner \u2014 the transform\n * reparents the containing block to the wrapper, after all. But under\n * that combination, browsers (per the CSS positioning + scrolling spec\n * interaction) demote the fixed descendant to absolute-like behavior\n * AND translate it by the wrapper's scroll offset. The \"sticky CTA\"\n * ends up scrolling with content rather than staying pinned.\n *\n * Splitting them fixes this:\n * - `.eazo-app-area` (outer): `position: fixed` between the banners\n * + `transform: translateZ(0)`. It establishes the containing block\n * but is NOT a scroll container \u2014 its own padding box never moves.\n * - `.eazo-app-area-scroller` (inner): `position: absolute; inset: 0`\n * to fill the outer + `overflow-y: auto` to scroll host content. It\n * is NOT a containing block for fixed descendants (no transform,\n * static position contexts don't qualify), so host's\n * `position: fixed; bottom: 0` still resolves all the way up to the\n * outer. The bar is painted in the outer's coordinate space, OUTSIDE\n * the inner's scroll layer, so scrolling host content does NOT\n * translate it. Result: bar stays visually pinned to the outer's\n * bottom edge \u2014 which sits exactly above the SDK banner.\n *\n * Why scope the styles to `html.eazo-host-web`:\n * The wrapper only exists to keep host content clear of the SDK's\n * handoff banners. In a mobile WebView or iframe the banners don't\n * render \u2014 the wrapper has no job, and activating the fixed-position\n * + overflow + containing-block semantics there would silently break\n * `window.scrollY`, `window` scroll listeners, body-overflow scroll\n * locks, and host modals at `position: fixed; inset: 0` for zero\n * product benefit. So both layers stay inert outside plain web.\n *\n * Known trade-offs on web (called out in CHANGELOG):\n * - Scrolling happens inside `.eazo-app-area-scroller`, not on\n * `window`. Code reading `window.scrollY` or attaching `scroll`\n * listeners to `window` must migrate to the scroller element.\n * - `document.body { overflow: hidden }` no longer locks scroll;\n * body-scroll-lock libraries must target the scroller.\n * - Host modals at `position: fixed; inset: 0` are contained to\n * the outer wrapper rather than covering the full viewport \u2014\n * visually equivalent (the wrapper IS the safe-area box) but\n * `inset: 0` no longer covers the banner area.\n */\n/* Default state for the two wrapper layers: `display: contents` makes\n * the wrapper boxes disappear from layout entirely. Their children\n * participate in the GRANDPARENT's layout context (i.e. directly in the\n * `<body>`'s flex column) as if the wrapper elements didn't exist.\n * With no generated box there's also no containing block for fixed\n * descendants \u2014 host's `position: fixed; bottom: 0` resolves all the\n * way up to the viewport, exactly as it would without the SDK present.\n *\n * This is the ONLY state the wrapper takes in mobile WebView / iframe\n * hosts: the banners aren't mounted, the wrapper has no job, so it\n * collapses to a layout no-op. `html.eazo-host-web` (added by banner-ui\n * on mount, only in plain web) overrides BOTH layers below to their\n * full active styles. */\n.eazo-app-area,\n.eazo-app-area-scroller {\n display: contents;\n}\n\nhtml.eazo-host-web .eazo-app-area {\n display: block;\n position: fixed;\n inset: var(--eazo-handoff-top, 0px) 0 var(--eazo-handoff-bottom, 0px) 0;\n /* Containing block for fixed-positioned descendants \u2014 this is what\n * lets host's `position: fixed; bottom: 0` anchor to the wrapper\n * (between the banners) instead of to the viewport (under our banner).\n *\n * IMPORTANT: do not move `overflow: auto` onto this element. The\n * combination of transform + overflow makes fixed descendants scroll\n * with content. The scroll lives on `.eazo-app-area-scroller` below. */\n transform: translateZ(0);\n}\nhtml.eazo-host-web .eazo-app-area-scroller {\n display: block;\n /* Fill the outer wrapper exactly. `position: absolute` is the\n * cheapest way to do this \u2014 `width/height: 100%` plus margin/padding\n * inheritance can leak; `inset: 0` against the outer's padding box\n * is unambiguous. */\n position: absolute;\n inset: 0;\n /* This is the actual scroll container for host content. Crucially,\n * it does NOT have `transform` \u2014 so it is NOT a containing block\n * for host's `position: fixed` descendants. Those still resolve up\n * to `.eazo-app-area` and stay pinned to its edges, ignoring scroll. */\n overflow-x: hidden;\n overflow-y: auto;\n /* Disable rubber-band overscroll. The wrapper sits between two fixed\n * banners on its own compositor layer (via the outer's translateZ);\n * during native overscroll bounce the scroller's content briefly\n * translates beyond its padding box and the compositor briefly\n * reveals adjacent layers \u2014 the cream top banner above, the white\n * bottom banner below, the body's UA-default background everywhere\n * else \u2014 as a flash of \"other colors\" at the top/bottom edges.\n * `overscroll-behavior: none` keeps the scroll fully contained and\n * eliminates that visual seam, at the cost of the native bounce\n * gesture inside the wrapper (acceptable trade for the SDK's promo\n * surface, which is already constrained by the banner sandwich). */\n overscroll-behavior: none;\n}\n\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":"
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/internal/banner-ui/styles.ts"],"names":[],"mappings":"AAIA,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,4m4BAisBzB,CAAC;AAEF,wBAAgB,0BAA0B,IAAI,IAAI,CA0BjD"}
|