@mrrocket/sdk 0.12.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 (45) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +60 -0
  3. package/dist/browser/mrrocket-flows.js +11 -2
  4. package/dist/browser/mrrocket-messenger.js +143 -65
  5. package/dist/browser/mrrocket.js +1 -1
  6. package/dist/browser.d.ts +1 -1
  7. package/dist/browser.js +2 -1
  8. package/dist/browser.js.map +1 -1
  9. package/dist/client.d.ts +12 -0
  10. package/dist/client.js +22 -0
  11. package/dist/client.js.map +1 -1
  12. package/dist/context.d.ts +3 -0
  13. package/dist/context.js.map +1 -1
  14. package/dist/core.d.ts +4 -1
  15. package/dist/core.js +1 -1
  16. package/dist/core.js.map +1 -1
  17. package/dist/flows-ui.d.ts +6 -4
  18. package/dist/flows-ui.js +99 -4
  19. package/dist/flows-ui.js.map +1 -1
  20. package/dist/index.d.ts +2 -1
  21. package/dist/index.js +4 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/messenger-ui.d.ts +22 -0
  24. package/dist/messenger-ui.js +313 -79
  25. package/dist/messenger-ui.js.map +1 -1
  26. package/dist/paywall.d.ts +123 -0
  27. package/dist/paywall.js +131 -0
  28. package/dist/paywall.js.map +1 -0
  29. package/dist/proxy.d.ts +1 -0
  30. package/dist/proxy.js +3 -0
  31. package/dist/proxy.js.map +1 -1
  32. package/dist/react-native/index.d.cts +134 -3
  33. package/dist/react-native/index.d.ts +134 -3
  34. package/dist/react-native/index.js +204 -4
  35. package/dist/react-native/index.js.map +1 -1
  36. package/dist/security.d.ts +71 -0
  37. package/dist/security.js +73 -0
  38. package/dist/security.js.map +1 -0
  39. package/dist/server.d.ts +1 -0
  40. package/dist/server.js +8 -1
  41. package/dist/server.js.map +1 -1
  42. package/dist/styles-css.d.ts +1 -1
  43. package/dist/styles-css.js +141 -63
  44. package/dist/styles-css.js.map +1 -1
  45. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Attack reports (0.13.0), from your app's server only: failed sign-ins, requests that look like an attack, and refused
