@mrrocket/sdk 0.14.0 → 0.16.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 (70) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +45 -4
  3. package/dist/api-client.d.ts +67 -5
  4. package/dist/api-client.js +39 -8
  5. package/dist/api-client.js.map +1 -1
  6. package/dist/api.d.ts +4 -1
  7. package/dist/api.js +2 -1
  8. package/dist/api.js.map +1 -1
  9. package/dist/batch.d.ts +6 -4
  10. package/dist/batch.js +3 -3
  11. package/dist/batch.js.map +1 -1
  12. package/dist/browser/mrrocket-flows.js +2 -2
  13. package/dist/browser/mrrocket-forms.js +1 -1
  14. package/dist/browser/mrrocket-messenger.js +202 -46
  15. package/dist/browser/mrrocket-push.js +1 -1
  16. package/dist/browser/mrrocket-replay.js +3 -3
  17. package/dist/browser/mrrocket.js +1 -1
  18. package/dist/client.d.ts +10 -2
  19. package/dist/client.js +36 -15
  20. package/dist/client.js.map +1 -1
  21. package/dist/context.d.ts +5 -1
  22. package/dist/context.js.map +1 -1
  23. package/dist/core.d.ts +1 -1
  24. package/dist/core.js +1 -1
  25. package/dist/core.js.map +1 -1
  26. package/dist/errors.d.ts +5 -1
  27. package/dist/errors.js +8 -2
  28. package/dist/errors.js.map +1 -1
  29. package/dist/flows-ui.js +48 -1
  30. package/dist/flows-ui.js.map +1 -1
  31. package/dist/flows-web.d.ts +2 -0
  32. package/dist/flows-web.js +4 -1
  33. package/dist/flows-web.js.map +1 -1
  34. package/dist/flows.d.ts +61 -1
  35. package/dist/flows.js +64 -21
  36. package/dist/flows.js.map +1 -1
  37. package/dist/forms.d.ts +4 -0
  38. package/dist/forms.js +4 -1
  39. package/dist/forms.js.map +1 -1
  40. package/dist/identity.d.ts +42 -0
  41. package/dist/identity.js +24 -0
  42. package/dist/identity.js.map +1 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/messenger-ui.d.ts +21 -6
  46. package/dist/messenger-ui.js +334 -80
  47. package/dist/messenger-ui.js.map +1 -1
  48. package/dist/messenger.d.ts +11 -3
  49. package/dist/messenger.js +9 -5
  50. package/dist/messenger.js.map +1 -1
  51. package/dist/push-web.d.ts +2 -13
  52. package/dist/push-web.js +4 -2
  53. package/dist/push-web.js.map +1 -1
  54. package/dist/react-native/index.d.cts +162 -14
  55. package/dist/react-native/index.d.ts +162 -14
  56. package/dist/react-native/index.js +552 -207
  57. package/dist/react-native/index.js.map +1 -1
  58. package/dist/replay.d.ts +4 -1
  59. package/dist/replay.js +8 -2
  60. package/dist/replay.js.map +1 -1
  61. package/dist/server.d.ts +2 -0
  62. package/dist/server.js +2 -0
  63. package/dist/server.js.map +1 -1
  64. package/dist/styles-css.d.ts +1 -1
  65. package/dist/styles-css.js +200 -44
  66. package/dist/styles-css.js.map +1 -1
  67. package/dist/user-hash.d.ts +27 -0
  68. package/dist/user-hash.js +24 -0
  69. package/dist/user-hash.js.map +1 -0
  70. package/package.json +9 -1
package/dist/replay.d.ts CHANGED
@@ -18,6 +18,8 @@ export interface ReplayOptions {
18
18
  distinctId?: string;
19
19
  /** Whether `distinctId` comes from identify() (a person) rather than the visitor cookie. */
20
20
  identified?: boolean;
21
+ /** The identity hash of `distinctId` (0.16.0), sent with every chunk while the recording is theirs. */
22
+ userHash?: string;
21
23
  sampleRate: number;
22
24
  warn: (message: string) => void;
23
25
  }
@@ -27,7 +29,8 @@ export interface ReplayHandle {
27
29
  click(c: ClickInfo, rage: boolean): void;
28
30
  /** A client-side navigation. */
29
31
  page(path: string): void;
30
- identify(id: string): void;
32
+ /** The person the site identified, with their identity hash when it has one (0.16.0). */
33
+ identify(id: string, userHash?: string): void;
31
34
  /** Stops recording. `discard` (consent withdrawn) drops everything not sent yet. */
32
35
  stop(discard?: boolean): void;
33
36
  }
package/dist/replay.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/replay.ts
4
+ import { warnUnsigned } from "./identity.js";
4
5
  import {
5
6
  BUFFER_MS,
6
7
  CHECKOUT_MS,
@@ -28,6 +29,7 @@ var startReplay = async (o) => {
28
29
  let trigger = mode === "upload" ? "sample" : null;
29
30
  let distinctId = o.distinctId;
30
31
  let identified = o.identified === true;
32
+ let userHash = o.userHash;
31
33
  let start = null;
32
34
  let seq = 0;
33
35
  let stopped = false;
@@ -66,7 +68,8 @@ var startReplay = async (o) => {
66
68
  if (sync && "sendBeacon" in navigator && navigator.sendBeacon(url, body)) return;
67
69
  try {
68
70
  const r = await fetch(url, { method: "POST", headers: { "content-type": "application/json" }, body, keepalive: sync || body.length < 6e4 });
69
- if (r.status === 403 || r.status === 409 || r.status === 413) end(true);
71
+ warnUnsigned(r.status);
72
+ if (r.status === 401 || r.status === 403 || r.status === 409 || r.status === 413) end(true);
70
73
  } catch {
71
74
  o.warn(`Could not reach ${o.host || "this origin"} to upload a recording.`);
72
75
  }
@@ -85,6 +88,7 @@ var startReplay = async (o) => {
85
88
  sessionId: o.sessionId,
86
89
  distinctId,
87
90
  identified,
91
+ ...userHash ? { userHash } : {},
88
92
  start,
89
93
  seq: seq++,
90
94
  consent: { replay: true },
@@ -187,7 +191,9 @@ var startReplay = async (o) => {
187
191
  }
188
192
  },
189
193
  page: (path) => !stopped && marks.push({ t: Date.now(), kind: "pageview", path: path.slice(0, 300) || "/" }),
190
- identify: (id) => {
194
+ identify: (id, hash) => {
195
+ if (hash) userHash = hash;
196
+ else if (id !== distinctId) userHash = void 0;
191
197
  distinctId = id;
192
198
  identified = true;
193
199
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/replay.ts"],"sourcesContent":["\"use client\";\n\n/**\n * The session recorder. The provider imports this file (and rrweb's recorder with it) only when a visit records: the\n * product has recordings on and the visitor gave replay consent. Pages without recording never load it.\n *\n * A visit in the product's sample uploads from its start. Any other visit keeps the last minute in memory and uploads\n * it only when something happens worth watching: a JS error, a rage click, or a `purchase…` / `checkout…` event; from\n * then on it uploads like a sampled one. Chunks go every 5 seconds or 64 KB, gzip'd where the browser can, to\n * `POST {host}/api/sdk/replay`; the last one leaves with sendBeacon when the page hides. A recording ends after 30\n * minutes. Withdrawing consent stops it and drops what was not sent.\n */\nimport type { ClickInfo } from \"./capture\";\nimport {\n BUFFER_MS,\n CHECKOUT_MS,\n FLUSH_BYTES,\n MAX_RECORDING_MS,\n MAX_REQUEST_BYTES,\n type ReplayEvent,\n type ReplayTrigger,\n RingBuffer,\n chunkDue,\n inSample,\n recordOptions,\n toBase64,\n triggerOfEvent,\n} from \"./replay-core\";\n\nexport interface ReplayOptions {\n host: string;\n apiKey: string;\n /** The SDK's session id; every chunk carries it, so the recording joins the session's events. */\n sessionId: string;\n /** The identify() id or the cookie id; missing cookieless until identify(). */\n distinctId?: string;\n /** Whether `distinctId` comes from identify() (a person) rather than the visitor cookie. */\n identified?: boolean;\n sampleRate: number;\n warn: (message: string) => void;\n}\n\nexport interface ReplayHandle {\n /** A tracked event; `purchase…` and `checkout…` upload the buffered minute. */\n event(name: string): void;\n click(c: ClickInfo, rage: boolean): void;\n /** A client-side navigation. */\n page(path: string): void;\n identify(id: string): void;\n /** Stops recording. `discard` (consent withdrawn) drops everything not sent yet. */\n stop(discard?: boolean): void;\n}\n\n/** One line of the masked event list the server keeps next to the recording (pages, clicks, rage clicks, errors). */\ninterface Mark {\n t: number;\n kind: \"pageview\" | \"click\" | \"rage\" | \"error\";\n path: string;\n label?: string;\n x?: number;\n y?: number;\n vw?: number;\n dead?: boolean;\n}\n\ninterface Held extends ReplayEvent {\n json: string;\n}\n\nconst clip = (s: unknown, n: number) => String(s ?? \"\").slice(0, n);\nconst pathNow = () => window.location.pathname.slice(0, 300) || \"/\";\n\nconst gzip = async (text: string) => {\n const stream = new Blob([text]).stream().pipeThrough(new CompressionStream(\"gzip\"));\n return new Uint8Array(await new Response(stream).arrayBuffer());\n};\n\nexport const startReplay = async (o: ReplayOptions): Promise<ReplayHandle | null> => {\n if (typeof window === \"undefined\") return null;\n const { record } = await import(\"@rrweb/record\");\n\n const url = `${o.host}/api/sdk/replay`;\n let mode: \"upload\" | \"buffer\" = inSample(o.sessionId, o.sampleRate) ? \"upload\" : \"buffer\";\n let trigger: ReplayTrigger | null = mode === \"upload\" ? \"sample\" : null;\n let distinctId = o.distinctId;\n let identified = o.identified === true;\n let start: number | null = null;\n let seq = 0;\n let stopped = false;\n const begun = Date.now();\n const ring = new RingBuffer<Held>();\n let pending: Held[] = [];\n let pendingBytes = 0;\n let lastFlush = Date.now();\n let marks: Mark[] = [{ t: Date.now(), kind: \"pageview\", path: pathNow() }];\n let queue: Promise<void> = Promise.resolve();\n\n const end = (discard: boolean) => {\n if (stopped) return;\n if (!discard && mode === \"upload\") flush(false);\n stopped = true;\n stopRecording?.();\n window.clearInterval(tick);\n window.clearInterval(checkout);\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n window.removeEventListener(\"pagehide\", onHide);\n document.removeEventListener(\"visibilitychange\", onVisibility);\n if (console.error === patched) console.error = original;\n pending = [];\n ring.clear();\n marks = [];\n };\n\n const send = async (payload: Record<string, unknown>, events: string, sync: boolean) => {\n let body: string;\n if (!sync && typeof CompressionStream !== \"undefined\") body = JSON.stringify({ ...payload, enc: \"gzip\", data: toBase64(await gzip(events)) });\n else body = JSON.stringify({ ...payload, enc: \"json\", data: events });\n if (body.length > MAX_REQUEST_BYTES) {\n o.warn(\"A recording chunk was over 1 MB (a very large page). The recording stopped.\");\n end(true);\n return;\n }\n if (sync && \"sendBeacon\" in navigator && navigator.sendBeacon(url, body)) return;\n try {\n const r = await fetch(url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body, keepalive: sync || body.length < 60_000 });\n // Recordings turned off, consent missing, or the recording reached its size cap: stop for this visit.\n if (r.status === 403 || r.status === 409 || r.status === 413) end(true);\n } catch {\n o.warn(`Could not reach ${o.host || \"this origin\"} to upload a recording.`);\n }\n };\n\n function flush(sync: boolean) {\n if (!pending.length) return;\n const events = pending;\n pending = [];\n pendingBytes = 0;\n lastFlush = Date.now();\n start ??= events[0].timestamp;\n const chunkMarks = marks;\n marks = [];\n const payload = {\n key: o.apiKey,\n sessionId: o.sessionId,\n distinctId,\n identified,\n start,\n seq: seq++,\n consent: { replay: true },\n trigger,\n final: sync,\n meta: {\n first: events[0].timestamp,\n last: events[events.length - 1].timestamp,\n vw: window.innerWidth,\n vh: window.innerHeight,\n marks: chunkMarks,\n },\n };\n const json = `[${events.map((e) => e.json).join(\",\")}]`;\n if (sync) void send(payload, json, true);\n else queue = queue.then(() => send(payload, json, false));\n }\n\n const fire = (kind: Exclude<ReplayTrigger, \"sample\">) => {\n if (stopped || mode === \"upload\") return;\n mode = \"upload\";\n trigger = kind;\n window.clearInterval(checkout);\n pending = ring.drain();\n pendingBytes = pending.reduce((s, e) => s + e.json.length, 0);\n flush(false);\n };\n\n const emit = (e: ReplayEvent) => {\n if (stopped) return;\n if (Date.now() - (start ?? begun) > MAX_RECORDING_MS) {\n end(false);\n return;\n }\n const json = JSON.stringify(e);\n const item: Held = { type: e.type, timestamp: e.timestamp, json };\n if (mode === \"buffer\") {\n ring.push(item, json.length);\n const from = ring.first ?? e.timestamp - BUFFER_MS;\n marks = marks.filter((m) => m.t >= from);\n return;\n }\n pending.push(item);\n pendingBytes += json.length;\n if (pendingBytes >= FLUSH_BYTES) flush(false);\n };\n\n const original = console.error;\n const patched = (...args: unknown[]) => {\n try {\n record.addCustomEvent(\"$console\", { level: \"error\", text: clip(args.map((a) => (a instanceof Error ? a.message : typeof a === \"string\" ? a : \"[object]\")).join(\" \"), 300) });\n } catch {\n // Recording must never break the page's own logging.\n }\n original.apply(console, args);\n };\n const onError = (e: ErrorEvent) => {\n const message = clip(e.message || \"Script error\", 200);\n marks.push({ t: Date.now(), kind: \"error\", path: pathNow(), label: message });\n try {\n record.addCustomEvent(\"$error\", { message });\n } catch {\n // ignore\n }\n fire(\"error\");\n };\n const onRejection = (e: PromiseRejectionEvent) => {\n const r = e.reason as { message?: unknown } | undefined;\n const message = clip(typeof r?.message === \"string\" ? r.message : \"Unhandled promise rejection\", 200);\n marks.push({ t: Date.now(), kind: \"error\", path: pathNow(), label: message });\n try {\n record.addCustomEvent(\"$error\", { message });\n } catch {\n // ignore\n }\n fire(\"error\");\n };\n const onHide = () => mode === \"upload\" && flush(true);\n const onVisibility = () => document.visibilityState === \"hidden\" && mode === \"upload\" && flush(false);\n\n const stopRecording = record(recordOptions(emit) as unknown as Parameters<typeof record>[0]);\n if (!stopRecording) return null;\n console.error = patched;\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n window.addEventListener(\"pagehide\", onHide);\n document.addEventListener(\"visibilitychange\", onVisibility);\n const tick = window.setInterval(() => {\n if (Date.now() - (start ?? begun) > MAX_RECORDING_MS) end(false);\n else if (mode === \"upload\" && chunkDue(pendingBytes, Date.now() - lastFlush)) flush(false);\n }, 1000);\n // A buffering visit snapshots every 30 seconds, so the buffer always starts at a full snapshot within the minute.\n const checkout = mode === \"buffer\" ? window.setInterval(() => record.takeFullSnapshot(true), CHECKOUT_MS) : 0;\n\n return {\n event: (name) => {\n const t = triggerOfEvent(name);\n if (t) fire(t);\n },\n click: (c, rage) => {\n if (stopped) return;\n const t = Date.now();\n const path = pathNow();\n const label = c.label || c.selector.split(\" > \").pop() || \"\";\n marks.push({ t, kind: \"click\", path, label, x: c.x, y: c.y, vw: c.vw, dead: !c.interactive });\n if (rage) {\n marks.push({ t, kind: \"rage\", path, label, x: c.x, y: c.y, vw: c.vw });\n fire(\"rage\");\n }\n },\n page: (path) => !stopped && marks.push({ t: Date.now(), kind: \"pageview\", path: path.slice(0, 300) || \"/\" }),\n identify: (id) => {\n distinctId = id;\n identified = true;\n },\n stop: (discard = false) => end(discard),\n };\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/replay.ts"],"sourcesContent":["\"use client\";\n\n/**\n * The session recorder. The provider imports this file (and rrweb's recorder with it) only when a visit records: the\n * product has recordings on and the visitor gave replay consent. Pages without recording never load it.\n *\n * A visit in the product's sample uploads from its start. Any other visit keeps the last minute in memory and uploads\n * it only when something happens worth watching: a JS error, a rage click, or a `purchase…` / `checkout…` event; from\n * then on it uploads like a sampled one. Chunks go every 5 seconds or 64 KB, gzip'd where the browser can, to\n * `POST {host}/api/sdk/replay`; the last one leaves with sendBeacon when the page hides. A recording ends after 30\n * minutes. Withdrawing consent stops it and drops what was not sent.\n */\nimport type { ClickInfo } from \"./capture\";\nimport { warnUnsigned } from \"./identity\";\nimport {\n BUFFER_MS,\n CHECKOUT_MS,\n FLUSH_BYTES,\n MAX_RECORDING_MS,\n MAX_REQUEST_BYTES,\n type ReplayEvent,\n type ReplayTrigger,\n RingBuffer,\n chunkDue,\n inSample,\n recordOptions,\n toBase64,\n triggerOfEvent,\n} from \"./replay-core\";\n\nexport interface ReplayOptions {\n host: string;\n apiKey: string;\n /** The SDK's session id; every chunk carries it, so the recording joins the session's events. */\n sessionId: string;\n /** The identify() id or the cookie id; missing cookieless until identify(). */\n distinctId?: string;\n /** Whether `distinctId` comes from identify() (a person) rather than the visitor cookie. */\n identified?: boolean;\n /** The identity hash of `distinctId` (0.16.0), sent with every chunk while the recording is theirs. */\n userHash?: string;\n sampleRate: number;\n warn: (message: string) => void;\n}\n\nexport interface ReplayHandle {\n /** A tracked event; `purchase…` and `checkout…` upload the buffered minute. */\n event(name: string): void;\n click(c: ClickInfo, rage: boolean): void;\n /** A client-side navigation. */\n page(path: string): void;\n /** The person the site identified, with their identity hash when it has one (0.16.0). */\n identify(id: string, userHash?: string): void;\n /** Stops recording. `discard` (consent withdrawn) drops everything not sent yet. */\n stop(discard?: boolean): void;\n}\n\n/** One line of the masked event list the server keeps next to the recording (pages, clicks, rage clicks, errors). */\ninterface Mark {\n t: number;\n kind: \"pageview\" | \"click\" | \"rage\" | \"error\";\n path: string;\n label?: string;\n x?: number;\n y?: number;\n vw?: number;\n dead?: boolean;\n}\n\ninterface Held extends ReplayEvent {\n json: string;\n}\n\nconst clip = (s: unknown, n: number) => String(s ?? \"\").slice(0, n);\nconst pathNow = () => window.location.pathname.slice(0, 300) || \"/\";\n\nconst gzip = async (text: string) => {\n const stream = new Blob([text]).stream().pipeThrough(new CompressionStream(\"gzip\"));\n return new Uint8Array(await new Response(stream).arrayBuffer());\n};\n\nexport const startReplay = async (o: ReplayOptions): Promise<ReplayHandle | null> => {\n if (typeof window === \"undefined\") return null;\n const { record } = await import(\"@rrweb/record\");\n\n const url = `${o.host}/api/sdk/replay`;\n let mode: \"upload\" | \"buffer\" = inSample(o.sessionId, o.sampleRate) ? \"upload\" : \"buffer\";\n let trigger: ReplayTrigger | null = mode === \"upload\" ? \"sample\" : null;\n let distinctId = o.distinctId;\n let identified = o.identified === true;\n let userHash = o.userHash;\n let start: number | null = null;\n let seq = 0;\n let stopped = false;\n const begun = Date.now();\n const ring = new RingBuffer<Held>();\n let pending: Held[] = [];\n let pendingBytes = 0;\n let lastFlush = Date.now();\n let marks: Mark[] = [{ t: Date.now(), kind: \"pageview\", path: pathNow() }];\n let queue: Promise<void> = Promise.resolve();\n\n const end = (discard: boolean) => {\n if (stopped) return;\n if (!discard && mode === \"upload\") flush(false);\n stopped = true;\n stopRecording?.();\n window.clearInterval(tick);\n window.clearInterval(checkout);\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n window.removeEventListener(\"pagehide\", onHide);\n document.removeEventListener(\"visibilitychange\", onVisibility);\n if (console.error === patched) console.error = original;\n pending = [];\n ring.clear();\n marks = [];\n };\n\n const send = async (payload: Record<string, unknown>, events: string, sync: boolean) => {\n let body: string;\n if (!sync && typeof CompressionStream !== \"undefined\") body = JSON.stringify({ ...payload, enc: \"gzip\", data: toBase64(await gzip(events)) });\n else body = JSON.stringify({ ...payload, enc: \"json\", data: events });\n if (body.length > MAX_REQUEST_BYTES) {\n o.warn(\"A recording chunk was over 1 MB (a very large page). The recording stopped.\");\n end(true);\n return;\n }\n if (sync && \"sendBeacon\" in navigator && navigator.sendBeacon(url, body)) return;\n try {\n const r = await fetch(url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body, keepalive: sync || body.length < 60_000 });\n warnUnsigned(r.status);\n // Recordings turned off, consent missing, the recording reached its size cap, or the person's identity hash\n // is missing or wrong (401): stop for this visit.\n if (r.status === 401 || r.status === 403 || r.status === 409 || r.status === 413) end(true);\n } catch {\n o.warn(`Could not reach ${o.host || \"this origin\"} to upload a recording.`);\n }\n };\n\n function flush(sync: boolean) {\n if (!pending.length) return;\n const events = pending;\n pending = [];\n pendingBytes = 0;\n lastFlush = Date.now();\n start ??= events[0].timestamp;\n const chunkMarks = marks;\n marks = [];\n const payload = {\n key: o.apiKey,\n sessionId: o.sessionId,\n distinctId,\n identified,\n ...(userHash ? { userHash } : {}),\n start,\n seq: seq++,\n consent: { replay: true },\n trigger,\n final: sync,\n meta: {\n first: events[0].timestamp,\n last: events[events.length - 1].timestamp,\n vw: window.innerWidth,\n vh: window.innerHeight,\n marks: chunkMarks,\n },\n };\n const json = `[${events.map((e) => e.json).join(\",\")}]`;\n if (sync) void send(payload, json, true);\n else queue = queue.then(() => send(payload, json, false));\n }\n\n const fire = (kind: Exclude<ReplayTrigger, \"sample\">) => {\n if (stopped || mode === \"upload\") return;\n mode = \"upload\";\n trigger = kind;\n window.clearInterval(checkout);\n pending = ring.drain();\n pendingBytes = pending.reduce((s, e) => s + e.json.length, 0);\n flush(false);\n };\n\n const emit = (e: ReplayEvent) => {\n if (stopped) return;\n if (Date.now() - (start ?? begun) > MAX_RECORDING_MS) {\n end(false);\n return;\n }\n const json = JSON.stringify(e);\n const item: Held = { type: e.type, timestamp: e.timestamp, json };\n if (mode === \"buffer\") {\n ring.push(item, json.length);\n const from = ring.first ?? e.timestamp - BUFFER_MS;\n marks = marks.filter((m) => m.t >= from);\n return;\n }\n pending.push(item);\n pendingBytes += json.length;\n if (pendingBytes >= FLUSH_BYTES) flush(false);\n };\n\n const original = console.error;\n const patched = (...args: unknown[]) => {\n try {\n record.addCustomEvent(\"$console\", { level: \"error\", text: clip(args.map((a) => (a instanceof Error ? a.message : typeof a === \"string\" ? a : \"[object]\")).join(\" \"), 300) });\n } catch {\n // Recording must never break the page's own logging.\n }\n original.apply(console, args);\n };\n const onError = (e: ErrorEvent) => {\n const message = clip(e.message || \"Script error\", 200);\n marks.push({ t: Date.now(), kind: \"error\", path: pathNow(), label: message });\n try {\n record.addCustomEvent(\"$error\", { message });\n } catch {\n // ignore\n }\n fire(\"error\");\n };\n const onRejection = (e: PromiseRejectionEvent) => {\n const r = e.reason as { message?: unknown } | undefined;\n const message = clip(typeof r?.message === \"string\" ? r.message : \"Unhandled promise rejection\", 200);\n marks.push({ t: Date.now(), kind: \"error\", path: pathNow(), label: message });\n try {\n record.addCustomEvent(\"$error\", { message });\n } catch {\n // ignore\n }\n fire(\"error\");\n };\n const onHide = () => mode === \"upload\" && flush(true);\n const onVisibility = () => document.visibilityState === \"hidden\" && mode === \"upload\" && flush(false);\n\n const stopRecording = record(recordOptions(emit) as unknown as Parameters<typeof record>[0]);\n if (!stopRecording) return null;\n console.error = patched;\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n window.addEventListener(\"pagehide\", onHide);\n document.addEventListener(\"visibilitychange\", onVisibility);\n const tick = window.setInterval(() => {\n if (Date.now() - (start ?? begun) > MAX_RECORDING_MS) end(false);\n else if (mode === \"upload\" && chunkDue(pendingBytes, Date.now() - lastFlush)) flush(false);\n }, 1000);\n // A buffering visit snapshots every 30 seconds, so the buffer always starts at a full snapshot within the minute.\n const checkout = mode === \"buffer\" ? window.setInterval(() => record.takeFullSnapshot(true), CHECKOUT_MS) : 0;\n\n return {\n event: (name) => {\n const t = triggerOfEvent(name);\n if (t) fire(t);\n },\n click: (c, rage) => {\n if (stopped) return;\n const t = Date.now();\n const path = pathNow();\n const label = c.label || c.selector.split(\" > \").pop() || \"\";\n marks.push({ t, kind: \"click\", path, label, x: c.x, y: c.y, vw: c.vw, dead: !c.interactive });\n if (rage) {\n marks.push({ t, kind: \"rage\", path, label, x: c.x, y: c.y, vw: c.vw });\n fire(\"rage\");\n }\n },\n page: (path) => !stopped && marks.push({ t: Date.now(), kind: \"pageview\", path: path.slice(0, 300) || \"/\" }),\n identify: (id, hash) => {\n // A hash belongs to its id: another id without one goes unsigned.\n if (hash) userHash = hash;\n else if (id !== distinctId) userHash = undefined;\n distinctId = id;\n identified = true;\n },\n stop: (discard = false) => end(discard),\n };\n};\n"],"mappings":"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","names":[]}
package/dist/server.d.ts CHANGED
@@ -55,6 +55,8 @@ 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
+ /** Identity verification (0.16.0): sign a user id for `identify(id, props, { hash })` with the product's server key. */
59
+ export { type UserHashOptions, userHash } from "./user-hash.js";
58
60
  export { type AttackKind, type SecurityOptions, attackIn, ipOf, reportAuthDenied, reportFailedLogin, reportSuspiciousRequest, watchRequest } from "./security.js";
59
61
  /** Affiliate referrals on the server: read the `mrr_ref` cookie in the route that creates a Stripe Checkout Session. */
60
62
  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 { userHash } from "./user-hash.js";
4
5
  import { attackIn, ipOf, reportAuthDenied, reportFailedLogin, reportSuspiciousRequest, watchRequest } from "./security.js";
5
6
  import { REFERRAL_COOKIE, readReferralCookie, referralForCheckout, referralLink } from "./referral.js";
6
7
  var CONFIG_TIMEOUT_MS = 2500;
@@ -52,6 +53,7 @@ export {
52
53
  reportAuthDenied,
53
54
  reportFailedLogin,
54
55
  reportSuspiciousRequest,
56
+ userHash,
55
57
  watchRequest
56
58
  };
57
59
  //# 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\";\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
+ {"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/** Identity verification (0.16.0): sign a user id for `identify(id, props, { hash })` with the product's server key. */\nexport { type UserHashOptions, userHash } from \"./user-hash\";\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;AAEd,SAA+B,gBAAgB;AAC/C,SAAgD,UAAU,MAAM,kBAAkB,mBAAmB,yBAAyB,oBAAoB;AAElJ,SAA+C,iBAAiB,oBAAoB,qBAAqB,oBAAoB;AA9E7H,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). 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";
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);--ckw-soft:0 2px 8px rgba(16,18,24,.05),0 18px 48px rgba(16,18,24,.15);\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);--ckw-soft:0 0 0 1px rgba(255,255,255,.07),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);--ckw-soft:0 0 0 1px rgba(255,255,255,.07),0 18px 50px rgba(0,0,0,.55)}}\n.ckw-root *,.ckw-root *::before,.ckw-root *::after{box-sizing:border-box}\n.ckw-root{--ckw-r-panel:22px;--ckw-r-bubble:18px;--ckw-r-tail:6px;--ckw-r-card:16px;--ckw-r-field:20px;--ckw-r-pill:999px;--ckw-r-fab:999px}\n.ckw-root[data-corners=soft]{--ckw-r-panel:14px;--ckw-r-bubble:12px;--ckw-r-tail:4px;--ckw-r-card:10px;--ckw-r-field:10px;--ckw-r-pill:10px;--ckw-r-fab:18px}\n.ckw-root[data-corners=square]{--ckw-r-panel:6px;--ckw-r-bubble:6px;--ckw-r-tail:2px;--ckw-r-card:4px;--ckw-r-field:4px;--ckw-r-pill:4px;--ckw-r-fab:8px}\n.ckw-root[data-font=system]{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif}\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 1px 2px rgba(16,18,24,.1),0 6px 20px rgba(16,18,24,.16);transition:transform .3s cubic-bezier(.32,.72,0,1),box-shadow .2s linear}\n.ckw-msgr-fab:hover{transform:scale(1.05);box-shadow:0 1px 2px rgba(16,18,24,.1),0 10px 28px rgba(16,18,24,.2)}\n.ckw-msgr-fab:active{transform:scale(.94);transition-duration:.1s}\n.ckw-msgr-round{width:56px;height:56px;padding:0;justify-content:center}\n.ckw-msgr-fab{border-radius:var(--ckw-r-fab)}\n.ckw-msgr-fab-ic{position:relative;display:block;width:20px;height:20px;flex:none}\n.ckw-msgr-round .ckw-msgr-fab-ic{width:27px;height:27px}\n.ckw-msgr-fab .ckw-cut{stroke:var(--ckw-accent)}\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.2}\n.ckw-msgr-count{position:absolute;top:-2px;right:-2px;min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:#ef4444;color:#fff;font-size:10.5px;font-weight:700;line-height:18px;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 40px 14px 16px;border:0;border-radius:calc(var(--ckw-r-card) + 2px);background:var(--ckw-bg);box-shadow:var(--ckw-soft);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:26px;height:26px;border-radius:50%;border:0;background:transparent;color:var(--ckw-fg3);display:flex;align-items:center;justify-content:center;padding:0;transition:background-color .12s linear,color .12s linear}\n.ckw-msgr-preview-x:hover{background:var(--ckw-bg2);color:var(--ckw-fg)}\n.ckw-msgr-teaser{width:340px;padding:16px;border-radius:calc(var(--ckw-r-card) + 4px);background:var(--ckw-bg);box-shadow:var(--ckw-soft);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:var(--ckw-r-pill);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:var(--ckw-r-panel);box-shadow:var(--ckw-soft);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-count-tab{top:-1px;right:-3px;min-width:0;width:8px;height:8px;padding:0;font-size:0;line-height:0;box-shadow:0 0 0 2px var(--ckw-bg)}\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:6px;padding:10px 4px 8px}\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:14px 0 0;font-size:28px;font-weight:650;letter-spacing:-.03em;line-height:1.12;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:var(--ckw-r-card);background:var(--ckw-bg2);transition:background-color .15s linear,box-shadow .15s linear,transform .25s cubic-bezier(.32,.72,0,1)}\nbutton.ckw-msgr-card:hover{background:var(--ckw-bg3)}\n.ckw-msgr-card-col .ckw-msgr-search{background:var(--ckw-bg)}\n.ckw-msgr-card .ckw-msgr-art:hover{background:var(--ckw-bg3)}\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:var(--ckw-r-field);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-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:8px 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:var(--ckw-r-bubble) var(--ckw-r-bubble) var(--ckw-r-bubble) var(--ckw-r-tail);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:var(--ckw-r-bubble) var(--ckw-r-bubble) var(--ckw-r-tail) var(--ckw-r-bubble)}\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:0;border-radius:var(--ckw-r-card);background:var(--ckw-bg2);font-size:13.5px;text-align:left;color:var(--ckw-fg);transition:background-color .12s linear,color .12s linear,transform .2s cubic-bezier(.32,.72,0,1)}\n.ckw-msgr-sugg-b:hover{background:var(--ckw-bg3);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:var(--ckw-r-field);background:var(--ckw-bg2);min-height:40px;padding:8px 16px}\n.ckw-root .ckw-msgr-compose .ckw-input:focus-visible{outline:none;background:var(--ckw-bg3)}\n.ckw-msgr-box{display:flex;flex-direction:column;align-items:stretch;gap:4px;padding:12px 8px 8px 16px;border:0;border-radius:calc(var(--ckw-r-card) + 4px);background:var(--ckw-bg2);box-shadow:0 0 0 1px color-mix(in srgb,var(--ckw-line) 70%,transparent);cursor:text;transition:background-color .15s linear,box-shadow .15s linear}\n.ckw-msgr-box:focus-within{background:var(--ckw-bg);box-shadow:0 0 0 1px var(--ckw-line),0 6px 18px rgba(16,18,24,.07)}\n.ckw-msgr-tools{display:flex;align-items:center;gap:2px;margin-left:-8px}\n.ckw-msgr-sp{flex:1}\n.ckw-msgr-input{flex:1;min-width:0;min-height:24px;max-height:140px;padding:2px 8px 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-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/* Files: the paperclip in the field, picked files above it, sent ones in the bubbles, dropping onto the panel */\n.ckw-msgr-compose{position:relative}\n.ckw-msgr-clip{width:32px;height:32px;padding:0;flex:none;display:flex;align-items:center;justify-content:center;border:0;border-radius:calc(var(--ckw-r-field) - 4px);background:transparent;color:var(--ckw-fg3);transition:color .12s linear,background-color .12s linear,transform .2s cubic-bezier(.32,.72,0,1)}\n.ckw-msgr-clip:hover,.ckw-msgr-clip[aria-expanded=true]{color:var(--ckw-fg);background:color-mix(in srgb,var(--ckw-fg) 7%,transparent)}\n.ckw-msgr-clip:active{transform:scale(.9)}\n\n.ckw-msgr-amenu{top:auto;bottom:calc(100% - 4px);right:14px;transform-origin:bottom right}\n.ckw-msgr-files{display:flex;flex-wrap:wrap;gap:10px;padding:6px 2px 0}\n.ckw-msgr-file{position:relative;width:56px;height:56px;border-radius:calc(var(--ckw-r-card) - 4px);background:var(--ckw-bg2);box-shadow:0 0 0 1px var(--ckw-line)}\n.ckw-msgr-file img{display:block;width:100%;height:100%;object-fit:cover;border-radius:inherit}\n.ckw-msgr-file[aria-busy=true] img,.ckw-msgr-file[aria-busy=true] .ckw-msgr-file-doc{opacity:.45}\n.ckw-msgr-file[aria-busy=true]::after{content:\"\";position:absolute;left:50%;top:50%;width:18px;height:18px;margin:-9px 0 0 -9px;border:2px solid var(--ckw-fg2);border-right-color:transparent;border-radius:50%;animation:ckw-spin .8s linear infinite}\n@keyframes ckw-spin{to{transform:rotate(360deg)}}\n.ckw-msgr-file-doc{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;width:100%;height:100%;padding:4px;color:var(--ckw-fg2);font-size:10px;line-height:1.2}\n.ckw-msgr-file-doc span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-msgr-file-x{position:absolute;top:-7px;right:-7px;width:20px;height:20px;padding:0;display:flex;align-items:center;justify-content:center;border:2px solid var(--ckw-bg);border-radius:50%;background:var(--ckw-fg);color:var(--ckw-bg)}\n.ckw-msgr-file-x .ckw-icon{width:10px;height:10px}\n.ckw-msg-files{display:flex;flex-wrap:wrap;gap:6px;max-width:100%}\n.ckw-msg[data-from=me] .ckw-msg-files{justify-content:flex-end}\n.ckw-msg-img{display:block;overflow:hidden;border-radius:var(--ckw-r-bubble);background:var(--ckw-bg3);box-shadow:0 0 0 1px var(--ckw-line)}\n.ckw-msg-img img{display:block;max-width:220px;max-height:200px;min-width:48px;min-height:48px;object-fit:cover}\n.ckw-msg-files a.ckw-msgr-cite{color:var(--ckw-fg);text-decoration:none}\n.ckw-msgr[data-drop]::after{content:\"Drop to attach\";position:absolute;inset:8px;z-index:5;display:flex;align-items:center;justify-content:center;border:2px dashed var(--ckw-accent);border-radius:calc(var(--ckw-r-panel) - 6px);background:color-mix(in srgb,var(--ckw-bg) 90%,transparent);color:var(--ckw-fg);font-size:15px;font-weight:600;pointer-events:none}\n/* Powered by */\n.ckw-msgr-powered{flex:none;display:block;padding:2px 0 10px;text-align:center;font-size:11.5px;color:var(--ckw-fg3);text-decoration:none;background:var(--ckw-bg)}\n.ckw-msgr-powered b{font-weight:650;color:var(--ckw-fg2)}\n.ckw-msgr-powered:hover b{color:var(--ckw-fg)}\n.ckw-msgr-compose:has(+.ckw-msgr-powered){padding-bottom:8px}\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/* Bar middle, tab bar, Home's glow and \"Ask a question\" (Featurebase-like) */\n.ckw-msgr-mid{display:flex;align-items:center;justify-content:center;gap:10px;min-width:0;max-width:62%;flex:none}\n.ckw-msgr-mid b{font-size:15px;font-weight:650;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.ckw-msgr-mid-t{display:flex;flex-direction:column;gap:1px;min-width:0;line-height:1.25}\n.ckw-msgr-mid-t .ckw-msgr-online{font-size:11.5px;white-space:nowrap}\n.ckw-msgr-mid-t .ckw-msgr-online::before{width:6px;height:6px;box-shadow:none}\n.ckw-msgr-tabs{flex:none;display:flex;padding:6px 10px 4px;background:var(--ckw-bg);box-shadow:0 -1px 0 color-mix(in srgb,var(--ckw-line) 60%,transparent)}\n.ckw-msgr-tab{flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;padding:6px 0 4px;border:0;border-radius:12px;background:none;color:var(--ckw-fg3);font-size:12px;font-weight:550;transition:color .15s linear}\n.ckw-msgr-tab:hover{color:var(--ckw-fg2)}\n.ckw-msgr-tab[aria-current=page]{color:var(--ckw-fg)}\n.ckw-msgr-tab-ic{position:relative;display:block;width:22px;height:22px;transition:transform .25s cubic-bezier(.34,1.56,.64,1)}\n.ckw-msgr-tab:active .ckw-msgr-tab-ic{transform:scale(.88)}\n.ckw-msgr-tab-ic>.ckw-icon{position:absolute;inset:0;transition:opacity .15s linear,transform .3s cubic-bezier(.34,1.56,.64,1)}\n.ckw-msgr-tab-ic>.ckw-icon+.ckw-icon{opacity:0;transform:scale(.75)}\n.ckw-msgr-tab[aria-current=page] .ckw-msgr-tab-ic>.ckw-icon:first-child{opacity:0}\n.ckw-msgr-tab[aria-current=page] .ckw-msgr-tab-ic>.ckw-icon+.ckw-icon{opacity:1;transform:none}\n.ckw-cut{stroke:var(--ckw-bg)}\n.ckw-root:not([data-header=color]) .ckw-msgr[data-view=home] :is(.ckw-msgr-bar,.ckw-msgr-body){background:transparent}\n.ckw-msgr-askq{display:flex;align-items:center;gap:8px;height:52px;margin:6px 0 4px;padding:0 7px 0 18px;border-radius:var(--ckw-r-field);background:var(--ckw-bg2);box-shadow:0 0 0 1px color-mix(in srgb,var(--ckw-line) 70%,transparent);transition:background-color .15s linear,box-shadow .15s linear}\n.ckw-msgr-askq:focus-within{background:var(--ckw-bg);box-shadow:0 0 0 1px var(--ckw-line),0 6px 18px rgba(16,18,24,.07)}\n.ckw-msgr-askq-in{flex:1;min-width:0;border:0;background:transparent;outline:none;font-size:15px;color:var(--ckw-fg)}\n.ckw-msgr-askq-in::placeholder{color:var(--ckw-fg3)}\n.ckw-root .ckw-msgr-askq-in:focus-visible{outline:none}\n.ckw-msgr-askq .ckw-msgr-send{width:38px;height:38px}\n.ckw-root[data-header=color] .ckw-msgr-askq{background:var(--ckw-card);box-shadow:var(--ckw-raise)}\n/* Home in the accent color (look: header \"color\"): the bar and the top in the accent, raised cards over its edge */\n.ckw-root[data-header=color] .ckw-msgr[data-view=home] .ckw-msgr-bar{background:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-root[data-header=color] .ckw-msgr[data-view=home] .ckw-msgr-ib{color:color-mix(in srgb,var(--ckw-accent-ink) 80%,transparent)}\n.ckw-root[data-header=color] .ckw-msgr[data-view=home] .ckw-msgr-ib:hover,.ckw-root[data-header=color] .ckw-msgr[data-view=home] .ckw-msgr-ib[aria-expanded=true]{background:color-mix(in srgb,var(--ckw-accent-ink) 14%,transparent);color:var(--ckw-accent-ink)}\n.ckw-root[data-header=color] .ckw-msgr[data-view=home] .ckw-msgr-body,.ckw-root[data-header=color] .ckw-msgr[data-view=home] .ckw-msgr-powered{background:var(--ckw-bg2)}\n.ckw-root[data-header=color] .ckw-msgr[data-view=home] .ckw-msgr-body{scrollbar-width:none}\n.ckw-root[data-header=color] .ckw-msgr-hero{margin:-4px -16px 0;padding:14px 20px 64px;background:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-root[data-header=color] .ckw-msgr-hero .ckw-msgr-brand,.ckw-root[data-header=color] .ckw-msgr-hero .ckw-msgr-online{color:color-mix(in srgb,var(--ckw-accent-ink) 82%,transparent)}\n.ckw-root[data-header=color] .ckw-msgr-hero .ckw-msgr-av{background:var(--ckw-accent-ink);color:var(--ckw-accent)}\n.ckw-root[data-header=color] .ckw-msgr-hero+*{margin-top:-58px}\n.ckw-root[data-header=color] .ckw-msgr-card{background:var(--ckw-card);box-shadow:var(--ckw-raise)}\n.ckw-root[data-header=color] button.ckw-msgr-card:hover{background:var(--ckw-card);transform:translateY(-1px)}\n.ckw-root[data-header=color] .ckw-msgr-card-col .ckw-msgr-search{background:var(--ckw-bg2)}\n.ckw-root[data-header=color] .ckw-msgr-card .ckw-msgr-art:hover{background:var(--ckw-bg2)}\n/* Where it sits: a corner, the middle of the top or bottom edge, or the middle of a side; panel and cards open from the launcher */\n.ckw-msgr-fab[data-pos$=left],.ckw-msgr[data-pos$=left],.ckw-msgr-preview[data-pos$=left],.ckw-msgr-teaser[data-pos$=left]{left:20px;right:auto}\n.ckw-msgr-fab[data-pos$=right],.ckw-msgr[data-pos$=right],.ckw-msgr-preview[data-pos$=right],.ckw-msgr-teaser[data-pos$=right]{right:20px;left:auto}\n.ckw-msgr-fab[data-pos$=center],.ckw-msgr[data-pos$=center],.ckw-msgr-preview[data-pos$=center],.ckw-msgr-teaser[data-pos$=center]{left:50%;right:auto;translate:-50% 0}\n.ckw-msgr-fab[data-pos^=top]{top:20px;bottom:auto}\n.ckw-msgr[data-pos^=top],.ckw-msgr-preview[data-pos^=top],.ckw-msgr-teaser[data-pos^=top]{top:92px;bottom:auto}\n.ckw-msgr-fab[data-pos^=middle],.ckw-msgr[data-pos^=middle],.ckw-msgr-preview[data-pos^=middle],.ckw-msgr-teaser[data-pos^=middle]{top:50%;bottom:auto;translate:0 -50%}\n.ckw-msgr[data-pos=middle-left],.ckw-msgr-preview[data-pos=middle-left],.ckw-msgr-teaser[data-pos=middle-left]{left:92px}\n.ckw-msgr[data-pos=middle-right],.ckw-msgr-preview[data-pos=middle-right],.ckw-msgr-teaser[data-pos=middle-right]{right:92px}\n.ckw-msgr[data-pos^=middle]{height:min(680px,calc(100vh - 40px))}\n.ckw-msgr[data-pos=top-right],.ckw-msgr-preview[data-pos=top-right],.ckw-msgr-teaser[data-pos=top-right]{transform-origin:top right}\n.ckw-msgr[data-pos=top-left],.ckw-msgr-preview[data-pos=top-left],.ckw-msgr-teaser[data-pos=top-left]{transform-origin:top left}\n.ckw-msgr[data-pos=top-center],.ckw-msgr-preview[data-pos=top-center],.ckw-msgr-teaser[data-pos=top-center]{transform-origin:top center}\n.ckw-msgr[data-pos=bottom-center],.ckw-msgr-preview[data-pos=bottom-center],.ckw-msgr-teaser[data-pos=bottom-center]{transform-origin:bottom center}\n.ckw-msgr[data-pos=middle-left],.ckw-msgr-preview[data-pos=middle-left],.ckw-msgr-teaser[data-pos=middle-left]{transform-origin:center left}\n.ckw-msgr[data-pos=middle-right],.ckw-msgr-preview[data-pos=middle-right],.ckw-msgr-teaser[data-pos=middle-right]{transform-origin:center right}\n@media (max-width:520px){.ckw-root:not([data-contained]) .ckw-msgr{top:0;translate:none}.ckw-msgr-fab[data-pos$=left]{left:14px}.ckw-msgr-fab[data-pos$=right]{right:14px}.ckw-msgr-fab[data-pos^=top]{top:14px}.ckw-msgr-preview[data-pos^=top],.ckw-msgr-teaser[data-pos^=top]{top:84px}.ckw-msgr-preview[data-pos^=middle],.ckw-msgr-teaser[data-pos^=middle]{top:auto;bottom:20px;left:16px;right:16px;width:auto;translate:none}}\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.ckw-root[data-contained] .ckw-msgr[data-pos^=middle]{height:min(680px,calc(100% - 40px));max-width:calc(100% - 112px)}\n.ckw-root[data-contained] .ckw-msgr-preview[data-pos^=middle],.ckw-root[data-contained] .ckw-msgr-teaser[data-pos^=middle]{max-width: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-tab-ic,.ckw-msgr-tab-ic>.ckw-icon,.ckw-msgr{transition:none}}\n/* The messenger in HeroUI v3's look (heroui.com, its free components): a soft gray ground, white surfaces with a hairline\n shadow, white fields, pill buttons and a pill tab switch, a 2px ring in the accent on focus. Its tokens (--hu-*) map onto\n the widget's own, inside the messenger only, so the other widgets keep theirs. */\n.ckw-root{--hu-bg:#fff;--hu-surface:#f4f4f5;--hu-default:#ececee;--hu-default-hover:#e4e4e7;--hu-segment:#fff;--hu-muted:#71717a;--hu-fg:#18181b;--hu-border:#e4e4e7;--hu-shadow:0 0 0 0 transparent;--hu-lift:0 1px 2px rgba(0,0,0,.08),0 0 1px rgba(0,0,0,.1);--hu-overlay:0 0 0 1px rgba(0,0,0,.05),0 2px 8px rgba(0,0,0,.05),0 18px 40px rgba(0,0,0,.1)}\n.ckw-root[data-theme=dark]{--hu-bg:#111113;--hu-surface:#1c1c1f;--hu-default:#26262a;--hu-default-hover:#2e2e33;--hu-segment:#3a3a40;--hu-muted:#a1a1aa;--hu-fg:#fafafa;--hu-border:#27272a;--hu-shadow:0 0 0 0 transparent;--hu-lift:0 0 0 1px rgba(255,255,255,.06);--hu-overlay:0 0 0 1px rgba(255,255,255,.08),0 18px 40px rgba(0,0,0,.55)}\n@media (prefers-color-scheme:dark){.ckw-root[data-theme=auto]{--hu-bg:#111113;--hu-surface:#1c1c1f;--hu-default:#26262a;--hu-default-hover:#2e2e33;--hu-segment:#3a3a40;--hu-muted:#a1a1aa;--hu-fg:#fafafa;--hu-border:#27272a;--hu-shadow:0 0 0 0 transparent;--hu-lift:0 0 0 1px rgba(255,255,255,.06);--hu-overlay:0 0 0 1px rgba(255,255,255,.08),0 18px 40px rgba(0,0,0,.55)}}\n.ckw-msgr,.ckw-msgr-preview,.ckw-msgr-teaser{--ckw-bg:var(--hu-bg);--ckw-bg2:var(--hu-surface);--ckw-bg3:var(--hu-default);--ckw-card:var(--hu-surface);--ckw-fg:var(--hu-fg);--ckw-fg2:var(--hu-muted);--ckw-fg3:var(--hu-muted);--ckw-line:var(--hu-border);--ckw-soft:var(--hu-overlay);--ckw-raise:var(--hu-shadow)}\n.ckw-root .ckw-msgr-preview-card,.ckw-root .ckw-msgr-teaser{background:var(--hu-surface)}\n.ckw-root .ckw-msgr-card,.ckw-root .ckw-msgr-sugg-b,.ckw-root .ckw-msgr-rate,.ckw-root .ckw-msgr-cite{background:var(--hu-surface);box-shadow:var(--hu-shadow);border:0}\n.ckw-root button.ckw-msgr-card:hover{background:var(--hu-default)}\n.ckw-root .ckw-msgr-card-col{padding:6px}\n.ckw-root .ckw-msgr-card .ckw-msgr-art:hover,.ckw-root .ckw-msgr-hrow:hover{background:var(--hu-default)}\n.ckw-root .ckw-msgr-hrow+.ckw-msgr-hrow{box-shadow:0 -1px 0 var(--hu-border)}\n.ckw-root .ckw-msgr-search,.ckw-root .ckw-msgr-askq,.ckw-root .ckw-msgr-box,.ckw-root .ckw-msgr-compose .ckw-input{background:var(--hu-surface);border:0;box-shadow:var(--hu-shadow)}\n.ckw-root .ckw-msgr-search:focus-within,.ckw-root .ckw-msgr-askq:focus-within,.ckw-root .ckw-msgr-box:focus-within,.ckw-root .ckw-msgr-compose .ckw-input:focus-visible{background:var(--hu-surface);box-shadow:0 0 0 1.5px color-mix(in srgb,var(--ckw-accent) 55%,transparent),0 0 0 5px color-mix(in srgb,var(--ckw-accent) 12%,transparent);color:var(--ckw-fg3)}\n.ckw-root .ckw-msgr-askq{height:48px;border-radius:24px}\n.ckw-root .ckw-msgr-box{border-radius:20px}\n.ckw-root .ckw-msgr-send{border-radius:999px}\n.ckw-root .ckw-msgr-send:disabled{background:var(--hu-default);color:var(--hu-muted)}\n.ckw-root .ckw-msgr-send:not(:disabled):hover{transform:none;filter:brightness(1.08)}\n.ckw-root .ckw-msgr-send:not(:disabled):active{transform:scale(.97)}\n.ckw-root .ckw-bubble{background:var(--hu-surface);box-shadow:var(--hu-shadow)}\n.ckw-root .ckw-msg[data-from=me] .ckw-bubble{box-shadow:none}\n.ckw-root .ckw-msgr-av{background:color-mix(in srgb,var(--ckw-accent) 16%,var(--hu-surface));color:var(--ckw-accent);font-weight:600}\n.ckw-root .ckw-msgr-ib:hover,.ckw-root .ckw-msgr-ib[aria-expanded=true]{background:var(--hu-default)}\n.ckw-root .ckw-msgr-menu{background:var(--hu-surface);box-shadow:var(--hu-overlay);border-radius:16px}\n.ckw-root .ckw-msgr-menu-i{border-radius:10px}\n.ckw-root .ckw-msgr-menu-i:hover,.ckw-root .ckw-msgr-menu-i:focus-visible{background:var(--hu-default)}\n.ckw-root .ckw-msgr .ckw-btn,.ckw-root .ckw-msgr-teaser-b{height:36px;padding:0 16px;border:0;border-radius:999px;background:var(--hu-default);color:var(--hu-fg);font-size:14px;font-weight:500;box-shadow:none;transition:transform .25s cubic-bezier(.32,.72,0,1),background-color .1s linear}\n.ckw-root .ckw-msgr .ckw-btn:hover,.ckw-root .ckw-msgr-teaser-b:hover{background:var(--hu-default-hover);filter:none}\n.ckw-root .ckw-msgr .ckw-btn:active,.ckw-root .ckw-msgr-teaser-b:active{transform:scale(.97)}\n.ckw-root .ckw-msgr .ckw-btn-primary,.ckw-root .ckw-msgr-teaser-b[data-main]{background:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-root .ckw-msgr .ckw-btn-primary:hover,.ckw-root .ckw-msgr-teaser-b[data-main]:hover{background:var(--ckw-accent);filter:brightness(1.08)}\n.ckw-root .ckw-msgr-sugg-b{border-radius:999px}\n.ckw-root .ckw-msgr-ask{height:40px;border-radius:999px}\n/* Home like Featurebase: the product's icon top left, \"Hi, Lukas \uD83D\uDC4B\" over the greeting in gray, a silk of the accent\n with drifting dots behind the top right (.ckw-msgr-sky, also drawn by MRRocket's phone preview) */\n.ckw-msgr-logo{display:flex;align-items:center;justify-content:center;width:32px;height:32px;flex:none;margin-left:6px}\n.ckw-msgr-logo img{width:28px;height:28px;border-radius:8px;object-fit:contain}\n.ckw-root .ckw-msgr[data-view=home] .ckw-msgr-bar{padding-top:16px}\n.ckw-root .ckw-msgr[data-view=home] .ckw-msgr-body{position:relative;z-index:1;gap:10px;padding-top:0}\n.ckw-root .ckw-msgr-hero.ckw-msgr-hello{display:flex;flex-direction:column;gap:1px;margin:22px 0 10px;padding:0 4px;font-size:25px;font-weight:600;letter-spacing:-.028em;line-height:1.2;color:var(--hu-fg)}\n.ckw-msgr-hello-sub{color:var(--hu-muted);font-weight:500}\n.ckw-msgr-wave{display:inline-block;transform-origin:70% 75%;animation:ckw-wave 2.4s ease-in-out .6s 1 both}\n@keyframes ckw-wave{0%,60%,100%{transform:none}10%,30%{transform:rotate(14deg)}20%{transform:rotate(-8deg)}40%{transform:rotate(-4deg)}50%{transform:rotate(10deg)}}\n.ckw-root .ckw-msgr[data-view=home] .ckw-msgr-askq{margin:0 0 6px}\n.ckw-msgr-sky{position:absolute;z-index:0;top:0;left:0;right:0;height:250px;pointer-events:none;overflow:hidden;border-radius:inherit;-webkit-mask-image:linear-gradient(to bottom,#000 25%,transparent 92%);mask-image:linear-gradient(to bottom,#000 25%,transparent 92%)}\n.ckw-msgr-sky::before{content:\"\";position:absolute;top:-45%;right:-25%;width:115%;height:135%;background:radial-gradient(36% 40% at 62% 46%,color-mix(in srgb,var(--ckw-accent) 92%,#fff) 0%,transparent 70%),radial-gradient(32% 36% at 86% 24%,color-mix(in srgb,var(--ckw-glow2,var(--ckw-accent)) 70%,#fff) 0%,transparent 72%),radial-gradient(46% 30% at 36% 64%,color-mix(in srgb,var(--ckw-glow3,var(--ckw-glow2,var(--ckw-accent))) 50%,#fff) 0%,transparent 70%);filter:blur(20px);opacity:.75;animation:ckw-silk 16s cubic-bezier(.45,0,.55,1) infinite alternate}\n@keyframes ckw-silk{to{transform:translate(-7%,5%) rotate(8deg) scale(1.08)}}\n.ckw-msgr-sky::after{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,color-mix(in srgb,var(--ckw-accent) 85%,#fff) .9px,transparent 1.4px);background-size:5px 5px;-webkit-mask-image:linear-gradient(110deg,transparent 0%,#000 18%,transparent 34%,#000 52%,transparent 70%),radial-gradient(70% 85% at 88% 12%,#000 15%,transparent 75%);-webkit-mask-size:220% 100%,100% 100%;-webkit-mask-repeat:repeat-x,no-repeat;-webkit-mask-composite:source-in;mask-image:linear-gradient(110deg,transparent 0%,#000 18%,transparent 34%,#000 52%,transparent 70%),radial-gradient(70% 85% at 88% 12%,#000 15%,transparent 75%);mask-size:220% 100%,100% 100%;mask-repeat:repeat-x,no-repeat;mask-composite:intersect;opacity:.6;animation:ckw-dots 11s linear infinite}\n@keyframes ckw-dots{from{-webkit-mask-position:0 0,0 0;mask-position:0 0,0 0}to{-webkit-mask-position:-220% 0,0 0;mask-position:-220% 0,0 0}}\n.ckw-root[data-theme=dark] .ckw-msgr-sky::before{opacity:.4}\n.ckw-root[data-theme=dark] .ckw-msgr-sky::after{opacity:.4}\n@media (prefers-color-scheme:dark){.ckw-root[data-theme=auto] .ckw-msgr-sky::before{opacity:.4}.ckw-root[data-theme=auto] .ckw-msgr-sky::after{opacity:.4}}\n.ckw-root[data-header=color] .ckw-msgr-sky{display:none}\n@media (prefers-reduced-motion:reduce){.ckw-msgr-sky::before,.ckw-msgr-sky::after,.ckw-msgr-wave{animation:none}}\n/* Home's latest message: one quiet row, the small mark, the words, who and when */\n.ckw-root .ckw-msgr-recent{padding:10px 12px;gap:0}\n.ckw-root .ckw-msgr-recent .ckw-msgr-recent-row{gap:10px}\n.ckw-root .ckw-msgr-recent .ckw-msgr-card-main{gap:1px}\n.ckw-root .ckw-msgr-recent-text{font-size:13.5px;font-weight:500}\n.ckw-root .ckw-msgr-recent .ckw-msgr-card-meta{font-size:11.5px}\n/* The tab switch: HeroUI's Tabs, a gray pill with the current tab on a raised white one */\n.ckw-root .ckw-msgr-tabs{position:relative;z-index:1;margin:2px 12px 8px;padding:4px;gap:0;border-radius:999px;background:var(--hu-surface);box-shadow:none}\n.ckw-root .ckw-msgr-tab{flex-direction:row;justify-content:center;gap:7px;height:36px;padding:0 10px;border-radius:999px;color:var(--hu-muted);font-size:13.5px;font-weight:500;transition:color .15s ease,background-color .15s ease,box-shadow .15s ease,opacity .15s ease}\n.ckw-root .ckw-msgr-tab:not([aria-current=page]):hover{color:var(--hu-muted);opacity:.7}\n.ckw-root .ckw-msgr-tab[aria-current=page]{background:var(--hu-segment);color:var(--hu-fg);box-shadow:var(--hu-lift)}\n.ckw-root .ckw-msgr-tab-ic{width:18px;height:18px}\n.ckw-root .ckw-msgr-tab .ckw-cut{stroke:var(--hu-segment)}\n.ckw-root .ckw-msgr-count-tab{box-shadow:0 0 0 2px var(--hu-default)}\n.ckw-root .ckw-msgr-tab[aria-current=page] .ckw-msgr-count-tab{box-shadow:0 0 0 2px var(--hu-segment)}\n.ckw-root .ckw-msgr-powered{background:var(--hu-bg);padding-top:0}\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;