3
+ * requests (401/403). MRRocket's Build agent watches them and puts a possible attack into Needs you as Important.
4
+ * They need the product's server key (`MRROCKET_SERVER_KEY`, MRRocket › Settings › Products › Sign-in), so nobody can
5
+ * send a false alarm from a browser. The IP and the account name you pass are hashed by MRRocket on arrival (a hash
6
+ * that changes every day) and never stored as sent. Runs on Node, the Edge runtime and Cloudflare Workers (only `fetch`).
7
+ *
8
+ * Every helper returns a promise that never throws; in a proxy or a route hand it to `waitUntil` so the response does
9
+ * not wait for it:
10
+ *
11
+ * // proxy.ts (middleware)
12
+ * export function proxy(request: NextRequest, event: NextFetchEvent) {
13
+ * const report = watchRequest(request);
14
+ * if (report) event.waitUntil(report);
15
+ * return NextResponse.next();
16
+ * }
17
+ *
18
+ * // where a sign-in fails
19
+ * await reportFailedLogin({ userId: user?.id, account: email, ip: request.headers.get("x-forwarded-for") });
20
+ */
21
+ export interface SecurityOptions {
22
+ /** Defaults to `NEXT_PUBLIC_MRROCKET_KEY`. */
23
+ apiKey?: string;
24
+ /** Defaults to `MRROCKET_SERVER_KEY`. */
25
+ serverKey?: string;
26
+ /** Defaults to `NEXT_PUBLIC_MRROCKET_HOST`. */
27
+ host?: string;
28
+ }
29
+ /** What a request looks like an attempt at; the same patterns MRRocket reads (src/data/security.ts there), smaller. */
30
+ export type AttackKind = "sqli" | "xss" | "traversal" | "scanner";
31
+ /** The attack a path and query look like, or null. Only reads the URL, sends nothing. */
32
+ export declare const attackIn: (path: string, query?: string) => AttackKind | null;
33
+ /** The address a request came from, as the hosts name it. */
34
+ export declare const ipOf: (headers: {
35
+ get(name: string): string | null;
36
+ }) => string | undefined;
37
+ /** A sign-in that failed. `userId` when the account exists, `account` the name typed in (hashed by MRRocket). */
38
+ export declare const reportFailedLogin: (a: {
39
+ userId?: string | null;
40
+ account?: string | null;
41
+ ip?: string | null;
42
+ reason?: string;
43
+ }, o?: SecurityOptions) => Promise<boolean>;
44
+ /** The app answered 401 or 403. A spike over the week's normal becomes a signal. */
45
+ export declare const reportAuthDenied: (a: {
46
+ status: 401 | 403 | number;
47
+ path: string;
48
+ ip?: string | null;
49
+ userId?: string | null;
50
+ }, o?: SecurityOptions) => Promise<boolean>;
51
+ /** A request you judged suspicious yourself (a 404 on a weak-spot path, a blocked payload). */
52
+ export declare const reportSuspiciousRequest: (a: {
53
+ path: string;
54
+ query?: string;
55
+ method?: string;
56
+ ip?: string | null;
57
+ reason?: AttackKind | string;
58
+ status?: number;
59
+ }, o?: SecurityOptions) => Promise<boolean>;
60
+ /**
61
+ * For `proxy.ts` (middleware): reads the request's path and query and, only when they look like an attack (SQL
62
+ * injection, XSS, path traversal, a scanner's path like `/.env`), reports it. Returns the report's promise, or null for
63
+ * an ordinary request (nothing is sent then). Pass the promise to `event.waitUntil`.
64
+ */
65
+ export declare const watchRequest: (request: {
66
+ url: string;
67
+ method?: string;
68
+ headers: {
69
+ get(name: string): string | null;
70
+ };
71
+ }, o?: SecurityOptions) => Promise<boolean> | null;
@@ -0,0 +1,73 @@
1
+ // src/security.ts
2
+ import { envKey, resolveHost } from "./core.js";
3
+ var SCANNER = /^\/(wp-admin|wp-login\.php|wp-content|wp-includes|xmlrpc\.php|\.env|\.git(\/|$)|\.svn|\.hg|\.aws|\.ssh|\.DS_Store|phpmyadmin|pma\/|myadmin|adminer|phpinfo|server-status|actuator|vendor\/phpunit|cgi-bin|boaform|HNAP1|owa\/|config\.(php|json|yml)|web\.config|backup\.(sql|zip|tar)|dump\.sql|.*\.(php|asp|aspx|jsp|cgi)$)/i;
4
+ var SQLI = /('|")\s*(or|and)\s+('|")?\w*('|")?\s*=\s*('|")?\w|\bor\s+1\s*=\s*1\b|union(\s|\/\*.*?\*\/)+(all\s+)?select\b|\b(pg_sleep|sleep|benchmark|waitfor\s+delay)\s*\(|;\s*(drop|delete|truncate|insert|update)\s+\w|information_schema|xp_cmdshell|('|")\s*(--|#)/i;
5
+ var XSS = /<\s*\/?\s*script\b|javascript\s*:|\bon(error|load|mouseover|focus|toggle)\s*=|document\.cookie|\balert\s*\(|<\s*iframe\b/i;
6
+ var TRAVERSAL = /(\.\.[/\\]){2,}|\/etc\/(passwd|shadow|hosts)|\/proc\/self|c:\\windows|\0/i;
7
+ var decode = (s) => {
8
+ let out = s.replace(/\+/g, " ");
9
+ for (let i = 0; i < 2; i++)
10
+ try {
11
+ out = decodeURIComponent(out);
12
+ } catch {
13
+ break;
14
+ }
15
+ return out;
16
+ };
17
+ var attackIn = (path, query = "") => {
18
+ const p = decode(path);
19
+ const q = decode(query);
20
+ const both = `${p}?${q}`;
21
+ if (SQLI.test(both)) return "sqli";
22
+ if (XSS.test(both)) return "xss";
23
+ if (TRAVERSAL.test(both)) return "traversal";
24
+ return SCANNER.test(p) ? "scanner" : null;
25
+ };
26
+ var serverKeyOf = (o) => {
27
+ if (o.serverKey) return o.serverKey;
28
+ try {
29
+ return typeof process !== "undefined" ? process.env.MRROCKET_SERVER_KEY : void 0;
30
+ } catch {
31
+ return void 0;
32
+ }
33
+ };
34
+ var ipOf = (headers) => (headers.get("cf-connecting-ip") ?? headers.get("x-real-ip") ?? headers.get("x-forwarded-for")?.split(",")[0] ?? "").trim() || void 0;
35
+ var send = async (event, props, o = {}) => {
36
+ const key = o.apiKey ?? envKey();
37
+ const secret = serverKeyOf(o);
38
+ if (!key || !secret) return false;
39
+ try {
40
+ const res = await fetch(`${resolveHost(o.host)}/api/sdk/events`, {
41
+ method: "POST",
42
+ headers: { "content-type": "application/json", authorization: `Bearer ${secret}` },
43
+ body: JSON.stringify({ key, server: true, events: [{ event, distinctId: typeof props.userId === "string" && props.userId ? props.userId : "$security", ts: Date.now(), props }] })
44
+ });
45
+ return res.ok;
46
+ } catch {
47
+ return false;
48
+ }
49
+ };
50
+ var reportFailedLogin = (a, o) => send("$login_failed", { userId: a.userId ?? void 0, account: a.account ?? void 0, ip: a.ip ?? void 0, reason: a.reason }, o);
51
+ var reportAuthDenied = (a, o) => send("$auth_denied", { status: a.status, path: a.path, ip: a.ip ?? void 0, userId: a.userId ?? void 0 }, o);
52
+ var reportSuspiciousRequest = (a, o) => send("$suspicious_request", { path: a.path, query: a.query, method: a.method, ip: a.ip ?? void 0, reason: a.reason, status: a.status }, o);
53
+ var watchRequest = (request, o) => {
54
+ let url;
55
+ try {
56
+ url = new URL(request.url);
57
+ } catch {
58
+ return null;
59
+ }
60
+ const query = url.search.slice(1);
61
+ const reason = attackIn(url.pathname, query);
62
+ if (!reason) return null;
63
+ return reportSuspiciousRequest({ path: url.pathname, query, method: request.method, ip: ipOf(request.headers), reason }, o);
64
+ };
65
+ export {
66
+ attackIn,
67
+ ipOf,
68
+ reportAuthDenied,
69
+ reportFailedLogin,
70
+ reportSuspiciousRequest,
71
+ watchRequest
72
+ };
73
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/security.ts"],"sourcesContent":["import { envKey, resolveHost } from \"./core\";\n\n/**\n * Attack reports (0.13.0), from your app's server only: failed sign-ins, requests that look like an attack, and refused\n * requests (401/403). MRRocket's Build agent watches them and puts a possible attack into Needs you as Important.\n * They need the product's server key (`MRROCKET_SERVER_KEY`, MRRocket › Settings › Products › Sign-in), so nobody can\n * send a false alarm from a browser. The IP and the account name you pass are hashed by MRRocket on arrival (a hash\n * that changes every day) and never stored as sent. Runs on Node, the Edge runtime and Cloudflare Workers (only `fetch`).\n *\n * Every helper returns a promise that never throws; in a proxy or a route hand it to `waitUntil` so the response does\n * not wait for it:\n *\n * // proxy.ts (middleware)\n * export function proxy(request: NextRequest, event: NextFetchEvent) {\n * const report = watchRequest(request);\n * if (report) event.waitUntil(report);\n * return NextResponse.next();\n * }\n *\n * // where a sign-in fails\n * await reportFailedLogin({ userId: user?.id, account: email, ip: request.headers.get(\"x-forwarded-for\") });\n */\n\nexport interface SecurityOptions {\n /** Defaults to `NEXT_PUBLIC_MRROCKET_KEY`. */\n apiKey?: string;\n /** Defaults to `MRROCKET_SERVER_KEY`. */\n serverKey?: string;\n /** Defaults to `NEXT_PUBLIC_MRROCKET_HOST`. */\n host?: string;\n}\n\n/** What a request looks like an attempt at; the same patterns MRRocket reads (src/data/security.ts there), smaller. */\nexport type AttackKind = \"sqli\" | \"xss\" | \"traversal\" | \"scanner\";\n\nconst SCANNER =\n /^\\/(wp-admin|wp-login\\.php|wp-content|wp-includes|xmlrpc\\.php|\\.env|\\.git(\\/|$)|\\.svn|\\.hg|\\.aws|\\.ssh|\\.DS_Store|phpmyadmin|pma\\/|myadmin|adminer|phpinfo|server-status|actuator|vendor\\/phpunit|cgi-bin|boaform|HNAP1|owa\\/|config\\.(php|json|yml)|web\\.config|backup\\.(sql|zip|tar)|dump\\.sql|.*\\.(php|asp|aspx|jsp|cgi)$)/i;\nconst SQLI = /('|\")\\s*(or|and)\\s+('|\")?\\w*('|\")?\\s*=\\s*('|\")?\\w|\\bor\\s+1\\s*=\\s*1\\b|union(\\s|\\/\\*.*?\\*\\/)+(all\\s+)?select\\b|\\b(pg_sleep|sleep|benchmark|waitfor\\s+delay)\\s*\\(|;\\s*(drop|delete|truncate|insert|update)\\s+\\w|information_schema|xp_cmdshell|('|\")\\s*(--|#)/i;\nconst XSS = /<\\s*\\/?\\s*script\\b|javascript\\s*:|\\bon(error|load|mouseover|focus|toggle)\\s*=|document\\.cookie|\\balert\\s*\\(|<\\s*iframe\\b/i;\nconst TRAVERSAL = /(\\.\\.[/\\\\]){2,}|\\/etc\\/(passwd|shadow|hosts)|\\/proc\\/self|c:\\\\windows|\\0/i;\n\nconst decode = (s: string) => {\n let out = s.replace(/\\+/g, \" \");\n for (let i = 0; i < 2; i++)\n try {\n out = decodeURIComponent(out);\n } catch {\n break;\n }\n return out;\n};\n\n/** The attack a path and query look like, or null. Only reads the URL, sends nothing. */\nexport const attackIn = (path: string, query = \"\"): AttackKind | null => {\n const p = decode(path);\n const q = decode(query);\n const both = `${p}?${q}`;\n if (SQLI.test(both)) return \"sqli\";\n if (XSS.test(both)) return \"xss\";\n if (TRAVERSAL.test(both)) return \"traversal\";\n return SCANNER.test(p) ? \"scanner\" : null;\n};\n\nconst serverKeyOf = (o: SecurityOptions) => {\n if (o.serverKey) return o.serverKey;\n try {\n return typeof process !== \"undefined\" ? process.env.MRROCKET_SERVER_KEY : undefined;\n } catch {\n return undefined;\n }\n};\n\n/** The address a request came from, as the hosts name it. */\nexport const ipOf = (headers: { get(name: string): string | null }) =>\n (headers.get(\"cf-connecting-ip\") ?? headers.get(\"x-real-ip\") ?? headers.get(\"x-forwarded-for\")?.split(\",\")[0] ?? \"\").trim() || undefined;\n\nconst send = async (event: string, props: Record<string, unknown>, o: SecurityOptions = {}): Promise<boolean> => {\n const key = o.apiKey ?? envKey();\n const secret = serverKeyOf(o);\n if (!key || !secret) return false;\n try {\n const res = await fetch(`${resolveHost(o.host)}/api/sdk/events`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", authorization: `Bearer ${secret}` },\n body: JSON.stringify({ key, server: true, events: [{ event, distinctId: typeof props.userId === \"string\" && props.userId ? props.userId : \"$security\", ts: Date.now(), props }] }),\n });\n return res.ok;\n } catch {\n return false;\n }\n};\n\n/** A sign-in that failed. `userId` when the account exists, `account` the name typed in (hashed by MRRocket). */\nexport const reportFailedLogin = (a: { userId?: string | null; account?: string | null; ip?: string | null; reason?: string }, o?: SecurityOptions) =>\n send(\"$login_failed\", { userId: a.userId ?? undefined, account: a.account ?? undefined, ip: a.ip ?? undefined, reason: a.reason }, o);\n\n/** The app answered 401 or 403. A spike over the week's normal becomes a signal. */\nexport const reportAuthDenied = (a: { status: 401 | 403 | number; path: string; ip?: string | null; userId?: string | null }, o?: SecurityOptions) =>\n send(\"$auth_denied\", { status: a.status, path: a.path, ip: a.ip ?? undefined, userId: a.userId ?? undefined }, o);\n\n/** A request you judged suspicious yourself (a 404 on a weak-spot path, a blocked payload). */\nexport const reportSuspiciousRequest = (a: { path: string; query?: string; method?: string; ip?: string | null; reason?: AttackKind | string; status?: number }, o?: SecurityOptions) =>\n send(\"$suspicious_request\", { path: a.path, query: a.query, method: a.method, ip: a.ip ?? undefined, reason: a.reason, status: a.status }, o);\n\n/**\n * For `proxy.ts` (middleware): reads the request's path and query and, only when they look like an attack (SQL\n * injection, XSS, path traversal, a scanner's path like `/.env`), reports it. Returns the report's promise, or null for\n * an ordinary request (nothing is sent then). Pass the promise to `event.waitUntil`.\n */\nexport const watchRequest = (request: { url: string; method?: string; headers: { get(name: string): string | null } }, o?: SecurityOptions): Promise<boolean> | null => {\n let url: URL;\n try {\n url = new URL(request.url);\n } catch {\n return null;\n }\n const query = url.search.slice(1);\n const reason = attackIn(url.pathname, query);\n if (!reason) return null;\n return reportSuspiciousRequest({ path: url.pathname, query, method: request.method, ip: ipOf(request.headers), reason }, o);\n};\n"],"mappings":";AAAA,SAAS,QAAQ,mBAAmB;AAmCpC,IAAM,UACF;AACJ,IAAM,OAAO;AACb,IAAM,MAAM;AACZ,IAAM,YAAY;AAElB,IAAM,SAAS,CAAC,MAAc;AAC1B,MAAI,MAAM,EAAE,QAAQ,OAAO,GAAG;AAC9B,WAAS,IAAI,GAAG,IAAI,GAAG;AACnB,QAAI;AACA,YAAM,mBAAmB,GAAG;AAAA,IAChC,QAAQ;AACJ;AAAA,IACJ;AACJ,SAAO;AACX;AAGO,IAAM,WAAW,CAAC,MAAc,QAAQ,OAA0B;AACrE,QAAM,IAAI,OAAO,IAAI;AACrB,QAAM,IAAI,OAAO,KAAK;AACtB,QAAM,OAAO,GAAG,CAAC,IAAI,CAAC;AACtB,MAAI,KAAK,KAAK,IAAI,EAAG,QAAO;AAC5B,MAAI,IAAI,KAAK,IAAI,EAAG,QAAO;AAC3B,MAAI,UAAU,KAAK,IAAI,EAAG,QAAO;AACjC,SAAO,QAAQ,KAAK,CAAC,IAAI,YAAY;AACzC;AAEA,IAAM,cAAc,CAAC,MAAuB;AACxC,MAAI,EAAE,UAAW,QAAO,EAAE;AAC1B,MAAI;AACA,WAAO,OAAO,YAAY,cAAc,QAAQ,IAAI,sBAAsB;AAAA,EAC9E,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAGO,IAAM,OAAO,CAAC,aAChB,QAAQ,IAAI,kBAAkB,KAAK,QAAQ,IAAI,WAAW,KAAK,QAAQ,IAAI,iBAAiB,GAAG,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,KAAK;AAEnI,IAAM,OAAO,OAAO,OAAe,OAAgC,IAAqB,CAAC,MAAwB;AAC7G,QAAM,MAAM,EAAE,UAAU,OAAO;AAC/B,QAAM,SAAS,YAAY,CAAC;AAC5B,MAAI,CAAC,OAAO,CAAC,OAAQ,QAAO;AAC5B,MAAI;AACA,UAAM,MAAM,MAAM,MAAM,GAAG,YAAY,EAAE,IAAI,CAAC,mBAAmB;AAAA,MAC7D,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,eAAe,UAAU,MAAM,GAAG;AAAA,MACjF,MAAM,KAAK,UAAU,EAAE,KAAK,QAAQ,MAAM,QAAQ,CAAC,EAAE,OAAO,YAAY,OAAO,MAAM,WAAW,YAAY,MAAM,SAAS,MAAM,SAAS,aAAa,IAAI,KAAK,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC;AAAA,IACrL,CAAC;AACD,WAAO,IAAI;AAAA,EACf,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAGO,IAAM,oBAAoB,CAAC,GAA6F,MAC3H,KAAK,iBAAiB,EAAE,QAAQ,EAAE,UAAU,QAAW,SAAS,EAAE,WAAW,QAAW,IAAI,EAAE,MAAM,QAAW,QAAQ,EAAE,OAAO,GAAG,CAAC;AAGjI,IAAM,mBAAmB,CAAC,GAA6F,MAC1H,KAAK,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,MAAM,EAAE,MAAM,IAAI,EAAE,MAAM,QAAW,QAAQ,EAAE,UAAU,OAAU,GAAG,CAAC;AAG7G,IAAM,0BAA0B,CAAC,GAAyH,MAC7J,KAAK,uBAAuB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,OAAO,QAAQ,EAAE,QAAQ,IAAI,EAAE,MAAM,QAAW,QAAQ,EAAE,QAAQ,QAAQ,EAAE,OAAO,GAAG,CAAC;AAOzI,IAAM,eAAe,CAAC,SAA0F,MAAiD;AACpK,MAAI;AACJ,MAAI;AACA,UAAM,IAAI,IAAI,QAAQ,GAAG;AAAA,EAC7B,QAAQ;AACJ,WAAO;AAAA,EACX;AACA,QAAM,QAAQ,IAAI,OAAO,MAAM,CAAC;AAChC,QAAM,SAAS,SAAS,IAAI,UAAU,KAAK;AAC3C,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,wBAAwB,EAAE,MAAM,IAAI,UAAU,OAAO,QAAQ,QAAQ,QAAQ,IAAI,KAAK,QAAQ,OAAO,GAAG,OAAO,GAAG,CAAC;AAC9H;","names":[]}
package/dist/server.d.ts CHANGED
@@ -55,5 +55,6 @@ export declare const getFlagVariant: (key: string, { config, cookies, distinctId
55
55
  /** Every text change for this request, to render `<Text>` defaults server-side. */
56
56
  export declare const getTexts: ({ config, searchParams, cookies, distinctId }: RequestInfo) => Record<string, string>;
57
57
  export * from "./core.js";
58
+ export { type AttackKind, type SecurityOptions, attackIn, ipOf, reportAuthDenied, reportFailedLogin, reportSuspiciousRequest, watchRequest } from "./security.js";
58
59
  /** Affiliate referrals on the server: read the `mrr_ref` cookie in the route that creates a Stripe Checkout Session. */
59
60
  export { type CheckoutReferral, type Referral, REFERRAL_COOKIE, readReferralCookie, referralForCheckout, referralLink } from "./referral.js";
package/dist/server.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/server.ts
2
2
  import { EMBED_PARAM, ID_COOKIE, PREVIEW_PARAM, envKey, flagFor, parsePreview, resolveHost, resolveVariant, textChanges } from "./core.js";
3
3
  export * from "./core.js";
4
+ import { attackIn, ipOf, reportAuthDenied, reportFailedLogin, reportSuspiciousRequest, watchRequest } from "./security.js";
4
5
  import { REFERRAL_COOKIE, readReferralCookie, referralForCheckout, referralLink } from "./referral.js";
5
6
  var CONFIG_TIMEOUT_MS = 2500;
6
7
  var getCockpitConfig = async ({ apiKey, host, timeoutMs = CONFIG_TIMEOUT_MS } = {}) => {
@@ -36,15 +37,21 @@ var getFlagVariant = (key, { config, cookies, distinctId, person }) => {
36
37
  var getTexts = ({ config, searchParams, cookies, distinctId }) => textChanges(config, distinctId ?? distinctIdFrom(cookies), previewFrom(searchParams).preview);
37
38
  export {
38
39
  REFERRAL_COOKIE,
40
+ attackIn,
39
41
  distinctIdFrom,
40
42
  getCockpitConfig,
41
43
  getFlag,
42
44
  getFlagVariant,
43
45
  getTexts,
44
46
  getVariant,
47
+ ipOf,
45
48
  previewFrom,
46
49
  readReferralCookie,
47
50
  referralForCheckout,
48
- referralLink
51
+ referralLink,
52
+ reportAuthDenied,
53
+ reportFailedLogin,
54
+ reportSuspiciousRequest,
55
+ watchRequest
49
56
  };
50
57
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts"],"sourcesContent":["import { type CockpitConfig, EMBED_PARAM, type FlagPerson, ID_COOKIE, PREVIEW_PARAM, type VariantId, envKey, flagFor, parsePreview, resolveHost, resolveVariant, textChanges } from \"./core\";\n\n/**\n * Server helpers for the App Router: load the experiments once per request, then render the right variant on the\n * first paint (no flicker) and hand the same config to `<CockpitProvider config>` so the browser agrees.\n */\n\ntype SearchParams = Record<string, string | string[] | undefined>;\ninterface CookieReader {\n get(name: string): { value: string } | undefined;\n}\n\n/** How long the server waits for MRRocket before it renders with control. */\nconst CONFIG_TIMEOUT_MS = 2500;\n\n/**\n * The experiments for a public key. `null` when MRRocket cannot be reached, answers with an error or is slower than\n * `timeoutMs`: everyone then sees control and the page still renders. Runs on Node, the Edge runtime and Cloudflare\n * Workers (only `fetch` and `AbortController`).\n *\n * `apiKey` and `host` default to `NEXT_PUBLIC_MRROCKET_KEY` and `NEXT_PUBLIC_MRROCKET_HOST`.\n */\nexport const getCockpitConfig = async ({ apiKey, host, timeoutMs = CONFIG_TIMEOUT_MS }: { apiKey?: string; host?: string; timeoutMs?: number } = {}): Promise<CockpitConfig | null> => {\n const key = apiKey ?? envKey();\n if (!key) return null;\n const ctl = new AbortController();\n const timer = setTimeout(() => ctl.abort(), timeoutMs);\n try {\n // `next.revalidate: 0` keeps this request uncached in Next.js. Not `cache: \"no-store\"`: Cloudflare Workers\n // throws on the `cache` field of a fetch unless a compatibility flag is on, and every config would be null.\n const res = await fetch(`${resolveHost(host)}/api/sdk/config?key=${encodeURIComponent(key)}`, {\n signal: ctl.signal,\n next: { revalidate: 0 },\n } as RequestInit);\n if (!res.ok) return null;\n const config = (await res.json()) as CockpitConfig | null;\n return config && Array.isArray(config.experiments) ? config : null;\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n};\n\n/** Forced variants and the embed flag from a page's search params, for previews in Cockpit. */\nexport const previewFrom = (searchParams: SearchParams | undefined) => {\n const embed = searchParams?.[EMBED_PARAM];\n return { preview: parsePreview(searchParams?.[PREVIEW_PARAM]), embedded: (Array.isArray(embed) ? embed[0] : embed) === \"1\" };\n};\n\n/** The visitor's id from the `ck_id` cookie, if the browser has one yet (only in consent \"cookies\" mode). */\nexport const distinctIdFrom = (cookies: CookieReader | undefined) => cookies?.get(ID_COOKIE)?.value ?? \"anonymous\";\n\n/**\n * Who the visitor is for this request. Pass `distinctId` (your signed-in user id, the same you give\n * `<CockpitProvider distinctId>`) when there is no `ck_id` cookie, as in the default cookieless mode.\n */\ninterface RequestInfo {\n config: CockpitConfig | null;\n searchParams?: SearchParams;\n cookies?: CookieReader;\n distinctId?: string;\n}\n\n/** The variant of one experiment for this request. */\nexport const getVariant = (key: string, { config, searchParams, cookies, distinctId }: RequestInfo): VariantId =>\n resolveVariant(key, config, distinctId ?? distinctIdFrom(cookies), previewFrom(searchParams).preview);\n\n/**\n * A feature flag for this request: true while it is on for the visitor, false while it is off or unknown (or MRRocket\n * could not be reached). `person` takes the properties the flag's audience reads (\"Email\", \"Plan\" …), as you pass them\n * to identify(). For a multivariate flag use `getFlagVariant`.\n */\nexport const getFlag = (key: string, { config, cookies, distinctId, person }: RequestInfo & { person?: FlagPerson }): boolean =>\n flagFor(key, config, distinctId ?? distinctIdFrom(cookies), person) !== false;\n\n/** The variant of a multivariate flag for this request, or null while it is off. */\nexport const getFlagVariant = (key: string, { config, cookies, distinctId, person }: RequestInfo & { person?: FlagPerson }): string | null => {\n const v = flagFor(key, config, distinctId ?? distinctIdFrom(cookies), person);\n return typeof v === \"string\" ? v : null;\n};\n\n/** Every text change for this request, to render `<Text>` defaults server-side. */\nexport const getTexts = ({ config, searchParams, cookies, distinctId }: RequestInfo) =>\n textChanges(config, distinctId ?? distinctIdFrom(cookies), previewFrom(searchParams).preview);\n\nexport * from \"./core\";\n/** Affiliate referrals on the server: read the `mrr_ref` cookie in the route that creates a Stripe Checkout Session. */\nexport { type CheckoutReferral, type Referral, REFERRAL_COOKIE, readReferralCookie, referralForCheckout, referralLink } from \"./referral\";\n"],"mappings":";AAAA,SAA6B,aAA8B,WAAW,eAA+B,QAAQ,SAAS,cAAc,aAAa,gBAAgB,mBAAmB;AAsFpL,cAAc;AAEd,SAA+C,iBAAiB,oBAAoB,qBAAqB,oBAAoB;AA3E7H,IAAM,oBAAoB;AASnB,IAAM,mBAAmB,OAAO,EAAE,QAAQ,MAAM,YAAY,kBAAkB,IAA4D,CAAC,MAAqC;AACnL,QAAM,MAAM,UAAU,OAAO;AAC7B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,MAAM,IAAI,gBAAgB;AAChC,QAAM,QAAQ,WAAW,MAAM,IAAI,MAAM,GAAG,SAAS;AACrD,MAAI;AAGA,UAAM,MAAM,MAAM,MAAM,GAAG,YAAY,IAAI,CAAC,uBAAuB,mBAAmB,GAAG,CAAC,IAAI;AAAA,MAC1F,QAAQ,IAAI;AAAA,MACZ,MAAM,EAAE,YAAY,EAAE;AAAA,IAC1B,CAAgB;AAChB,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,SAAU,MAAM,IAAI,KAAK;AAC/B,WAAO,UAAU,MAAM,QAAQ,OAAO,WAAW,IAAI,SAAS;AAAA,EAClE,QAAQ;AACJ,WAAO;AAAA,EACX,UAAE;AACE,iBAAa,KAAK;AAAA,EACtB;AACJ;AAGO,IAAM,cAAc,CAAC,iBAA2C;AACnE,QAAM,QAAQ,eAAe,WAAW;AACxC,SAAO,EAAE,SAAS,aAAa,eAAe,aAAa,CAAC,GAAG,WAAW,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI,WAAW,IAAI;AAC/H;AAGO,IAAM,iBAAiB,CAAC,YAAsC,SAAS,IAAI,SAAS,GAAG,SAAS;AAchG,IAAM,aAAa,CAAC,KAAa,EAAE,QAAQ,cAAc,SAAS,WAAW,MAChF,eAAe,KAAK,QAAQ,cAAc,eAAe,OAAO,GAAG,YAAY,YAAY,EAAE,OAAO;AAOjG,IAAM,UAAU,CAAC,KAAa,EAAE,QAAQ,SAAS,YAAY,OAAO,MACvE,QAAQ,KAAK,QAAQ,cAAc,eAAe,OAAO,GAAG,MAAM,MAAM;AAGrE,IAAM,iBAAiB,CAAC,KAAa,EAAE,QAAQ,SAAS,YAAY,OAAO,MAA4D;AAC1I,QAAM,IAAI,QAAQ,KAAK,QAAQ,cAAc,eAAe,OAAO,GAAG,MAAM;AAC5E,SAAO,OAAO,MAAM,WAAW,IAAI;AACvC;AAGO,IAAM,WAAW,CAAC,EAAE,QAAQ,cAAc,SAAS,WAAW,MACjE,YAAY,QAAQ,cAAc,eAAe,OAAO,GAAG,YAAY,YAAY,EAAE,OAAO;","names":[]}
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["import { type CockpitConfig, EMBED_PARAM, type FlagPerson, ID_COOKIE, PREVIEW_PARAM, type VariantId, envKey, flagFor, parsePreview, resolveHost, resolveVariant, textChanges } from \"./core\";\n\n/**\n * Server helpers for the App Router: load the experiments once per request, then render the right variant on the\n * first paint (no flicker) and hand the same config to `<CockpitProvider config>` so the browser agrees.\n */\n\ntype SearchParams = Record<string, string | string[] | undefined>;\ninterface CookieReader {\n get(name: string): { value: string } | undefined;\n}\n\n/** How long the server waits for MRRocket before it renders with control. */\nconst CONFIG_TIMEOUT_MS = 2500;\n\n/**\n * The experiments for a public key. `null` when MRRocket cannot be reached, answers with an error or is slower than\n * `timeoutMs`: everyone then sees control and the page still renders. Runs on Node, the Edge runtime and Cloudflare\n * Workers (only `fetch` and `AbortController`).\n *\n * `apiKey` and `host` default to `NEXT_PUBLIC_MRROCKET_KEY` and `NEXT_PUBLIC_MRROCKET_HOST`.\n */\nexport const getCockpitConfig = async ({ apiKey, host, timeoutMs = CONFIG_TIMEOUT_MS }: { apiKey?: string; host?: string; timeoutMs?: number } = {}): Promise<CockpitConfig | null> => {\n const key = apiKey ?? envKey();\n if (!key) return null;\n const ctl = new AbortController();\n const timer = setTimeout(() => ctl.abort(), timeoutMs);\n try {\n // `next.revalidate: 0` keeps this request uncached in Next.js. Not `cache: \"no-store\"`: Cloudflare Workers\n // throws on the `cache` field of a fetch unless a compatibility flag is on, and every config would be null.\n const res = await fetch(`${resolveHost(host)}/api/sdk/config?key=${encodeURIComponent(key)}`, {\n signal: ctl.signal,\n next: { revalidate: 0 },\n } as RequestInit);\n if (!res.ok) return null;\n const config = (await res.json()) as CockpitConfig | null;\n return config && Array.isArray(config.experiments) ? config : null;\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n};\n\n/** Forced variants and the embed flag from a page's search params, for previews in Cockpit. */\nexport const previewFrom = (searchParams: SearchParams | undefined) => {\n const embed = searchParams?.[EMBED_PARAM];\n return { preview: parsePreview(searchParams?.[PREVIEW_PARAM]), embedded: (Array.isArray(embed) ? embed[0] : embed) === \"1\" };\n};\n\n/** The visitor's id from the `ck_id` cookie, if the browser has one yet (only in consent \"cookies\" mode). */\nexport const distinctIdFrom = (cookies: CookieReader | undefined) => cookies?.get(ID_COOKIE)?.value ?? \"anonymous\";\n\n/**\n * Who the visitor is for this request. Pass `distinctId` (your signed-in user id, the same you give\n * `<CockpitProvider distinctId>`) when there is no `ck_id` cookie, as in the default cookieless mode.\n */\ninterface RequestInfo {\n config: CockpitConfig | null;\n searchParams?: SearchParams;\n cookies?: CookieReader;\n distinctId?: string;\n}\n\n/** The variant of one experiment for this request. */\nexport const getVariant = (key: string, { config, searchParams, cookies, distinctId }: RequestInfo): VariantId =>\n resolveVariant(key, config, distinctId ?? distinctIdFrom(cookies), previewFrom(searchParams).preview);\n\n/**\n * A feature flag for this request: true while it is on for the visitor, false while it is off or unknown (or MRRocket\n * could not be reached). `person` takes the properties the flag's audience reads (\"Email\", \"Plan\" …), as you pass them\n * to identify(). For a multivariate flag use `getFlagVariant`.\n */\nexport const getFlag = (key: string, { config, cookies, distinctId, person }: RequestInfo & { person?: FlagPerson }): boolean =>\n flagFor(key, config, distinctId ?? distinctIdFrom(cookies), person) !== false;\n\n/** The variant of a multivariate flag for this request, or null while it is off. */\nexport const getFlagVariant = (key: string, { config, cookies, distinctId, person }: RequestInfo & { person?: FlagPerson }): string | null => {\n const v = flagFor(key, config, distinctId ?? distinctIdFrom(cookies), person);\n return typeof v === \"string\" ? v : null;\n};\n\n/** Every text change for this request, to render `<Text>` defaults server-side. */\nexport const getTexts = ({ config, searchParams, cookies, distinctId }: RequestInfo) =>\n textChanges(config, distinctId ?? distinctIdFrom(cookies), previewFrom(searchParams).preview);\n\nexport * from \"./core\";\nexport { type AttackKind, type SecurityOptions, attackIn, ipOf, reportAuthDenied, reportFailedLogin, reportSuspiciousRequest, watchRequest } from \"./security\";\n/** Affiliate referrals on the server: read the `mrr_ref` cookie in the route that creates a Stripe Checkout Session. */\nexport { type CheckoutReferral, type Referral, REFERRAL_COOKIE, readReferralCookie, referralForCheckout, referralLink } from \"./referral\";\n"],"mappings":";AAAA,SAA6B,aAA8B,WAAW,eAA+B,QAAQ,SAAS,cAAc,aAAa,gBAAgB,mBAAmB;AAsFpL,cAAc;AACd,SAAgD,UAAU,MAAM,kBAAkB,mBAAmB,yBAAyB,oBAAoB;AAElJ,SAA+C,iBAAiB,oBAAoB,qBAAqB,oBAAoB;AA5E7H,IAAM,oBAAoB;AASnB,IAAM,mBAAmB,OAAO,EAAE,QAAQ,MAAM,YAAY,kBAAkB,IAA4D,CAAC,MAAqC;AACnL,QAAM,MAAM,UAAU,OAAO;AAC7B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,MAAM,IAAI,gBAAgB;AAChC,QAAM,QAAQ,WAAW,MAAM,IAAI,MAAM,GAAG,SAAS;AACrD,MAAI;AAGA,UAAM,MAAM,MAAM,MAAM,GAAG,YAAY,IAAI,CAAC,uBAAuB,mBAAmB,GAAG,CAAC,IAAI;AAAA,MAC1F,QAAQ,IAAI;AAAA,MACZ,MAAM,EAAE,YAAY,EAAE;AAAA,IAC1B,CAAgB;AAChB,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,SAAU,MAAM,IAAI,KAAK;AAC/B,WAAO,UAAU,MAAM,QAAQ,OAAO,WAAW,IAAI,SAAS;AAAA,EAClE,QAAQ;AACJ,WAAO;AAAA,EACX,UAAE;AACE,iBAAa,KAAK;AAAA,EACtB;AACJ;AAGO,IAAM,cAAc,CAAC,iBAA2C;AACnE,QAAM,QAAQ,eAAe,WAAW;AACxC,SAAO,EAAE,SAAS,aAAa,eAAe,aAAa,CAAC,GAAG,WAAW,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI,WAAW,IAAI;AAC/H;AAGO,IAAM,iBAAiB,CAAC,YAAsC,SAAS,IAAI,SAAS,GAAG,SAAS;AAchG,IAAM,aAAa,CAAC,KAAa,EAAE,QAAQ,cAAc,SAAS,WAAW,MAChF,eAAe,KAAK,QAAQ,cAAc,eAAe,OAAO,GAAG,YAAY,YAAY,EAAE,OAAO;AAOjG,IAAM,UAAU,CAAC,KAAa,EAAE,QAAQ,SAAS,YAAY,OAAO,MACvE,QAAQ,KAAK,QAAQ,cAAc,eAAe,OAAO,GAAG,MAAM,MAAM;AAGrE,IAAM,iBAAiB,CAAC,KAAa,EAAE,QAAQ,SAAS,YAAY,OAAO,MAA4D;AAC1I,QAAM,IAAI,QAAQ,KAAK,QAAQ,cAAc,eAAe,OAAO,GAAG,MAAM;AAC5E,SAAO,OAAO,MAAM,WAAW,IAAI;AACvC;AAGO,IAAM,WAAW,CAAC,EAAE,QAAQ,cAAc,SAAS,WAAW,MACjE,YAAY,QAAQ,cAAc,eAAe,OAAO,GAAG,YAAY,YAAY,EAAE,OAAO;","names":[]}
@@ -1,6 +1,6 @@
1
1
  /** The widgets' stylesheet as a string, without React, so the script tag's widgets (src/script) inject the same CSS. */
2
2
  export declare const STYLE_ID = "ckw-styles";
3
3
  /** The one stylesheet for every Cockpit widget. Everything is scoped under `.ckw-root` and prefixed `ckw-`. */
4
- export declare const CSS = "\n.ckw-root{--ckw-accent:#3f76ff;--ckw-accent-ink:#fff;--ckw-bg:#fff;--ckw-bg2:#f7f7f8;--ckw-bg3:#eeeff1;--ckw-fg:#1c1d20;--ckw-fg2:#5d6068;--ckw-fg3:#8b8e96;--ckw-line:#e4e5e8;--ckw-danger:#d92d20;--ckw-shadow:0 8px 30px rgba(16,18,24,.16);--ckw-card:#fff;--ckw-col:#f5f6f7;--ckw-lift:0 1px 2px rgba(16,18,24,.06),0 0 0 1px rgba(16,18,24,.07);--ckw-raise:0 0 0 1px rgba(16,18,24,.05),0 1px 2px rgba(16,18,24,.05),0 6px 18px rgba(16,18,24,.06);--ckw-float:0 0 0 1px rgba(16,18,24,.06),0 18px 50px rgba(16,18,24,.2);\nfont-size:14px;line-height:1.45;font-family:inherit;color:var(--ckw-fg);box-sizing:border-box;-webkit-font-smoothing:antialiased}\n.ckw-root[data-theme=dark]{--ckw-bg:#111214;--ckw-bg2:#1a1b1f;--ckw-bg3:#26272c;--ckw-fg:#f1f2f4;--ckw-fg2:#a9acb3;--ckw-fg3:#7a7d86;--ckw-line:#2a2b31;--ckw-shadow:0 8px 30px rgba(0,0,0,.5);--ckw-card:#1b1c20;--ckw-col:#16171a;--ckw-lift:0 0 0 1px rgba(255,255,255,.07);--ckw-raise:0 0 0 1px rgba(255,255,255,.07);--ckw-float:0 0 0 1px rgba(255,255,255,.09),0 18px 50px rgba(0,0,0,.55)}\n@media (prefers-color-scheme:dark){.ckw-root[data-theme=auto]{--ckw-bg:#111214;--ckw-bg2:#1a1b1f;--ckw-bg3:#26272c;--ckw-fg:#f1f2f4;--ckw-fg2:#a9acb3;--ckw-fg3:#7a7d86;--ckw-line:#2a2b31;--ckw-shadow:0 8px 30px rgba(0,0,0,.5);--ckw-card:#1b1c20;--ckw-col:#16171a;--ckw-lift:0 0 0 1px rgba(255,255,255,.07);--ckw-raise:0 0 0 1px rgba(255,255,255,.07);--ckw-float:0 0 0 1px rgba(255,255,255,.09),0 18px 50px rgba(0,0,0,.55)}}\n.ckw-root *,.ckw-root *::before,.ckw-root *::after{box-sizing:border-box}\n:where(.ckw-root) :where(button,input,select,textarea){font:inherit;color:inherit;margin:0}\n.ckw-root button{cursor:pointer}\n.ckw-root :focus-visible{outline:2px solid var(--ckw-accent);outline-offset:2px}\n.ckw-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}\n.ckw-title{font-size:20px;font-weight:600;margin:0;letter-spacing:-.01em}\n.ckw-sub{color:var(--ckw-fg2);margin:2px 0 0;font-size:13px}\n.ckw-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 12px;border-radius:8px;border:1px solid var(--ckw-line);background:var(--ckw-bg);font-weight:500;font-size:13px;white-space:nowrap}\n.ckw-btn:hover{background:var(--ckw-bg2)}\n.ckw-btn[disabled]{opacity:.55;cursor:not-allowed}\n.ckw-btn-primary{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-btn-primary:hover{background:var(--ckw-accent);filter:brightness(1.08)}\n.ckw-btn-ghost{border-color:transparent;background:transparent;color:var(--ckw-fg2)}\n.ckw-icon{width:16px;height:16px;flex:none;display:block}\n.ckw-tabs{display:flex;gap:4px;border-bottom:1px solid var(--ckw-line);margin-bottom:12px;overflow-x:auto}\n.ckw-tab{background:none;border:0;border-bottom:2px solid transparent;padding:8px 10px;margin-bottom:-1px;color:var(--ckw-fg2);font-weight:500;font-size:13px;white-space:nowrap}\n.ckw-tab[aria-selected=true]{color:var(--ckw-fg);border-bottom-color:var(--ckw-accent)}\n.ckw-tools{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:10px}\n.ckw-input,.ckw-select,.ckw-textarea{width:100%;border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg);padding:7px 10px;font-size:14px;min-height:34px}\n.ckw-input::placeholder,.ckw-textarea::placeholder{color:var(--ckw-fg3)}\n.ckw-textarea{resize:vertical;min-height:72px}\n.ckw-search{flex:1 1 160px;min-width:0}\n.ckw-seg{display:inline-flex;border:1px solid var(--ckw-line);border-radius:8px;overflow:hidden}\n.ckw-seg button{border:0;background:var(--ckw-bg);padding:0 10px;height:32px;font-size:13px;color:var(--ckw-fg2)}\n.ckw-seg button[aria-pressed=true]{background:var(--ckw-bg3);color:var(--ckw-fg);font-weight:500}\n.ckw-chips{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}\n.ckw-chip{border:1px solid var(--ckw-line);background:var(--ckw-bg);border-radius:999px;padding:3px 10px;font-size:12px;color:var(--ckw-fg2)}\n.ckw-chip[aria-pressed=true],.ckw-chip[aria-checked=true]{background:var(--ckw-fg);color:var(--ckw-bg);border-color:var(--ckw-fg)}\n.ckw-list{list-style:none;margin:0;padding:0;border:1px solid var(--ckw-line);border-radius:8px;overflow:hidden}\n.ckw-list>li+li{border-top:1px solid var(--ckw-line)}\n.ckw-row{display:flex;gap:12px;align-items:flex-start;padding:10px 12px;background:var(--ckw-bg)}\n.ckw-row:hover{background:var(--ckw-bg2)}\n.ckw-vote{flex:none;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;width:44px;min-height:48px;border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;padding:4px 0;color:var(--ckw-fg2)}\n.ckw-vote:hover{border-color:var(--ckw-accent);color:var(--ckw-accent)}\n.ckw-vote[aria-pressed=true]{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-vote-sm{width:auto;min-height:0;flex-direction:row;gap:4px;padding:2px 8px}\n.ckw-main{flex:1;min-width:0;text-align:left}\n.ckw-link{display:block;width:100%;text-align:left;background:none;border:0;padding:0;font-weight:600;font-size:14px;color:var(--ckw-fg);overflow-wrap:anywhere}\n.ckw-link:hover{color:var(--ckw-accent)}\n.ckw-excerpt{margin:2px 0 0;color:var(--ckw-fg2);font-size:13px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}\n.ckw-meta{display:flex;gap:8px;align-items:center;margin-top:6px;color:var(--ckw-fg3);font-size:12px;flex-wrap:wrap}\n.ckw-pill{display:inline-flex;align-items:center;border-radius:999px;padding:1px 8px;font-size:11px;font-weight:600;background:var(--ckw-bg3);color:var(--ckw-fg2)}\n.ckw-pill[data-s=planned]{background:rgba(63,118,255,.14);color:#3f76ff}\n.ckw-pill[data-s=progress]{background:rgba(245,158,11,.16);color:#b45309}\n.ckw-pill[data-s=shipped]{background:rgba(22,163,74,.15);color:#15803d}\n.ckw-root[data-theme=dark] .ckw-pill[data-s=progress]{color:#fbbf24}\n.ckw-root[data-theme=dark] .ckw-pill[data-s=shipped]{color:#4ade80}\n@media (prefers-color-scheme:dark){.ckw-root[data-theme=auto] .ckw-pill[data-s=progress]{color:#fbbf24}.ckw-root[data-theme=auto] .ckw-pill[data-s=shipped]{color:#4ade80}}\n.ckw-stat{display:inline-flex;align-items:center;gap:4px}\n.ckw-empty{padding:64px 16px;text-align:center;color:var(--ckw-fg2)}\n.ckw-empty-icon{display:block;width:56px;height:56px;margin:0 auto 14px;opacity:.3}\n.ckw-error{border:1px solid color-mix(in srgb,var(--ckw-danger) 40%,transparent);background:color-mix(in srgb,var(--ckw-danger) 8%,transparent);color:var(--ckw-danger);border-radius:8px;padding:8px 10px;font-size:13px;display:flex;gap:8px;align-items:center;justify-content:space-between;margin-bottom:10px}\n.ckw-skel{height:68px;border-radius:8px;background:var(--ckw-bg2);animation:ckw-sh 1.2s ease-in-out infinite;margin-bottom:6px}\n@keyframes ckw-sh{50%{opacity:.5}}\n@media (prefers-reduced-motion:reduce){.ckw-skel,.ckw-scrim,.ckw-modal{animation:none}.ckw-root *{transition:none!important}}\n.ckw-form{display:flex;flex-direction:column;gap:10px;border:1px solid var(--ckw-line);border-radius:8px;padding:12px;margin-bottom:12px;background:var(--ckw-bg)}\n.ckw-field{display:flex;flex-direction:column;gap:4px}\n.ckw-label{font-size:12px;font-weight:600;color:var(--ckw-fg2)}\n.ckw-actions{display:flex;gap:8px;justify-content:flex-end}\n.ckw-dupes{border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg2);padding:8px 10px;font-size:13px}\n.ckw-dupes p{margin:0 0 4px;color:var(--ckw-fg2);font-size:12px}\n.ckw-dupe{display:flex;gap:8px;align-items:center;justify-content:space-between;padding:3px 0}\n.ckw-dupe span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-team{margin-left:6px;background:var(--ckw-accent);color:var(--ckw-accent-ink);border-radius:4px;padding:0 5px;font-size:10px;font-weight:700;text-transform:uppercase;vertical-align:1px}\n.ckw-time{color:var(--ckw-fg3);font-size:12px;margin-left:6px}\n.ckw-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n/* Board */\n.ckw-board{width:100%;max-width:1160px;margin:0 auto;background:var(--ckw-bg);min-width:0}\n.ckw-board.ckw-full{max-width:none;min-height:100%;min-height:100dvh}\n.ckw-board.ckw-full>.ckw-body-wrap{max-width:1160px;margin:0 auto;padding:0 16px 56px}\n.ckw-top{position:sticky;top:0;z-index:5;background:color-mix(in srgb,var(--ckw-bg) 88%,transparent);backdrop-filter:saturate(1.4) blur(10px);-webkit-backdrop-filter:saturate(1.4) blur(10px);border-bottom:1px solid var(--ckw-line);margin-bottom:24px}\n.ckw-top-row{max-width:1160px;margin:0 auto;padding:10px 16px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}\n.ckw-brand{display:inline-flex;align-items:center;gap:10px;font-weight:650;font-size:15px;color:var(--ckw-fg);background:none;border:0;padding:4px 2px;border-radius:8px;min-width:0}\n.ckw-brand span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:220px}\n.ckw-brand img{width:28px;height:28px;border-radius:7px;object-fit:cover;outline:1px solid oklch(0 0 0/.1);outline-offset:-1px;flex:none}\n.ckw-root[data-theme=dark] .ckw-brand img{outline-color:oklch(1 0 0/.1)}\n.ckw-brand-mark{width:28px;height:28px;border-radius:7px;background:var(--ckw-accent);color:var(--ckw-accent-ink);display:inline-flex;align-items:center;justify-content:center;font-size:14px;flex:none}\n.ckw-nav{display:flex;gap:2px;flex:1;min-width:0}\n.ckw-nav-tab{background:none;border:0;border-radius:8px;padding:0 12px;height:34px;color:var(--ckw-fg2);font-weight:550;font-size:14px;white-space:nowrap;transition:background-color .1s linear,color .1s linear}\n.ckw-nav-tab:hover{color:var(--ckw-fg);background:var(--ckw-bg2)}\n.ckw-nav-tab[aria-selected=true]{color:var(--ckw-fg);background:var(--ckw-bg3)}\n.ckw-top-tools{display:flex;align-items:center;gap:6px;margin-left:auto}\n.ckw-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:8px;border:0;background:none;color:var(--ckw-fg2);flex:none}\n.ckw-icon-btn:hover{background:var(--ckw-bg2);color:var(--ckw-fg)}\n.ckw-searchbox{display:flex;align-items:center;border-radius:8px;transition:background-color .1s linear}\n.ckw-search-input{width:0;padding:0;border:0;background:none;outline:none;font-size:14px;height:34px;transition:width .15s cubic-bezier(.2,0,0,1)}\n.ckw-searchbox[data-open]{background:var(--ckw-bg2)}\n.ckw-searchbox[data-open] .ckw-search-input{width:180px;padding-right:10px}\n.ckw-searchbox:focus-within{outline:2px solid var(--ckw-accent);outline-offset:0}\n.ckw-root .ckw-search-input:focus-visible{outline:none}\n.ckw-search-input::-webkit-search-cancel-button{display:none}\n.ckw-submit{height:34px}\n.ckw-btn,.ckw-vote,.ckw-upvote,.ckw-icon-btn{transition-property:background-color,border-color,color,scale;transition-duration:.1s;transition-timing-function:linear}\n.ckw-btn:active:not([disabled]),.ckw-vote:active,.ckw-upvote:active,.ckw-icon-btn:active{scale:.96}\n.ckw-intro{margin:0 0 20px}\n.ckw-intro h1{font-size:24px;font-weight:650;letter-spacing:-.02em;margin:0;text-wrap:balance}\n.ckw-intro p{margin:4px 0 0;color:var(--ckw-fg2);font-size:14px;text-wrap:pretty}\n.ckw-note{display:flex;align-items:center;justify-content:space-between;gap:8px;border-radius:10px;padding:6px 6px 6px 12px;margin-bottom:14px;background:color-mix(in srgb,var(--ckw-accent) 10%,transparent);color:var(--ckw-fg);font-size:13px;font-weight:500}\n.ckw-tools .ckw-chips-inline{margin:0}\n.ckw-posts{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}\n.ckw-post{position:relative;display:flex;gap:14px;align-items:flex-start;padding:14px 16px;border-radius:12px;background:var(--ckw-card);box-shadow:var(--ckw-lift);transition:box-shadow .1s linear}\n.ckw-post:hover{box-shadow:var(--ckw-lift),0 4px 14px rgba(16,18,24,.06)}\n.ckw-post .ckw-vote,.ckw-card .ckw-vote{position:relative;z-index:1}\n.ckw-stretch::after{content:\"\";position:absolute;inset:0;border-radius:inherit}\n.ckw-post .ckw-link,.ckw-card .ckw-link{font-size:15px;font-weight:600;text-wrap:pretty}\n.ckw-dot-sep{color:var(--ckw-fg3)}\n.ckw-type{display:inline-flex;align-items:center;gap:5px;border-radius:6px;padding:1px 7px;font-size:11.5px;font-weight:600;color:var(--ckw-fg2);background:var(--ckw-bg2);white-space:nowrap;width:max-content}\n.ckw-type::before{content:\"\";width:6px;height:6px;border-radius:50%;background:var(--ckw-fg3)}\n.ckw-type[data-tone=blue]::before{background:#3f76ff}\n.ckw-type[data-tone=red]::before{background:#ef4444}\n.ckw-type[data-tone=violet]::before{background:#8b5cf6}\n.ckw-type[data-tone=green]::before{background:#22c55e}\n.ckw-av{width:22px;height:22px;border-radius:50%;flex:none;display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:650;background:var(--ckw-bg3);color:var(--ckw-fg2)}\n.ckw-av[data-team]{background:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-count{font-weight:500;color:var(--ckw-fg3);font-variant-numeric:tabular-nums;margin-left:2px}\n.ckw-empty p{margin:0 0 12px}\n.ckw-empty p:only-child{margin:0}\n/* Roadmap */\n.ckw-cols{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;align-items:start}\n.ckw-col{background:var(--ckw-col);border-radius:14px;padding:6px;min-width:0}\n.ckw-col-head{padding:8px 8px 10px}\n.ckw-col-head h3{display:flex;align-items:center;gap:8px;margin:0;font-size:14px;font-weight:650}\n.ckw-col-head p{margin:2px 0 0 16px;font-size:12px;color:var(--ckw-fg3)}\n.ckw-sdot{width:8px;height:8px;border-radius:50%;background:var(--ckw-fg3);flex:none}\n.ckw-sdot[data-s=planned]{background:#3f76ff}\n.ckw-sdot[data-s=progress]{background:#f59e0b}\n.ckw-sdot[data-s=shipped]{background:#22c55e}\n.ckw-col-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}\n.ckw-col-empty{margin:0;padding:24px 8px;text-align:center;font-size:12px;color:var(--ckw-fg3)}\n.ckw-col .ckw-skel{margin:0 0 6px;height:96px;background:var(--ckw-card)}\n.ckw-card{position:relative;display:flex;flex-direction:column;gap:6px;padding:12px;border-radius:8px;background:var(--ckw-card);box-shadow:var(--ckw-lift);transition:box-shadow .1s linear}\n.ckw-card:hover{box-shadow:var(--ckw-lift),0 4px 14px rgba(16,18,24,.07)}\n.ckw-card .ckw-link{font-size:14px}\n.ckw-card .ckw-excerpt{margin:0}\n.ckw-card-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:4px}\n.ckw-who{display:inline-flex;align-items:center;gap:6px;min-width:0;font-size:12px;color:var(--ckw-fg2)}\n.ckw-who span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-vote-sm{height:28px;border-radius:7px;font-size:12px}\n@media (max-width:900px){.ckw-cols{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;margin:0 -16px;padding:0 16px 8px;scroll-padding:0 16px}.ckw-col{flex:0 0 min(82%,300px);scroll-snap-align:start}}\n/* Post page */\n.ckw-back{display:inline-flex;align-items:center;gap:4px;background:none;border:0;padding:6px 8px 6px 4px;margin:0 0 12px -4px;border-radius:8px;color:var(--ckw-fg2);font-size:13px;font-weight:550}\n.ckw-back:hover{color:var(--ckw-fg);background:var(--ckw-bg2)}\n.ckw-back-icon{display:inline-block;transform:rotate(-90deg);line-height:0}\n.ckw-detail-grid{display:grid;grid-template-columns:minmax(0,1fr) 260px;gap:32px;align-items:start}\n.ckw-detail-title{margin:0 0 8px;font-size:22px;font-weight:650;letter-spacing:-.015em;overflow-wrap:anywhere;text-wrap:balance}\n.ckw-byline{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--ckw-fg2)}\n.ckw-body{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--ckw-fg);margin:16px 0 0;font-size:14.5px;line-height:1.6;text-wrap:pretty}\n.ckw-h{display:flex;align-items:center;gap:6px;font-size:14px;font-weight:650;margin:28px 0 10px;padding-top:20px;border-top:1px solid var(--ckw-line)}\n.ckw-composer{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}\n.ckw-composer .ckw-actions{align-items:center}\n.ckw-as{margin:0 auto 0 0}\n.ckw-comment{display:flex;gap:10px;padding:12px 0;border-top:1px solid var(--ckw-line)}\n.ckw-comment b{font-size:13px}\n.ckw-comment p{margin:3px 0 0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px}\n.ckw-side{position:sticky;top:76px;display:flex;flex-direction:column;gap:12px;padding:14px;border-radius:12px;background:var(--ckw-card);box-shadow:var(--ckw-lift)}\n.ckw-upvote{display:flex;align-items:center;gap:8px;height:40px;padding:0 12px;border-radius:8px;border:1px solid var(--ckw-line);background:var(--ckw-bg);font-weight:600;font-size:14px;color:var(--ckw-fg)}\n.ckw-upvote b{margin-left:auto;font-variant-numeric:tabular-nums}\n.ckw-upvote:hover{border-color:var(--ckw-accent);color:var(--ckw-accent)}\n.ckw-upvote[aria-pressed=true]{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-facts{margin:0;display:flex;flex-direction:column;gap:10px;font-size:13px}\n.ckw-facts div{display:flex;justify-content:space-between;align-items:center;gap:8px}\n.ckw-facts dt{color:var(--ckw-fg3)}\n.ckw-facts dd{margin:0;color:var(--ckw-fg)}\n@media (max-width:760px){.ckw-detail-grid{grid-template-columns:1fr;gap:16px}.ckw-side{position:static;order:-1}}\n/* Changelog */\n.ckw-log{max-width:820px}\n.ckw-entry{display:grid;grid-template-columns:160px minmax(0,1fr);gap:24px;padding:20px 0;border-top:1px solid var(--ckw-line)}\n.ckw-entry:first-child{border-top:0;padding-top:4px}\n.ckw-entry time{font-size:13px;color:var(--ckw-fg3);padding-top:2px}\n.ckw-entry h3{font-size:17px;font-weight:650;margin:0 0 6px;text-wrap:balance}\n.ckw-entry p{margin:0;color:var(--ckw-fg2);white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.6}\n@media (max-width:640px){.ckw-entry{grid-template-columns:1fr;gap:4px}}\n/* Dialog */\n.ckw-scrim{position:fixed;inset:0;z-index:2147483002;background:rgba(0,0,0,.36);display:flex;align-items:center;justify-content:center;padding:16px;animation:ckw-fade .15s ease-out}\n.ckw-modal{width:100%;max-width:520px;max-height:calc(100dvh - 32px);overflow-y:auto;background:var(--ckw-bg);color:var(--ckw-fg);border-radius:14px;box-shadow:var(--ckw-shadow),0 0 0 1px var(--ckw-line);animation:ckw-in .18s ease-out}\n.ckw-modal-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 10px 4px 18px}\n.ckw-modal-head h2{margin:0;font-size:16px;font-weight:650}\n.ckw-modal-body{display:flex;flex-direction:column;gap:12px;padding:8px 18px 18px}\n@keyframes ckw-fade{from{opacity:0}}\n@media (max-width:520px){.ckw-scrim{align-items:flex-end;padding:0}.ckw-modal{max-width:none;border-radius:14px 14px 0 0;max-height:92dvh}}\n.ckw-pair{display:grid;grid-template-columns:1fr 1fr;gap:10px}\n@media (max-width:480px){.ckw-pair{grid-template-columns:1fr}}\n.ckw-fine{margin:-4px 0 0;font-size:12px;color:var(--ckw-fg3);text-wrap:pretty}\n.ckw-opt-l{font-weight:500;color:var(--ckw-fg3)}\n.ckw-foot{text-align:center;font-size:12px;color:var(--ckw-fg3);margin:0;padding:0 16px 24px}\n.ckw-linkish{background:none;border:0;padding:0;color:var(--ckw-fg2);text-decoration:underline;font-size:12px}\n@media (max-width:720px){.ckw-top-row{gap:8px;padding:8px 12px}.ckw-nav{order:3;flex-basis:100%;overflow-x:auto;margin:0 -4px}.ckw-submit span{display:none}.ckw-submit{width:34px;padding:0}.ckw-searchbox[data-open] .ckw-search-input{width:120px}.ckw-top{margin-bottom:16px}.ckw-board.ckw-full>.ckw-body-wrap{padding:0 12px 40px}.ckw-post{padding:12px}.ckw-intro h1{font-size:20px}}\n/* Floating widget */\n.ckw-fab{position:fixed;z-index:2147483000;bottom:20px;display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 16px;border-radius:999px;border:0;background:var(--ckw-accent);color:var(--ckw-accent-ink);font-weight:600;font-size:14px;box-shadow:var(--ckw-shadow)}\n.ckw-fab[data-pos=bottom-right],.ckw-pop[data-pos=bottom-right]{right:20px}\n.ckw-fab[data-pos=bottom-left],.ckw-pop[data-pos=bottom-left]{left:20px}\n.ckw-pop{position:fixed;z-index:2147483001;bottom:70px;width:380px;max-width:calc(100vw - 24px);max-height:min(560px,calc(100vh - 90px));display:flex;flex-direction:column;background:var(--ckw-bg);border:1px solid var(--ckw-line);border-radius:12px;box-shadow:var(--ckw-shadow);overflow:hidden}\n.ckw-pop-head{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--ckw-line);font-weight:600}\n.ckw-pop-body{padding:12px;overflow-y:auto;flex:1}\n.ckw-pop-foot{padding:8px 12px;border-top:1px solid var(--ckw-line);display:flex;justify-content:space-between;align-items:center;font-size:13px}\n.ckw-pop-foot a{color:var(--ckw-accent);text-decoration:none;font-weight:500}\n.ckw-pop .ckw-list{border:0;border-radius:0}\n.ckw-pop .ckw-row{padding:8px 0}\n.ckw-pop .ckw-form{border:0;padding:0;margin:12px 0 0}\n@media (max-width:520px){.ckw-pop{left:0!important;right:0!important;bottom:0;width:100%;max-width:100%;border-radius:12px 12px 0 0;max-height:85vh}.ckw-fab{bottom:14px}.ckw-fab[data-pos=bottom-right]{right:14px}.ckw-fab[data-pos=bottom-left]{left:14px}}\n/* Messenger (messenger-ui.ts) */\n.ckw-msgr-fab{position:fixed;bottom:20px;height:48px;padding:0 18px;gap:8px;font-size:15px;box-shadow:0 2px 4px rgba(16,18,24,.12),0 8px 24px color-mix(in srgb,var(--ckw-accent) 34%,transparent);transition:transform .16s cubic-bezier(.2,.8,.2,1)}\n.ckw-msgr-fab:hover{transform:scale(1.05)}\n.ckw-msgr-fab:active{transform:scale(.96)}\n.ckw-msgr-round{width:56px;height:56px;padding:0;justify-content:center}\n.ckw-msgr-fab .ckw-turn{animation:ckw-turn .22s cubic-bezier(.2,.8,.2,1)}\n@keyframes ckw-turn{from{opacity:0;transform:rotate(-70deg) scale(.6)}}\n.ckw-msgr-count{position:absolute;top:-2px;right:-2px;min-width:20px;height:20px;padding:0 5px;border-radius:10px;background:var(--ckw-danger);color:#fff;font-size:11px;font-weight:700;line-height:20px;text-align:center;box-shadow:0 0 0 2px var(--ckw-bg);font-variant-numeric:tabular-nums}\n.ckw-msgr-av{width:36px;height:36px;border-radius:50%;flex:none;display:inline-flex;align-items:center;justify-content:center;background:var(--ckw-accent);color:var(--ckw-accent-ink);font-size:15px;font-weight:700;line-height:1}\n.ckw-msgr-av-sm{width:26px;height:26px;font-size:12px}\n.ckw-msgr-preview{position:fixed;z-index:2147483000;bottom:88px;width:320px;max-width:calc(100vw - 32px);animation:ckw-in .22s cubic-bezier(.2,.8,.2,1)}\n.ckw-msgr-preview[data-pos=bottom-right]{right:20px}\n.ckw-msgr-preview[data-pos=bottom-left]{left:20px}\n.ckw-msgr-preview-card{display:flex;align-items:flex-start;gap:12px;width:100%;text-align:left;padding:14px 16px;border:0;border-radius:18px;background:var(--ckw-bg);box-shadow:var(--ckw-float)}\n.ckw-msgr-preview-main{display:flex;flex-direction:column;gap:2px;min-width:0}\n.ckw-msgr-preview-who{font-size:12px;font-weight:600;color:var(--ckw-fg3)}\n.ckw-msgr-preview-text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;white-space:pre-wrap;overflow-wrap:anywhere}\n.ckw-msgr-preview-x{position:absolute;top:-8px;right:-8px;width:24px;height:24px;border-radius:50%;border:0;background:var(--ckw-bg);color:var(--ckw-fg2);display:flex;align-items:center;justify-content:center;padding:0;box-shadow:var(--ckw-lift)}\n.ckw-msgr{bottom:88px;width:400px;height:min(660px,calc(100vh - 108px));max-height:none;border:0;border-radius:20px;box-shadow:var(--ckw-float);animation:ckw-pop .24s cubic-bezier(.2,.8,.2,1);transform-origin:bottom right}\n.ckw-msgr[data-pos=bottom-left]{transform-origin:bottom left}\n@keyframes ckw-pop{from{opacity:0;transform:translateY(12px) scale(.96)}}\n.ckw-msgr-head{gap:10px;min-height:60px;padding:12px 12px 12px 16px;border-bottom:1px solid var(--ckw-line)}\n.ckw-msgr[data-view=home] .ckw-msgr-head{background:var(--ckw-accent);color:var(--ckw-accent-ink);border-bottom-color:transparent}\n.ckw-msgr[data-view=home] .ckw-msgr-av{background:var(--ckw-accent-ink);color:var(--ckw-accent)}\n.ckw-msgr[data-view=home] .ckw-msgr-x{color:inherit}\n.ckw-msgr[data-view=home] .ckw-msgr-x:hover{background:color-mix(in srgb,var(--ckw-accent-ink) 16%,transparent)}\n.ckw-msgr-head-t{flex:1}\n.ckw-msgr-x{width:32px;height:32px;padding:0;border-radius:10px}\n.ckw-msgr-brand{display:inline-flex;align-items:center;gap:10px;font-size:15px;font-weight:650;min-width:0}\n.ckw-msgr-brand span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-msgr-who{display:flex;align-items:center;gap:10px;min-width:0;flex:1}\n.ckw-msgr-who-t{display:flex;flex-direction:column;min-width:0;font-weight:400}\n.ckw-msgr-who-t b{font-size:15px;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-msgr-who-t span{font-size:12px;color:var(--ckw-fg3)}\n.ckw-msgr-body{display:flex;flex-direction:column;gap:12px;background:var(--ckw-bg2);padding:16px}\n.ckw-msgr[data-view=article] .ckw-msgr-body,.ckw-msgr[data-view=messages] .ckw-msgr-body{background:var(--ckw-bg)}\n.ckw-msgr-hero{margin:-16px -16px 0;padding:10px 22px 64px;background:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-msgr-hero+*{margin-top:-52px}\n.ckw-msgr-hello{margin:0;font-size:26px;font-weight:700;letter-spacing:-.02em;line-height:1.2;text-wrap:balance;overflow-wrap:anywhere}\n.ckw-msgr-card{position:relative;display:flex;align-items:center;gap:12px;width:100%;text-align:left;padding:14px 16px;border:0;border-radius:16px;background:var(--ckw-card);box-shadow:var(--ckw-raise);transition:box-shadow .12s linear}\nbutton.ckw-msgr-card:hover{box-shadow:var(--ckw-raise),0 0 0 1px color-mix(in srgb,var(--ckw-accent) 30%,transparent)}\n.ckw-msgr-card-col{flex-direction:column;align-items:stretch;gap:2px;padding:8px}\n.ckw-msgr-card-main{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}\n.ckw-msgr-card-main b{font-size:15px;font-weight:650}\n.ckw-msgr-card-sub{color:var(--ckw-fg2);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.ckw-msgr-card-label{font-size:12px;font-weight:600;color:var(--ckw-fg3)}\n.ckw-msgr-card-meta{font-size:12px;color:var(--ckw-fg3)}\n.ckw-msgr-recent{flex-direction:column;align-items:stretch;gap:10px}\n.ckw-msgr-recent-row{display:flex;align-items:center;gap:12px;min-width:0}\n.ckw-msgr-recent-text{color:var(--ckw-fg);font-size:14px}\n.ckw-msgr-card .ckw-icon{color:var(--ckw-fg3)}\n.ckw-msgr-go{width:34px;height:34px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--ckw-accent) 12%,transparent)}\n.ckw-msgr-card .ckw-msgr-go .ckw-icon{color:var(--ckw-accent)}\n.ckw-msgr-badge{min-width:20px;height:20px;padding:0 6px;border-radius:10px;background:var(--ckw-danger);color:#fff;font-size:12px;font-weight:700;line-height:20px;text-align:center}\n.ckw-msgr-search{display:flex;align-items:center;gap:8px;height:42px;padding:0 14px;border:0;border-radius:12px;background:var(--ckw-bg2);color:var(--ckw-accent);cursor:text}\n.ckw-msgr[data-view=help] .ckw-msgr-search{background:var(--ckw-card);box-shadow:var(--ckw-raise)}\n.ckw-msgr-card-col .ckw-msgr-search{margin-bottom:4px}\n.ckw-msgr-search-in{flex:1;min-width:0;border:0;background:transparent;outline:none;font-size:14px;font-weight:500;color:var(--ckw-fg)}\n.ckw-root .ckw-msgr-search-in:focus-visible{outline:none}\n.ckw-msgr-search-in::placeholder{color:var(--ckw-fg2)}\n.ckw-msgr-search:focus-within{box-shadow:0 0 0 2px color-mix(in srgb,var(--ckw-accent) 45%,transparent)}\n.ckw-msgr-art{display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:10px 8px;border:0;border-radius:10px;background:transparent;font-size:14px}\n.ckw-msgr-art:hover{background:var(--ckw-bg2)}\n.ckw-msgr-art-t{flex:1;min-width:0}\n.ckw-msgr-art .ckw-icon{color:var(--ckw-accent)}\n.ckw-msgr-skel{height:40px;margin:4px 0 0}\n.ckw-msgr-group{display:flex;flex-direction:column;gap:6px}\n.ckw-msgr-group-h{margin:4px 2px 0;font-size:12px;font-weight:600;color:var(--ckw-fg3)}\n.ckw-msgr-empty{display:flex;flex-direction:column;align-items:center;gap:10px;padding:28px 8px;text-align:center;color:var(--ckw-fg2)}\n.ckw-msgr-empty p{margin:0}\n.ckw-msgr-doc{display:flex;flex-direction:column;gap:6px}\n.ckw-msgr-doc-t{margin:0;font-size:20px;font-weight:700;letter-spacing:-.015em;line-height:1.3}\n.ckw-msgr-doc-c{margin:0;font-size:12px;color:var(--ckw-fg3)}\n.ckw-msgr-doc-b{margin-top:8px;font-size:14px;line-height:1.6;overflow-wrap:anywhere}\n.ckw-msgr-doc-b>*{margin:0 0 10px}\n.ckw-msgr-doc-b h2{font-size:16px;margin-top:16px}\n.ckw-msgr-doc-b h3{font-size:15px;margin-top:12px}\n.ckw-msgr-doc-b ul,.ckw-msgr-doc-b ol{padding-left:20px}\n.ckw-msgr-doc-b blockquote{border-left:3px solid var(--ckw-line);padding-left:10px;color:var(--ckw-fg2)}\n.ckw-msgr-doc-b pre{background:var(--ckw-bg2);border-radius:8px;padding:10px;overflow-x:auto;font-size:13px}\n.ckw-msgr-doc-b code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.92em}\n.ckw-msgr-doc-b a{color:var(--ckw-accent)}\n.ckw-msgr-doc-b hr{border:0;border-top:1px solid var(--ckw-line)}\n.ckw-msgr-rate{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;margin-top:8px;padding:12px;border-radius:12px;background:var(--ckw-bg2);font-size:13px;color:var(--ckw-fg2)}\n.ckw-msgr-rate-b{display:flex;gap:6px}\n.ckw-msgr-ask{margin-top:6px;width:100%;height:40px;border-radius:12px}\n.ckw-msgr-cite{display:inline-flex;align-items:center;gap:6px;max-width:100%;padding:7px 10px;border:1px solid var(--ckw-line);border-radius:12px;background:var(--ckw-bg);font-size:13px;text-align:left}\n.ckw-msgr-cite .ckw-icon{color:var(--ckw-accent)}\n.ckw-msgr-cite span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-msgr-tabs{display:flex;border-top:1px solid var(--ckw-line);background:var(--ckw-bg);padding:2px 8px}\n.ckw-msgr-tab{position:relative;flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;padding:8px 0 10px;border:0;background:none;color:var(--ckw-fg3);font-size:12px;font-weight:600;transition:color .1s linear}\n.ckw-msgr-tab:hover{color:var(--ckw-fg2)}\n.ckw-msgr-tab[aria-current=page]{color:var(--ckw-accent)}\n.ckw-msgr-tab .ckw-icon{width:20px;height:20px}\n.ckw-msgr-count-tab{top:4px;right:calc(50% - 24px)}\n.ckw-msgs{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}\n.ckw-msg{display:flex;flex-direction:column;align-items:flex-start;gap:4px;max-width:82%}\n.ckw-msg[data-from=me]{align-self:flex-end;align-items:flex-end}\n.ckw-bubble{padding:9px 14px;border-radius:18px 18px 18px 6px;background:var(--ckw-bg3);white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px;line-height:1.45;text-wrap:pretty}\n.ckw-msg[data-from=me] .ckw-bubble{background:var(--ckw-accent);color:var(--ckw-accent-ink);border-radius:18px 18px 6px 18px}\n.ckw-msg[aria-busy=true] .ckw-bubble{opacity:.7}\n.ckw-msg-meta{font-size:11px;color:var(--ckw-fg3);font-variant-numeric:tabular-nums;padding:0 6px}\n.ckw-msgr-note{margin:4px 0 0;text-align:center}\n.ckw-msgr-compose{display:flex;flex-direction:column;gap:8px;padding:10px 12px 12px;background:var(--ckw-bg)}\n.ckw-msgr-compose .ckw-input{border:0;border-radius:12px;background:var(--ckw-bg2);min-height:38px;padding:8px 14px}\n.ckw-msgr-row{display:flex;gap:6px;align-items:flex-end;padding:5px 5px 5px 16px;border:1px solid var(--ckw-line);border-radius:22px;background:var(--ckw-bg);transition:border-color .1s linear}\n.ckw-msgr-row:focus-within{border-color:var(--ckw-accent)}\n.ckw-msgr-input{flex:1;min-height:32px;max-height:120px;padding:6px 0;border:0;border-radius:0;background:transparent;resize:none;field-sizing:content}\n.ckw-root .ckw-msgr-input:focus-visible{outline:none}\n.ckw-msgr-send{width:32px;height:32px;padding:0;flex:none;display:flex;align-items:center;justify-content:center;border:0;border-radius:50%;background:var(--ckw-accent);color:var(--ckw-accent-ink);transition:opacity .1s linear}\n.ckw-msgr-send:disabled{background:var(--ckw-bg3);color:var(--ckw-fg3);cursor:default}\n.ckw-msgr-error{margin:0;font-size:12px;color:var(--ckw-danger)}\n.ckw-msgr .ckw-input[aria-invalid=true]{box-shadow:0 0 0 1px var(--ckw-danger)}\n@media (max-width:520px){.ckw-msgr{height:100dvh;bottom:0;border-radius:0}.ckw-msgr-preview{bottom:80px}}\n@media (prefers-reduced-motion:reduce){.ckw-msgr,.ckw-msgr-preview,.ckw-msgr-fab .ckw-turn{animation:none}.ckw-msgr-fab{transition:none}}\n/* Survey */\n.ckw-survey{position:fixed;z-index:2147483000;left:20px;bottom:20px;width:360px;max-width:calc(100vw - 24px);background:var(--ckw-bg);border:1px solid var(--ckw-line);border-radius:12px;box-shadow:var(--ckw-shadow);padding:14px;animation:ckw-in .2s ease-out}\n@keyframes ckw-in{from{opacity:0;transform:translateY(8px)}}\n@media (max-width:520px){.ckw-survey{left:0;right:0;bottom:0;width:100%;max-width:100%;border-radius:12px 12px 0 0}}\n.ckw-sv-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}\n.ckw-dots{display:flex;gap:4px}\n.ckw-dot{width:6px;height:6px;border-radius:50%;background:var(--ckw-bg3)}\n.ckw-dot[data-on=true]{background:var(--ckw-accent)}\n.ckw-q{font-weight:600;font-size:15px;margin:0 0 2px;border:0;padding:0}\n.ckw-root .ckw-q:focus-visible{outline:none}\n.ckw-qd{color:var(--ckw-fg2);font-size:13px;margin:0 0 10px}\n.ckw-scale{display:flex;gap:4px;flex-wrap:wrap}\n.ckw-num{flex:1 1 0;min-width:26px;height:34px;border:1px solid var(--ckw-line);background:var(--ckw-bg);border-radius:8px;font-size:13px;font-weight:600;padding:0}\n.ckw-num:hover{border-color:var(--ckw-accent)}\n.ckw-num[aria-pressed=true]{background:var(--ckw-accent);color:var(--ckw-accent-ink);border-color:var(--ckw-accent)}\n.ckw-root .ckw-star{background:none;border:0;padding:2px;color:var(--ckw-bg3)}\n.ckw-root .ckw-star[data-on=true]{color:#f5a524}\n.ckw-star svg{width:28px;height:28px}\n.ckw-lh{display:flex;justify-content:space-between;color:var(--ckw-fg3);font-size:11px;margin-top:4px}\n.ckw-opts{display:flex;flex-direction:column;gap:6px}\n.ckw-opt{display:flex;gap:8px;align-items:center;border:1px solid var(--ckw-line);border-radius:8px;padding:7px 10px;background:var(--ckw-bg);cursor:pointer}\n.ckw-opt:has(input:checked){border-color:var(--ckw-accent);background:color-mix(in srgb,var(--ckw-accent) 8%,transparent)}\n.ckw-opt input{accent-color:var(--ckw-accent)}\n.ckw-sv-foot{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-top:12px}\n.ckw-thanks{text-align:center;padding:14px 0;display:flex;flex-direction:column;align-items:center;gap:8px}\n.ckw-tick{width:32px;height:32px;border-radius:50%;background:var(--ckw-accent);color:var(--ckw-accent-ink);display:flex;align-items:center;justify-content:center}\n";
4
+ export declare const CSS = "\n.ckw-root{--ckw-accent:#3f76ff;--ckw-accent-ink:#fff;--ckw-bg:#fff;--ckw-bg2:#f7f7f8;--ckw-bg3:#eeeff1;--ckw-fg:#1c1d20;--ckw-fg2:#5d6068;--ckw-fg3:#8b8e96;--ckw-line:#e4e5e8;--ckw-danger:#d92d20;--ckw-shadow:0 8px 30px rgba(16,18,24,.16);--ckw-card:#fff;--ckw-col:#f5f6f7;--ckw-lift:0 1px 2px rgba(16,18,24,.06),0 0 0 1px rgba(16,18,24,.07);--ckw-raise:0 0 0 1px rgba(16,18,24,.05),0 1px 2px rgba(16,18,24,.05),0 6px 18px rgba(16,18,24,.06);--ckw-float:0 0 0 1px rgba(16,18,24,.06),0 18px 50px rgba(16,18,24,.2);\nfont-size:14px;line-height:1.45;font-family:inherit;color:var(--ckw-fg);box-sizing:border-box;-webkit-font-smoothing:antialiased}\n.ckw-root[data-theme=dark]{--ckw-bg:#111214;--ckw-bg2:#1a1b1f;--ckw-bg3:#26272c;--ckw-fg:#f1f2f4;--ckw-fg2:#a9acb3;--ckw-fg3:#7a7d86;--ckw-line:#2a2b31;--ckw-shadow:0 8px 30px rgba(0,0,0,.5);--ckw-card:#1b1c20;--ckw-col:#16171a;--ckw-lift:0 0 0 1px rgba(255,255,255,.07);--ckw-raise:0 0 0 1px rgba(255,255,255,.07);--ckw-float:0 0 0 1px rgba(255,255,255,.09),0 18px 50px rgba(0,0,0,.55)}\n@media (prefers-color-scheme:dark){.ckw-root[data-theme=auto]{--ckw-bg:#111214;--ckw-bg2:#1a1b1f;--ckw-bg3:#26272c;--ckw-fg:#f1f2f4;--ckw-fg2:#a9acb3;--ckw-fg3:#7a7d86;--ckw-line:#2a2b31;--ckw-shadow:0 8px 30px rgba(0,0,0,.5);--ckw-card:#1b1c20;--ckw-col:#16171a;--ckw-lift:0 0 0 1px rgba(255,255,255,.07);--ckw-raise:0 0 0 1px rgba(255,255,255,.07);--ckw-float:0 0 0 1px rgba(255,255,255,.09),0 18px 50px rgba(0,0,0,.55)}}\n.ckw-root *,.ckw-root *::before,.ckw-root *::after{box-sizing:border-box}\n:where(.ckw-root) :where(button,input,select,textarea){font:inherit;color:inherit;margin:0}\n.ckw-root button{cursor:pointer}\n.ckw-root :focus-visible{outline:2px solid var(--ckw-accent);outline-offset:2px}\n.ckw-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}\n.ckw-title{font-size:20px;font-weight:600;margin:0;letter-spacing:-.01em}\n.ckw-sub{color:var(--ckw-fg2);margin:2px 0 0;font-size:13px}\n.ckw-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 12px;border-radius:8px;border:1px solid var(--ckw-line);background:var(--ckw-bg);font-weight:500;font-size:13px;white-space:nowrap}\n.ckw-btn:hover{background:var(--ckw-bg2)}\n.ckw-btn[disabled]{opacity:.55;cursor:not-allowed}\n.ckw-btn-primary{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-btn-primary:hover{background:var(--ckw-accent);filter:brightness(1.08)}\n.ckw-btn-ghost{border-color:transparent;background:transparent;color:var(--ckw-fg2)}\n.ckw-icon{width:16px;height:16px;flex:none;display:block}\n.ckw-tabs{display:flex;gap:4px;border-bottom:1px solid var(--ckw-line);margin-bottom:12px;overflow-x:auto}\n.ckw-tab{background:none;border:0;border-bottom:2px solid transparent;padding:8px 10px;margin-bottom:-1px;color:var(--ckw-fg2);font-weight:500;font-size:13px;white-space:nowrap}\n.ckw-tab[aria-selected=true]{color:var(--ckw-fg);border-bottom-color:var(--ckw-accent)}\n.ckw-tools{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:10px}\n.ckw-input,.ckw-select,.ckw-textarea{width:100%;border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg);padding:7px 10px;font-size:14px;min-height:34px}\n.ckw-input::placeholder,.ckw-textarea::placeholder{color:var(--ckw-fg3)}\n.ckw-textarea{resize:vertical;min-height:72px}\n.ckw-search{flex:1 1 160px;min-width:0}\n.ckw-seg{display:inline-flex;border:1px solid var(--ckw-line);border-radius:8px;overflow:hidden}\n.ckw-seg button{border:0;background:var(--ckw-bg);padding:0 10px;height:32px;font-size:13px;color:var(--ckw-fg2)}\n.ckw-seg button[aria-pressed=true]{background:var(--ckw-bg3);color:var(--ckw-fg);font-weight:500}\n.ckw-chips{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}\n.ckw-chip{border:1px solid var(--ckw-line);background:var(--ckw-bg);border-radius:999px;padding:3px 10px;font-size:12px;color:var(--ckw-fg2)}\n.ckw-chip[aria-pressed=true],.ckw-chip[aria-checked=true]{background:var(--ckw-fg);color:var(--ckw-bg);border-color:var(--ckw-fg)}\n.ckw-list{list-style:none;margin:0;padding:0;border:1px solid var(--ckw-line);border-radius:8px;overflow:hidden}\n.ckw-list>li+li{border-top:1px solid var(--ckw-line)}\n.ckw-row{display:flex;gap:12px;align-items:flex-start;padding:10px 12px;background:var(--ckw-bg)}\n.ckw-row:hover{background:var(--ckw-bg2)}\n.ckw-vote{flex:none;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;width:44px;min-height:48px;border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;padding:4px 0;color:var(--ckw-fg2)}\n.ckw-vote:hover{border-color:var(--ckw-accent);color:var(--ckw-accent)}\n.ckw-vote[aria-pressed=true]{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-vote-sm{width:auto;min-height:0;flex-direction:row;gap:4px;padding:2px 8px}\n.ckw-main{flex:1;min-width:0;text-align:left}\n.ckw-link{display:block;width:100%;text-align:left;background:none;border:0;padding:0;font-weight:600;font-size:14px;color:var(--ckw-fg);overflow-wrap:anywhere}\n.ckw-link:hover{color:var(--ckw-accent)}\n.ckw-excerpt{margin:2px 0 0;color:var(--ckw-fg2);font-size:13px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}\n.ckw-meta{display:flex;gap:8px;align-items:center;margin-top:6px;color:var(--ckw-fg3);font-size:12px;flex-wrap:wrap}\n.ckw-pill{display:inline-flex;align-items:center;border-radius:999px;padding:1px 8px;font-size:11px;font-weight:600;background:var(--ckw-bg3);color:var(--ckw-fg2)}\n.ckw-pill[data-s=planned]{background:rgba(63,118,255,.14);color:#3f76ff}\n.ckw-pill[data-s=progress]{background:rgba(245,158,11,.16);color:#b45309}\n.ckw-pill[data-s=shipped]{background:rgba(22,163,74,.15);color:#15803d}\n.ckw-root[data-theme=dark] .ckw-pill[data-s=progress]{color:#fbbf24}\n.ckw-root[data-theme=dark] .ckw-pill[data-s=shipped]{color:#4ade80}\n@media (prefers-color-scheme:dark){.ckw-root[data-theme=auto] .ckw-pill[data-s=progress]{color:#fbbf24}.ckw-root[data-theme=auto] .ckw-pill[data-s=shipped]{color:#4ade80}}\n.ckw-stat{display:inline-flex;align-items:center;gap:4px}\n.ckw-empty{padding:64px 16px;text-align:center;color:var(--ckw-fg2)}\n.ckw-empty-icon{display:block;width:56px;height:56px;margin:0 auto 14px;opacity:.3}\n.ckw-error{border:1px solid color-mix(in srgb,var(--ckw-danger) 40%,transparent);background:color-mix(in srgb,var(--ckw-danger) 8%,transparent);color:var(--ckw-danger);border-radius:8px;padding:8px 10px;font-size:13px;display:flex;gap:8px;align-items:center;justify-content:space-between;margin-bottom:10px}\n.ckw-skel{height:68px;border-radius:8px;background:var(--ckw-bg2);animation:ckw-sh 1.2s ease-in-out infinite;margin-bottom:6px}\n@keyframes ckw-sh{50%{opacity:.5}}\n@media (prefers-reduced-motion:reduce){.ckw-skel,.ckw-scrim,.ckw-modal{animation:none}.ckw-root *{transition:none!important}}\n.ckw-form{display:flex;flex-direction:column;gap:10px;border:1px solid var(--ckw-line);border-radius:8px;padding:12px;margin-bottom:12px;background:var(--ckw-bg)}\n.ckw-field{display:flex;flex-direction:column;gap:4px}\n.ckw-label{font-size:12px;font-weight:600;color:var(--ckw-fg2)}\n.ckw-actions{display:flex;gap:8px;justify-content:flex-end}\n.ckw-dupes{border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg2);padding:8px 10px;font-size:13px}\n.ckw-dupes p{margin:0 0 4px;color:var(--ckw-fg2);font-size:12px}\n.ckw-dupe{display:flex;gap:8px;align-items:center;justify-content:space-between;padding:3px 0}\n.ckw-dupe span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-team{margin-left:6px;background:var(--ckw-accent);color:var(--ckw-accent-ink);border-radius:4px;padding:0 5px;font-size:10px;font-weight:700;text-transform:uppercase;vertical-align:1px}\n.ckw-time{color:var(--ckw-fg3);font-size:12px;margin-left:6px}\n.ckw-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n/* Board */\n.ckw-board{width:100%;max-width:1160px;margin:0 auto;background:var(--ckw-bg);min-width:0}\n.ckw-board.ckw-full{max-width:none;min-height:100%;min-height:100dvh}\n.ckw-board.ckw-full>.ckw-body-wrap{max-width:1160px;margin:0 auto;padding:0 16px 56px}\n.ckw-top{position:sticky;top:0;z-index:5;background:color-mix(in srgb,var(--ckw-bg) 88%,transparent);backdrop-filter:saturate(1.4) blur(10px);-webkit-backdrop-filter:saturate(1.4) blur(10px);border-bottom:1px solid var(--ckw-line);margin-bottom:24px}\n.ckw-top-row{max-width:1160px;margin:0 auto;padding:10px 16px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}\n.ckw-brand{display:inline-flex;align-items:center;gap:10px;font-weight:650;font-size:15px;color:var(--ckw-fg);background:none;border:0;padding:4px 2px;border-radius:8px;min-width:0}\n.ckw-brand span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:220px}\n.ckw-brand img{width:28px;height:28px;border-radius:7px;object-fit:cover;outline:1px solid oklch(0 0 0/.1);outline-offset:-1px;flex:none}\n.ckw-root[data-theme=dark] .ckw-brand img{outline-color:oklch(1 0 0/.1)}\n.ckw-brand-mark{width:28px;height:28px;border-radius:7px;background:var(--ckw-accent);color:var(--ckw-accent-ink);display:inline-flex;align-items:center;justify-content:center;font-size:14px;flex:none}\n.ckw-nav{display:flex;gap:2px;flex:1;min-width:0}\n.ckw-nav-tab{background:none;border:0;border-radius:8px;padding:0 12px;height:34px;color:var(--ckw-fg2);font-weight:550;font-size:14px;white-space:nowrap;transition:background-color .1s linear,color .1s linear}\n.ckw-nav-tab:hover{color:var(--ckw-fg);background:var(--ckw-bg2)}\n.ckw-nav-tab[aria-selected=true]{color:var(--ckw-fg);background:var(--ckw-bg3)}\n.ckw-top-tools{display:flex;align-items:center;gap:6px;margin-left:auto}\n.ckw-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:8px;border:0;background:none;color:var(--ckw-fg2);flex:none}\n.ckw-icon-btn:hover{background:var(--ckw-bg2);color:var(--ckw-fg)}\n.ckw-searchbox{display:flex;align-items:center;border-radius:8px;transition:background-color .1s linear}\n.ckw-search-input{width:0;padding:0;border:0;background:none;outline:none;font-size:14px;height:34px;transition:width .15s cubic-bezier(.2,0,0,1)}\n.ckw-searchbox[data-open]{background:var(--ckw-bg2)}\n.ckw-searchbox[data-open] .ckw-search-input{width:180px;padding-right:10px}\n.ckw-searchbox:focus-within{outline:2px solid var(--ckw-accent);outline-offset:0}\n.ckw-root .ckw-search-input:focus-visible{outline:none}\n.ckw-search-input::-webkit-search-cancel-button{display:none}\n.ckw-submit{height:34px}\n.ckw-btn,.ckw-vote,.ckw-upvote,.ckw-icon-btn{transition-property:background-color,border-color,color,scale;transition-duration:.1s;transition-timing-function:linear}\n.ckw-btn:active:not([disabled]),.ckw-vote:active,.ckw-upvote:active,.ckw-icon-btn:active{scale:.96}\n.ckw-intro{margin:0 0 20px}\n.ckw-intro h1{font-size:24px;font-weight:650;letter-spacing:-.02em;margin:0;text-wrap:balance}\n.ckw-intro p{margin:4px 0 0;color:var(--ckw-fg2);font-size:14px;text-wrap:pretty}\n.ckw-note{display:flex;align-items:center;justify-content:space-between;gap:8px;border-radius:10px;padding:6px 6px 6px 12px;margin-bottom:14px;background:color-mix(in srgb,var(--ckw-accent) 10%,transparent);color:var(--ckw-fg);font-size:13px;font-weight:500}\n.ckw-tools .ckw-chips-inline{margin:0}\n.ckw-posts{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}\n.ckw-post{position:relative;display:flex;gap:14px;align-items:flex-start;padding:14px 16px;border-radius:12px;background:var(--ckw-card);box-shadow:var(--ckw-lift);transition:box-shadow .1s linear}\n.ckw-post:hover{box-shadow:var(--ckw-lift),0 4px 14px rgba(16,18,24,.06)}\n.ckw-post .ckw-vote,.ckw-card .ckw-vote{position:relative;z-index:1}\n.ckw-stretch::after{content:\"\";position:absolute;inset:0;border-radius:inherit}\n.ckw-post .ckw-link,.ckw-card .ckw-link{font-size:15px;font-weight:600;text-wrap:pretty}\n.ckw-dot-sep{color:var(--ckw-fg3)}\n.ckw-type{display:inline-flex;align-items:center;gap:5px;border-radius:6px;padding:1px 7px;font-size:11.5px;font-weight:600;color:var(--ckw-fg2);background:var(--ckw-bg2);white-space:nowrap;width:max-content}\n.ckw-type::before{content:\"\";width:6px;height:6px;border-radius:50%;background:var(--ckw-fg3)}\n.ckw-type[data-tone=blue]::before{background:#3f76ff}\n.ckw-type[data-tone=red]::before{background:#ef4444}\n.ckw-type[data-tone=violet]::before{background:#8b5cf6}\n.ckw-type[data-tone=green]::before{background:#22c55e}\n.ckw-av{width:22px;height:22px;border-radius:50%;flex:none;display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:650;background:var(--ckw-bg3);color:var(--ckw-fg2)}\n.ckw-av[data-team]{background:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-count{font-weight:500;color:var(--ckw-fg3);font-variant-numeric:tabular-nums;margin-left:2px}\n.ckw-empty p{margin:0 0 12px}\n.ckw-empty p:only-child{margin:0}\n/* Roadmap */\n.ckw-cols{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;align-items:start}\n.ckw-col{background:var(--ckw-col);border-radius:14px;padding:6px;min-width:0}\n.ckw-col-head{padding:8px 8px 10px}\n.ckw-col-head h3{display:flex;align-items:center;gap:8px;margin:0;font-size:14px;font-weight:650}\n.ckw-col-head p{margin:2px 0 0 16px;font-size:12px;color:var(--ckw-fg3)}\n.ckw-sdot{width:8px;height:8px;border-radius:50%;background:var(--ckw-fg3);flex:none}\n.ckw-sdot[data-s=planned]{background:#3f76ff}\n.ckw-sdot[data-s=progress]{background:#f59e0b}\n.ckw-sdot[data-s=shipped]{background:#22c55e}\n.ckw-col-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}\n.ckw-col-empty{margin:0;padding:24px 8px;text-align:center;font-size:12px;color:var(--ckw-fg3)}\n.ckw-col .ckw-skel{margin:0 0 6px;height:96px;background:var(--ckw-card)}\n.ckw-card{position:relative;display:flex;flex-direction:column;gap:6px;padding:12px;border-radius:8px;background:var(--ckw-card);box-shadow:var(--ckw-lift);transition:box-shadow .1s linear}\n.ckw-card:hover{box-shadow:var(--ckw-lift),0 4px 14px rgba(16,18,24,.07)}\n.ckw-card .ckw-link{font-size:14px}\n.ckw-card .ckw-excerpt{margin:0}\n.ckw-card-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:4px}\n.ckw-who{display:inline-flex;align-items:center;gap:6px;min-width:0;font-size:12px;color:var(--ckw-fg2)}\n.ckw-who span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-vote-sm{height:28px;border-radius:7px;font-size:12px}\n@media (max-width:900px){.ckw-cols{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;margin:0 -16px;padding:0 16px 8px;scroll-padding:0 16px}.ckw-col{flex:0 0 min(82%,300px);scroll-snap-align:start}}\n/* Post page */\n.ckw-back{display:inline-flex;align-items:center;gap:4px;background:none;border:0;padding:6px 8px 6px 4px;margin:0 0 12px -4px;border-radius:8px;color:var(--ckw-fg2);font-size:13px;font-weight:550}\n.ckw-back:hover{color:var(--ckw-fg);background:var(--ckw-bg2)}\n.ckw-back-icon{display:inline-block;transform:rotate(-90deg);line-height:0}\n.ckw-detail-grid{display:grid;grid-template-columns:minmax(0,1fr) 260px;gap:32px;align-items:start}\n.ckw-detail-title{margin:0 0 8px;font-size:22px;font-weight:650;letter-spacing:-.015em;overflow-wrap:anywhere;text-wrap:balance}\n.ckw-byline{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--ckw-fg2)}\n.ckw-body{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--ckw-fg);margin:16px 0 0;font-size:14.5px;line-height:1.6;text-wrap:pretty}\n.ckw-h{display:flex;align-items:center;gap:6px;font-size:14px;font-weight:650;margin:28px 0 10px;padding-top:20px;border-top:1px solid var(--ckw-line)}\n.ckw-composer{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}\n.ckw-composer .ckw-actions{align-items:center}\n.ckw-as{margin:0 auto 0 0}\n.ckw-comment{display:flex;gap:10px;padding:12px 0;border-top:1px solid var(--ckw-line)}\n.ckw-comment b{font-size:13px}\n.ckw-comment p{margin:3px 0 0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px}\n.ckw-side{position:sticky;top:76px;display:flex;flex-direction:column;gap:12px;padding:14px;border-radius:12px;background:var(--ckw-card);box-shadow:var(--ckw-lift)}\n.ckw-upvote{display:flex;align-items:center;gap:8px;height:40px;padding:0 12px;border-radius:8px;border:1px solid var(--ckw-line);background:var(--ckw-bg);font-weight:600;font-size:14px;color:var(--ckw-fg)}\n.ckw-upvote b{margin-left:auto;font-variant-numeric:tabular-nums}\n.ckw-upvote:hover{border-color:var(--ckw-accent);color:var(--ckw-accent)}\n.ckw-upvote[aria-pressed=true]{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-facts{margin:0;display:flex;flex-direction:column;gap:10px;font-size:13px}\n.ckw-facts div{display:flex;justify-content:space-between;align-items:center;gap:8px}\n.ckw-facts dt{color:var(--ckw-fg3)}\n.ckw-facts dd{margin:0;color:var(--ckw-fg)}\n@media (max-width:760px){.ckw-detail-grid{grid-template-columns:1fr;gap:16px}.ckw-side{position:static;order:-1}}\n/* Changelog */\n.ckw-log{max-width:820px}\n.ckw-entry{display:grid;grid-template-columns:160px minmax(0,1fr);gap:24px;padding:20px 0;border-top:1px solid var(--ckw-line)}\n.ckw-entry:first-child{border-top:0;padding-top:4px}\n.ckw-entry time{font-size:13px;color:var(--ckw-fg3);padding-top:2px}\n.ckw-entry h3{font-size:17px;font-weight:650;margin:0 0 6px;text-wrap:balance}\n.ckw-entry p{margin:0;color:var(--ckw-fg2);white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.6}\n@media (max-width:640px){.ckw-entry{grid-template-columns:1fr;gap:4px}}\n/* Dialog */\n.ckw-scrim{position:fixed;inset:0;z-index:2147483002;background:rgba(0,0,0,.36);display:flex;align-items:center;justify-content:center;padding:16px;animation:ckw-fade .15s ease-out}\n.ckw-modal{width:100%;max-width:520px;max-height:calc(100dvh - 32px);overflow-y:auto;background:var(--ckw-bg);color:var(--ckw-fg);border-radius:14px;box-shadow:var(--ckw-shadow),0 0 0 1px var(--ckw-line);animation:ckw-in .18s ease-out}\n.ckw-modal-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 10px 4px 18px}\n.ckw-modal-head h2{margin:0;font-size:16px;font-weight:650}\n.ckw-modal-body{display:flex;flex-direction:column;gap:12px;padding:8px 18px 18px}\n@keyframes ckw-fade{from{opacity:0}}\n@media (max-width:520px){.ckw-scrim{align-items:flex-end;padding:0}.ckw-modal{max-width:none;border-radius:14px 14px 0 0;max-height:92dvh}}\n.ckw-pair{display:grid;grid-template-columns:1fr 1fr;gap:10px}\n@media (max-width:480px){.ckw-pair{grid-template-columns:1fr}}\n.ckw-fine{margin:-4px 0 0;font-size:12px;color:var(--ckw-fg3);text-wrap:pretty}\n.ckw-opt-l{font-weight:500;color:var(--ckw-fg3)}\n.ckw-foot{text-align:center;font-size:12px;color:var(--ckw-fg3);margin:0;padding:0 16px 24px}\n.ckw-linkish{background:none;border:0;padding:0;color:var(--ckw-fg2);text-decoration:underline;font-size:12px}\n@media (max-width:720px){.ckw-top-row{gap:8px;padding:8px 12px}.ckw-nav{order:3;flex-basis:100%;overflow-x:auto;margin:0 -4px}.ckw-submit span{display:none}.ckw-submit{width:34px;padding:0}.ckw-searchbox[data-open] .ckw-search-input{width:120px}.ckw-top{margin-bottom:16px}.ckw-board.ckw-full>.ckw-body-wrap{padding:0 12px 40px}.ckw-post{padding:12px}.ckw-intro h1{font-size:20px}}\n/* Floating widget */\n.ckw-fab{position:fixed;z-index:2147483000;bottom:20px;display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 16px;border-radius:999px;border:0;background:var(--ckw-accent);color:var(--ckw-accent-ink);font-weight:600;font-size:14px;box-shadow:var(--ckw-shadow)}\n.ckw-fab[data-pos=bottom-right],.ckw-pop[data-pos=bottom-right]{right:20px}\n.ckw-fab[data-pos=bottom-left],.ckw-pop[data-pos=bottom-left]{left:20px}\n.ckw-pop{position:fixed;z-index:2147483001;bottom:70px;width:380px;max-width:calc(100vw - 24px);max-height:min(560px,calc(100vh - 90px));display:flex;flex-direction:column;background:var(--ckw-bg);border:1px solid var(--ckw-line);border-radius:12px;box-shadow:var(--ckw-shadow);overflow:hidden}\n.ckw-pop-head{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--ckw-line);font-weight:600}\n.ckw-pop-body{padding:12px;overflow-y:auto;flex:1}\n.ckw-pop-foot{padding:8px 12px;border-top:1px solid var(--ckw-line);display:flex;justify-content:space-between;align-items:center;font-size:13px}\n.ckw-pop-foot a{color:var(--ckw-accent);text-decoration:none;font-weight:500}\n.ckw-pop .ckw-list{border:0;border-radius:0}\n.ckw-pop .ckw-row{padding:8px 0}\n.ckw-pop .ckw-form{border:0;padding:0;margin:12px 0 0}\n@media (max-width:520px){.ckw-pop{left:0!important;right:0!important;bottom:0;width:100%;max-width:100%;border-radius:12px 12px 0 0;max-height:85vh}.ckw-fab{bottom:14px}.ckw-fab[data-pos=bottom-right]{right:14px}.ckw-fab[data-pos=bottom-left]{left:14px}}\n/* Messenger (messenger-ui.ts). Motion: one soft ease (.32,.72,0,1) for things that arrive, a quick ease-in for things that leave. */\n.ckw-msgr-fab{position:fixed;bottom:20px;height:52px;padding:0 20px 0 16px;gap:8px;font-size:15px;box-shadow:0 2px 6px rgba(16,18,24,.14),0 10px 28px color-mix(in srgb,var(--ckw-accent) 38%,transparent);transition:transform .28s cubic-bezier(.32,.72,0,1),box-shadow .2s linear}\n.ckw-msgr-fab:hover{transform:scale(1.06);box-shadow:0 2px 6px rgba(16,18,24,.14),0 14px 34px color-mix(in srgb,var(--ckw-accent) 46%,transparent)}\n.ckw-msgr-fab:active{transform:scale(.94);transition-duration:.1s}\n.ckw-msgr-round{width:60px;height:60px;padding:0;justify-content:center}\n.ckw-msgr-fab-ic{position:relative;display:block;width:20px;height:20px;flex:none}\n.ckw-msgr-round .ckw-msgr-fab-ic{width:28px;height:28px}\n.ckw-msgr-fab-i{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;transition:transform .38s cubic-bezier(.32,.72,0,1),opacity .2s linear}\n.ckw-msgr-fab-i .ckw-icon{width:auto;height:auto}\n.ckw-msgr-fab-i[data-i=x],.ckw-msgr-fab[data-open] .ckw-msgr-fab-i[data-i=chat]{opacity:0;transform:rotate(-90deg) scale(.4)}\n.ckw-msgr-fab[data-open] .ckw-msgr-fab-i[data-i=chat]{transform:rotate(90deg) scale(.4)}\n.ckw-msgr-fab[data-open] .ckw-msgr-fab-i[data-i=x]{opacity:1;transform:none}\n.ckw-msgr-fab-i[data-i=x] .ckw-icon{stroke-width:2}\n.ckw-msgr-count{position:absolute;top:-2px;right:-2px;min-width:20px;height:20px;padding:0 5px;border-radius:10px;background:var(--ckw-danger);color:#fff;font-size:11px;font-weight:700;line-height:20px;text-align:center;box-shadow:0 0 0 2px var(--ckw-bg);font-variant-numeric:tabular-nums;animation:ckw-badge .4s cubic-bezier(.34,1.56,.64,1)}\n@keyframes ckw-badge{from{transform:scale(0)}}\n.ckw-msgr-av{width:36px;height:36px;border-radius:50%;flex:none;display:inline-flex;align-items:center;justify-content:center;background:var(--ckw-accent);color:var(--ckw-accent-ink);font-size:15px;font-weight:700;line-height:1}\n.ckw-msgr-av-sm{width:26px;height:26px;font-size:12px}\n.ckw-msgr-online{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--ckw-fg2)}\n.ckw-msgr-online::before{content:\"\";width:7px;height:7px;border-radius:50%;background:#22c55e;box-shadow:0 0 0 3px color-mix(in srgb,#22c55e 22%,transparent);flex:none}\n/* Floating cards over the closed launcher: a new answer, the hover teaser */\n.ckw-msgr-preview,.ckw-msgr-teaser{position:fixed;z-index:2147483000;bottom:92px;width:320px;max-width:calc(100vw - 32px);transform-origin:bottom right;animation:ckw-rise .34s cubic-bezier(.32,.72,0,1)}\n.ckw-msgr-preview[data-pos=bottom-right],.ckw-msgr-teaser[data-pos=bottom-right]{right:20px}\n.ckw-msgr-preview[data-pos=bottom-left],.ckw-msgr-teaser[data-pos=bottom-left]{left:20px;transform-origin:bottom left}\n.ckw-msgr-preview[data-out],.ckw-msgr-teaser[data-out]{animation:ckw-sink .16s cubic-bezier(.4,0,1,1) forwards;pointer-events:none}\n@keyframes ckw-rise{from{opacity:0;transform:translateY(10px) scale(.94)}}\n@keyframes ckw-sink{to{opacity:0;transform:translateY(6px) scale(.97)}}\n.ckw-msgr-preview-card{display:flex;align-items:flex-start;gap:12px;width:100%;text-align:left;padding:14px 16px;border:0;border-radius:18px;background:var(--ckw-bg);box-shadow:var(--ckw-float);transition:transform .2s cubic-bezier(.32,.72,0,1)}\n.ckw-msgr-preview-card:hover{transform:translateY(-2px)}\n.ckw-msgr-preview-main{display:flex;flex-direction:column;gap:2px;min-width:0}\n.ckw-msgr-preview-who{font-size:12px;font-weight:600;color:var(--ckw-fg3)}\n.ckw-msgr-preview-text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;white-space:pre-wrap;overflow-wrap:anywhere}\n.ckw-msgr-preview-x{position:absolute;top:-8px;right:-8px;width:24px;height:24px;border-radius:50%;border:0;background:var(--ckw-bg);color:var(--ckw-fg2);display:flex;align-items:center;justify-content:center;padding:0;box-shadow:var(--ckw-lift)}\n.ckw-msgr-teaser{width:340px;padding:16px;border-radius:20px;background:var(--ckw-bg);box-shadow:var(--ckw-float);display:flex;flex-direction:column;gap:14px}\n.ckw-msgr-teaser-top{display:flex;align-items:center;gap:12px}\n.ckw-msgr-teaser-t{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}\n.ckw-msgr-teaser-t b{font-size:15px;font-weight:700;letter-spacing:-.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-msgr-teaser-row{display:flex;gap:8px}\n.ckw-msgr-teaser-b{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:7px;height:40px;padding:0 14px;border:0;border-radius:999px;background:var(--ckw-bg3);color:var(--ckw-fg);font-size:14px;font-weight:600;white-space:nowrap;transition:transform .2s cubic-bezier(.32,.72,0,1),filter .1s linear}\n.ckw-msgr-teaser-b[data-main]{flex:1.4;background:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-msgr-teaser-b:hover{filter:brightness(1.06)}\n.ckw-msgr-teaser-b:active{transform:scale(.96)}\n/* The panel */\n.ckw-msgr{bottom:92px;width:400px;height:min(680px,calc(100vh - 112px));max-height:none;border:0;border-radius:22px;box-shadow:var(--ckw-float);transform-origin:bottom right;animation:ckw-msgr-in .42s cubic-bezier(.32,.72,0,1);transition:width .36s cubic-bezier(.32,.72,0,1),height .36s cubic-bezier(.32,.72,0,1)}\n.ckw-msgr[data-pos=bottom-left]{transform-origin:bottom left}\n.ckw-msgr[data-wide]{width:680px;height:calc(100vh - 112px);max-height:860px}\n.ckw-msgr[data-still]{animation:none}\n.ckw-msgr[data-out]{animation:ckw-msgr-out .2s cubic-bezier(.4,0,1,1) forwards;pointer-events:none}\n@keyframes ckw-msgr-in{from{opacity:0;transform:translateY(20px) scale(.9)}}\n@keyframes ckw-msgr-out{to{opacity:0;transform:translateY(14px) scale(.94)}}\n.ckw-msgr-bar{position:relative;z-index:2;display:flex;align-items:center;gap:8px;padding:12px 12px 10px;background:var(--ckw-bg);flex:none;transition:background-color .25s linear,color .25s linear}\n.ckw-msgr-lead,.ckw-msgr-trail{display:flex;align-items:center;gap:2px;flex:1;min-width:0}\n.ckw-msgr-trail{justify-content:flex-end}\n.ckw-msgr-ib{width:34px;height:34px;padding:0;flex:none;display:flex;align-items:center;justify-content:center;border:0;border-radius:50%;background:transparent;color:var(--ckw-fg2);transition:background-color .12s linear,transform .2s cubic-bezier(.32,.72,0,1)}\n.ckw-msgr-ib:hover,.ckw-msgr-ib[aria-expanded=true]{background:var(--ckw-bg3);color:var(--ckw-fg)}\n.ckw-msgr-ib:active{transform:scale(.9)}\n.ckw-msgr-ib[aria-current=page]{color:var(--ckw-fg)}\n.ckw-msgr-x{display:none}\n.ckw-msgr-seg{position:relative;display:flex;padding:4px;border-radius:999px;background:var(--ckw-bg3);flex:none}\n.ckw-msgr-seg-mark{position:absolute;top:4px;bottom:4px;left:0;border-radius:999px;background:var(--ckw-accent);box-shadow:0 1px 3px color-mix(in srgb,var(--ckw-accent) 40%,transparent);opacity:0;transition:transform .38s cubic-bezier(.32,.72,0,1),width .38s cubic-bezier(.32,.72,0,1),opacity .2s linear}\n.ckw-msgr-seg-mark[data-on]{opacity:1}\n.ckw-msgr-seg-mark[data-still]{transition:opacity .2s linear}\n.ckw-msgr-seg-b{position:relative;z-index:1;display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 14px;border:0;border-radius:999px;background:none;color:var(--ckw-fg2);font-size:13.5px;font-weight:650;white-space:nowrap;transition:color .25s linear}\n.ckw-msgr-seg-b:hover{color:var(--ckw-fg)}\n.ckw-msgr-seg-b[aria-current=page]{color:var(--ckw-accent-ink)}\n.ckw-msgr-count-tab{top:8px;right:4px;min-width:0;width:7px;height:7px;padding:0;font-size:0;line-height:0;box-shadow:0 0 0 2px var(--ckw-bg3)}\n.ckw-msgr-menu{position:absolute;z-index:3;top:52px;right:12px;min-width:200px;padding:6px;border-radius:14px;background:var(--ckw-bg);box-shadow:var(--ckw-float);display:flex;flex-direction:column;transform-origin:top right;animation:ckw-menu-in .2s cubic-bezier(.32,.72,0,1)}\n.ckw-msgr-menu[data-out]{animation:ckw-menu-out .12s cubic-bezier(.4,0,1,1) forwards}\n@keyframes ckw-menu-in{from{opacity:0;transform:translateY(-4px) scale(.94)}}\n@keyframes ckw-menu-out{to{opacity:0;transform:scale(.96)}}\n.ckw-msgr-menu-h{padding:6px 10px 4px;font-size:12px;font-weight:600;color:var(--ckw-fg3)}\n.ckw-msgr-menu-i{display:flex;align-items:center;gap:10px;height:36px;padding:0 10px;border:0;border-radius:9px;background:none;font-size:14px;text-align:left;color:var(--ckw-fg)}\n.ckw-msgr-menu-i:hover,.ckw-msgr-menu-i:focus-visible{background:var(--ckw-bg2)}\n.ckw-msgr-menu-i .ckw-icon{color:var(--ckw-fg2)}\n.ckw-msgr-body{display:flex;flex-direction:column;gap:12px;background:var(--ckw-bg);padding:4px 16px 16px;overscroll-behavior:contain;scrollbar-width:thin}\n.ckw-msgr-enter>*{animation:ckw-view .3s cubic-bezier(.32,.72,0,1) backwards}\n.ckw-msgr-enter>:nth-child(2){animation-delay:.03s}\n.ckw-msgr-enter>:nth-child(3){animation-delay:.06s}\n.ckw-msgr-enter>:nth-child(n+4){animation-delay:.09s}\n@keyframes ckw-view{from{opacity:0;transform:translateY(8px)}}\n/* Home */\n.ckw-msgr-hero{display:flex;flex-direction:column;gap:8px;padding:14px 4px 10px}\n.ckw-msgr-brand{display:inline-flex;align-items:center;gap:8px;font-size:13px;font-weight:600;color:var(--ckw-fg2);min-width:0}\n.ckw-msgr-brand span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-msgr-hello{margin:4px 0 0;font-size:26px;font-weight:700;letter-spacing:-.025em;line-height:1.15;text-wrap:balance;overflow-wrap:anywhere}\n.ckw-msgr-card{position:relative;display:flex;align-items:center;gap:12px;width:100%;text-align:left;padding:14px 16px;border:0;border-radius:16px;background:var(--ckw-card);box-shadow:0 0 0 1px var(--ckw-line),0 1px 2px rgba(16,18,24,.04);transition:box-shadow .15s linear,transform .25s cubic-bezier(.32,.72,0,1)}\nbutton.ckw-msgr-card:hover{transform:translateY(-1px);box-shadow:0 0 0 1px color-mix(in srgb,var(--ckw-accent) 35%,var(--ckw-line)),0 6px 16px rgba(16,18,24,.06)}\nbutton.ckw-msgr-card:active{transform:scale(.985)}\n.ckw-msgr-card-col{flex-direction:column;align-items:stretch;gap:2px;padding:8px}\n.ckw-msgr-card-main{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}\n.ckw-msgr-card-main b{font-size:15px;font-weight:650}\n.ckw-msgr-card-sub{color:var(--ckw-fg2);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.ckw-msgr-card-label{font-size:12px;font-weight:600;color:var(--ckw-fg3)}\n.ckw-msgr-card-meta{font-size:12px;color:var(--ckw-fg3)}\n.ckw-msgr-recent{flex-direction:column;align-items:stretch;gap:10px}\n.ckw-msgr-recent-row{display:flex;align-items:center;gap:12px;min-width:0}\n.ckw-msgr-recent-text{color:var(--ckw-fg);font-size:14px}\n.ckw-msgr-card .ckw-icon{color:var(--ckw-fg3)}\n.ckw-msgr-go{width:34px;height:34px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--ckw-accent) 12%,transparent);transition:transform .25s cubic-bezier(.32,.72,0,1)}\nbutton.ckw-msgr-card:hover .ckw-msgr-go{transform:translateX(2px)}\n.ckw-msgr-card .ckw-msgr-go .ckw-icon{color:var(--ckw-accent)}\n.ckw-msgr-badge{width:8px;height:8px;flex:none;border-radius:50%;background:var(--ckw-danger);font-size:0}\n/* Search */\n.ckw-msgr-search{display:flex;align-items:center;gap:10px;height:46px;padding:0 14px;border:1px solid transparent;border-radius:14px;background:var(--ckw-bg2);color:var(--ckw-fg3);cursor:text;transition:border-color .15s linear,box-shadow .15s linear,background-color .15s linear}\n.ckw-msgr-card-col .ckw-msgr-search{margin-bottom:4px;height:42px}\n.ckw-msgr-search:focus-within{border-color:var(--ckw-accent);background:var(--ckw-bg);box-shadow:0 0 0 4px color-mix(in srgb,var(--ckw-accent) 16%,transparent);color:var(--ckw-accent)}\n.ckw-msgr-search-in{flex:1;min-width:0;border:0;background:transparent;outline:none;font-size:14.5px;color:var(--ckw-fg)}\n.ckw-root .ckw-msgr-search-in:focus-visible{outline:none}\n.ckw-msgr-search-in::placeholder{color:var(--ckw-fg3)}\n.ckw-msgr-search-in::-webkit-search-cancel-button{display:none}\n.ckw-msgr-art{display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:10px 8px;border:0;border-radius:10px;background:transparent;font-size:14px;transition:background-color .12s linear}\n.ckw-msgr-art:hover{background:var(--ckw-bg2)}\n.ckw-msgr-art-t{flex:1;min-width:0}\n.ckw-msgr-art .ckw-icon{color:var(--ckw-accent);transition:transform .25s cubic-bezier(.32,.72,0,1)}\n.ckw-msgr-art:hover .ckw-icon{transform:translateX(2px)}\n.ckw-msgr-skel{height:40px;margin:4px 0 0}\n/* Help */\n.ckw-msgr-help-h{margin:6px 0 2px;text-align:center;font-size:16px;font-weight:700;letter-spacing:-.01em}\n.ckw-msgr-hlist{display:flex;flex-direction:column;margin:0 -8px}\n.ckw-msgr-hrow{display:flex;align-items:center;gap:12px;width:100%;padding:12px;border:0;border-radius:14px;background:transparent;text-align:left;transition:background-color .12s linear}\n.ckw-msgr-hrow+.ckw-msgr-hrow{box-shadow:0 -1px 0 var(--ckw-line)}\n.ckw-msgr-hrow:hover,.ckw-msgr-hrow:hover+.ckw-msgr-hrow{box-shadow:none}\n.ckw-msgr-hrow:hover{background:var(--ckw-bg2)}\n.ckw-msgr-hrow-main{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}\n.ckw-msgr-hrow-main b{font-size:14.5px;font-weight:650}\n.ckw-msgr-hrow-sub{font-size:13px;color:var(--ckw-fg2);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;line-height:1.4}\n.ckw-msgr-hrow-meta{font-size:12px;color:var(--ckw-fg3)}\n.ckw-msgr-hrow .ckw-icon{color:var(--ckw-fg3);transition:transform .25s cubic-bezier(.32,.72,0,1),color .12s linear}\n.ckw-msgr-hrow:hover .ckw-icon{color:var(--ckw-fg);transform:translateX(2px)}\n.ckw-msgr-empty{display:flex;flex-direction:column;align-items:center;gap:10px;padding:28px 8px;text-align:center;color:var(--ckw-fg2)}\n.ckw-msgr-empty p{margin:0}\n.ckw-msgr-doc{display:flex;flex-direction:column;gap:6px;padding-top:4px}\n.ckw-msgr-doc-t{margin:0;font-size:20px;font-weight:700;letter-spacing:-.015em;line-height:1.3}\n.ckw-msgr-doc-c{margin:0;font-size:12px;color:var(--ckw-fg3)}\n.ckw-msgr-doc-b{margin-top:8px;font-size:14px;line-height:1.6;overflow-wrap:anywhere}\n.ckw-msgr-doc-b>*{margin:0 0 10px}\n.ckw-msgr-doc-b h2{font-size:16px;margin-top:16px}\n.ckw-msgr-doc-b h3{font-size:15px;margin-top:12px}\n.ckw-msgr-doc-b ul,.ckw-msgr-doc-b ol{padding-left:20px}\n.ckw-msgr-doc-b blockquote{border-left:3px solid var(--ckw-line);padding-left:10px;color:var(--ckw-fg2)}\n.ckw-msgr-doc-b pre{background:var(--ckw-bg2);border-radius:8px;padding:10px;overflow-x:auto;font-size:13px}\n.ckw-msgr-doc-b code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.92em}\n.ckw-msgr-doc-b a{color:var(--ckw-accent)}\n.ckw-msgr-doc-b hr{border:0;border-top:1px solid var(--ckw-line)}\n.ckw-msgr-rate{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;margin-top:8px;padding:12px;border-radius:12px;background:var(--ckw-bg2);font-size:13px;color:var(--ckw-fg2)}\n.ckw-msgr-rate-b{display:flex;gap:6px}\n.ckw-msgr-ask{margin-top:6px;width:100%;height:40px;border-radius:12px}\n/* Chat */\n.ckw-msgr-intro{display:flex;align-items:center;gap:12px;padding:6px 2px 14px;border-bottom:1px solid var(--ckw-line)}\n.ckw-msgr-intro-av .ckw-msgr-av{width:44px;height:44px;font-size:18px;box-shadow:0 4px 12px color-mix(in srgb,var(--ckw-accent) 30%,transparent)}\n.ckw-msgr-intro-t{display:flex;flex-direction:column;gap:3px;min-width:0}\n.ckw-msgr-intro-t b{font-size:15.5px;font-weight:700;letter-spacing:-.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-msgr-cite{display:inline-flex;align-items:center;gap:6px;max-width:100%;padding:7px 10px;border:1px solid var(--ckw-line);border-radius:12px;background:var(--ckw-bg);font-size:13px;text-align:left}\n.ckw-msgr-cite .ckw-icon{color:var(--ckw-accent)}\n.ckw-msgr-cite span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-msgs{list-style:none;margin:4px 0 0;padding:0;display:flex;flex-direction:column;gap:12px}\n.ckw-msg{display:flex;flex-direction:column;align-items:flex-start;gap:4px;max-width:82%;transform-origin:bottom left}\n.ckw-msg[data-from=me]{align-self:flex-end;align-items:flex-end;transform-origin:bottom right}\n.ckw-msg[data-new]{animation:ckw-bub .4s cubic-bezier(.32,.72,0,1)}\n@keyframes ckw-bub{from{opacity:0;transform:translateY(10px) scale(.92)}}\n.ckw-bubble{padding:10px 14px;border-radius:18px 18px 18px 6px;background:var(--ckw-bg3);white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px;line-height:1.45;text-wrap:pretty}\n.ckw-msg[data-from=me] .ckw-bubble{background:var(--ckw-accent);color:var(--ckw-accent-ink);border-radius:18px 18px 6px 18px}\n.ckw-msg[aria-busy=true] .ckw-bubble{opacity:.7}\n.ckw-msg-meta{font-size:11px;color:var(--ckw-fg3);font-variant-numeric:tabular-nums;padding:0 6px}\n.ckw-msgr-sugg{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:auto;padding-top:12px}\n.ckw-msgr-sugg-b{max-width:100%;padding:9px 14px;border:1px solid var(--ckw-line);border-radius:14px;background:var(--ckw-bg);font-size:13.5px;text-align:left;color:var(--ckw-fg);transition:border-color .12s linear,color .12s linear,transform .2s cubic-bezier(.32,.72,0,1)}\n.ckw-msgr-sugg-b:hover{border-color:var(--ckw-accent);color:var(--ckw-accent)}\n.ckw-msgr-sugg-b:active{transform:scale(.96)}\n.ckw-msgr-enter .ckw-msgr-sugg-b{animation:ckw-view .34s cubic-bezier(.32,.72,0,1) backwards}\n.ckw-msgr-enter .ckw-msgr-sugg-b:nth-child(2){animation-delay:.12s}\n.ckw-msgr-enter .ckw-msgr-sugg-b:nth-child(3){animation-delay:.18s}\n.ckw-msgr-note{margin:4px 0 0;text-align:center}\n.ckw-msgr-compose{display:flex;flex-direction:column;gap:8px;padding:4px 14px 14px;background:var(--ckw-bg);flex:none}\n.ckw-msgr-compose .ckw-input{border:0;border-radius:12px;background:var(--ckw-bg2);min-height:38px;padding:8px 14px}\n.ckw-msgr-box{display:flex;flex-direction:column;gap:2px;padding:10px 10px 8px 14px;border:1.5px solid var(--ckw-line);border-radius:16px;background:var(--ckw-bg);cursor:text;transition:border-color .15s linear,box-shadow .15s linear}\n.ckw-msgr-box:focus-within{border-color:var(--ckw-accent);box-shadow:0 0 0 4px color-mix(in srgb,var(--ckw-accent) 14%,transparent)}\n.ckw-msgr-input{min-height:24px;max-height:140px;padding:2px 0;border:0;border-radius:0;background:transparent;resize:none;field-sizing:content;font-size:14.5px}\n.ckw-root .ckw-msgr-input:focus-visible{outline:none}\n.ckw-msgr-box-foot{display:flex;align-items:center;justify-content:space-between;gap:8px}\n.ckw-msgr-hint{font-size:11.5px;color:var(--ckw-fg3);opacity:0;transition:opacity .15s linear}\n.ckw-msgr-box:focus-within .ckw-msgr-hint{opacity:1}\n.ckw-msgr-send{width:32px;height:32px;padding:0;flex:none;display:flex;align-items:center;justify-content:center;border:0;border-radius:50%;background:var(--ckw-accent);color:var(--ckw-accent-ink);transition:background-color .15s linear,color .15s linear,transform .25s cubic-bezier(.34,1.56,.64,1)}\n.ckw-msgr-send:not(:disabled):hover{transform:scale(1.08)}\n.ckw-msgr-send:not(:disabled):active{transform:scale(.9)}\n.ckw-msgr-send:disabled{background:var(--ckw-bg3);color:var(--ckw-fg3);cursor:default}\n.ckw-msgr-error{margin:0;font-size:12px;color:var(--ckw-danger)}\n.ckw-msgr .ckw-input[aria-invalid=true]{box-shadow:0 0 0 1px var(--ckw-danger)}\n/* Inside MRRocket's preview: placed in its frame, not the page */\n.ckw-root[data-contained]{position:absolute;inset:0;pointer-events:none;overflow:hidden}\n.ckw-root[data-contained]>*{position:absolute;pointer-events:auto}\n.ckw-root[data-contained] .ckw-msgr{height:min(680px,calc(100% - 112px));max-width:calc(100% - 40px)}\n.ckw-root[data-contained] .ckw-msgr[data-wide]{height:calc(100% - 112px)}\n@media (max-width:520px){.ckw-root:not([data-contained]) .ckw-msgr{width:100%;height:100dvh;bottom:0;border-radius:0;transform-origin:bottom center}.ckw-root:not([data-contained]) .ckw-msgr-x{display:flex}.ckw-msgr-preview,.ckw-msgr-teaser{bottom:84px}}\n@media (prefers-reduced-motion:reduce){.ckw-msgr,.ckw-msgr[data-out],.ckw-msgr-preview,.ckw-msgr-teaser,.ckw-msgr-menu,.ckw-msgr-count,.ckw-msg[data-new],.ckw-msgr-enter>*,.ckw-msgr-enter .ckw-msgr-sugg-b{animation:none}.ckw-msgr-fab,.ckw-msgr-fab-i,.ckw-msgr-seg-mark,.ckw-msgr{transition:none}}\n/* Survey */\n.ckw-survey{position:fixed;z-index:2147483000;left:20px;bottom:20px;width:360px;max-width:calc(100vw - 24px);background:var(--ckw-bg);border:1px solid var(--ckw-line);border-radius:12px;box-shadow:var(--ckw-shadow);padding:14px;animation:ckw-in .2s ease-out}\n@keyframes ckw-in{from{opacity:0;transform:translateY(8px)}}\n@media (max-width:520px){.ckw-survey{left:0;right:0;bottom:0;width:100%;max-width:100%;border-radius:12px 12px 0 0}}\n.ckw-sv-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}\n.ckw-dots{display:flex;gap:4px}\n.ckw-dot{width:6px;height:6px;border-radius:50%;background:var(--ckw-bg3)}\n.ckw-dot[data-on=true]{background:var(--ckw-accent)}\n.ckw-q{font-weight:600;font-size:15px;margin:0 0 2px;border:0;padding:0}\n.ckw-root .ckw-q:focus-visible{outline:none}\n.ckw-qd{color:var(--ckw-fg2);font-size:13px;margin:0 0 10px}\n.ckw-scale{display:flex;gap:4px;flex-wrap:wrap}\n.ckw-num{flex:1 1 0;min-width:26px;height:34px;border:1px solid var(--ckw-line);background:var(--ckw-bg);border-radius:8px;font-size:13px;font-weight:600;padding:0}\n.ckw-num:hover{border-color:var(--ckw-accent)}\n.ckw-num[aria-pressed=true]{background:var(--ckw-accent);color:var(--ckw-accent-ink);border-color:var(--ckw-accent)}\n.ckw-root .ckw-star{background:none;border:0;padding:2px;color:var(--ckw-bg3)}\n.ckw-root .ckw-star[data-on=true]{color:#f5a524}\n.ckw-star svg{width:28px;height:28px}\n.ckw-lh{display:flex;justify-content:space-between;color:var(--ckw-fg3);font-size:11px;margin-top:4px}\n.ckw-opts{display:flex;flex-direction:column;gap:6px}\n.ckw-opt{display:flex;gap:8px;align-items:center;border:1px solid var(--ckw-line);border-radius:8px;padding:7px 10px;background:var(--ckw-bg);cursor:pointer}\n.ckw-opt:has(input:checked){border-color:var(--ckw-accent);background:color-mix(in srgb,var(--ckw-accent) 8%,transparent)}\n.ckw-opt input{accent-color:var(--ckw-accent)}\n.ckw-sv-foot{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-top:12px}\n.ckw-thanks{text-align:center;padding:14px 0;display:flex;flex-direction:column;align-items:center;gap:8px}\n.ckw-tick{width:32px;height:32px;border-radius:50%;background:var(--ckw-accent);color:var(--ckw-accent-ink);display:flex;align-items:center;justify-content:center}\n";
5
5
  /** Puts the stylesheet into the page once, however many widgets mount. */
6
6
  export declare const injectStyles: () => void;