diff --git a/.env.example b/.env.example
index 8ccabe455b7f4c..5fccf93ee8f656 100644
--- a/.env.example
+++ b/.env.example
@@ -342,3 +342,6 @@ APP_ROUTER_TEAMS_ENABLED=0
# disable setry server source maps
SENTRY_DISABLE_SERVER_WEBPACK_PLUGIN=1
+
+# api v2
+NEXT_PUBLIC_API_V2_URL="http://localhost:5555/api/v2"
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
index 7944833941fa19..466e374a2a88d4 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -16,3 +16,4 @@ public
packages/prisma/zod
packages/prisma/enums
apps/web/public/embed
+apps/api/v2/swagger/documentation.json
diff --git a/apps/api/index.js b/apps/api/index.js
new file mode 100644
index 00000000000000..a7e4c44ce3e501
--- /dev/null
+++ b/apps/api/index.js
@@ -0,0 +1,18 @@
+const http = require("http");
+const connect = require("connect");
+const { createProxyMiddleware } = require("http-proxy-middleware");
+
+const apiProxyV1 = createProxyMiddleware({
+ target: "http://localhost:3003",
+});
+
+const apiProxyV2 = createProxyMiddleware({
+ target: "http://localhost:3004",
+});
+
+const app = connect();
+app.use("/", apiProxyV1);
+
+app.use("/v2", apiProxyV2);
+
+http.createServer(app).listen(3002);
diff --git a/apps/api/package.json b/apps/api/package.json
index 75040b614c8b79..252bd4f1772b8f 100644
--- a/apps/api/package.json
+++ b/apps/api/package.json
@@ -1,46 +1,16 @@
{
- "name": "@calcom/api",
+ "name": "@calcom/api-proxy",
"version": "1.0.0",
- "description": "Public API for Cal.com",
- "main": "index.ts",
- "repository": "git@github.com:calcom/api.git",
- "author": "Cal.com Inc.",
- "private": true,
+ "description": "",
+ "main": "index.js",
"scripts": {
- "build": "next build",
- "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
- "dev": "PORT=3002 next dev",
- "lint": "eslint . --ignore-path .gitignore",
- "lint:fix": "eslint . --ext .ts,.js,.tsx,.jsx --fix",
- "start": "PORT=3002 next start",
- "docker-start-api": "PORT=80 next start",
- "type-check": "tsc --pretty --noEmit"
- },
- "devDependencies": {
- "@calcom/tsconfig": "*",
- "@calcom/types": "*",
- "node-mocks-http": "^1.11.0"
+ "dev": "node ./index.js"
},
+ "author": "",
+ "license": "ISC",
"dependencies": {
- "@calcom/app-store": "*",
- "@calcom/core": "*",
- "@calcom/dayjs": "*",
- "@calcom/emails": "*",
- "@calcom/features": "*",
- "@calcom/lib": "*",
- "@calcom/prisma": "*",
- "@calcom/trpc": "*",
- "@sentry/nextjs": "^7.73.0",
- "bcryptjs": "^2.4.3",
- "memory-cache": "^0.2.0",
- "next": "^13.5.4",
- "next-api-middleware": "^1.0.1",
- "next-axiom": "^0.17.0",
- "next-swagger-doc": "^0.3.6",
- "next-validations": "^0.2.0",
- "typescript": "^4.9.4",
- "tzdata": "^1.0.30",
- "uuid": "^8.3.2",
- "zod": "^3.22.4"
+ "connect": "^3.7.0",
+ "http": "^0.0.1-security",
+ "http-proxy-middleware": "^2.0.6"
}
}
diff --git a/apps/api/.env.example b/apps/api/v1/.env.example
similarity index 100%
rename from apps/api/.env.example
rename to apps/api/v1/.env.example
diff --git a/apps/api/.gitignore b/apps/api/v1/.gitignore
similarity index 100%
rename from apps/api/.gitignore
rename to apps/api/v1/.gitignore
diff --git a/apps/api/.gitkeep b/apps/api/v1/.gitkeep
similarity index 100%
rename from apps/api/.gitkeep
rename to apps/api/v1/.gitkeep
diff --git a/apps/api/.prettierignore b/apps/api/v1/.prettierignore
similarity index 100%
rename from apps/api/.prettierignore
rename to apps/api/v1/.prettierignore
diff --git a/apps/api/LICENSE b/apps/api/v1/LICENSE
similarity index 100%
rename from apps/api/LICENSE
rename to apps/api/v1/LICENSE
diff --git a/apps/api/README.md b/apps/api/v1/README.md
similarity index 100%
rename from apps/api/README.md
rename to apps/api/v1/README.md
diff --git a/apps/api/lib/constants.ts b/apps/api/v1/lib/constants.ts
similarity index 100%
rename from apps/api/lib/constants.ts
rename to apps/api/v1/lib/constants.ts
diff --git a/apps/api/lib/helpers/addRequestid.ts b/apps/api/v1/lib/helpers/addRequestid.ts
similarity index 100%
rename from apps/api/lib/helpers/addRequestid.ts
rename to apps/api/v1/lib/helpers/addRequestid.ts
diff --git a/apps/api/lib/helpers/captureErrors.ts b/apps/api/v1/lib/helpers/captureErrors.ts
similarity index 100%
rename from apps/api/lib/helpers/captureErrors.ts
rename to apps/api/v1/lib/helpers/captureErrors.ts
diff --git a/apps/api/lib/helpers/extendRequest.ts b/apps/api/v1/lib/helpers/extendRequest.ts
similarity index 100%
rename from apps/api/lib/helpers/extendRequest.ts
rename to apps/api/v1/lib/helpers/extendRequest.ts
diff --git a/apps/api/lib/helpers/httpMethods.ts b/apps/api/v1/lib/helpers/httpMethods.ts
similarity index 100%
rename from apps/api/lib/helpers/httpMethods.ts
rename to apps/api/v1/lib/helpers/httpMethods.ts
diff --git a/apps/api/lib/helpers/rateLimitApiKey.ts b/apps/api/v1/lib/helpers/rateLimitApiKey.ts
similarity index 100%
rename from apps/api/lib/helpers/rateLimitApiKey.ts
rename to apps/api/v1/lib/helpers/rateLimitApiKey.ts
diff --git a/apps/api/lib/helpers/safeParseJSON.ts b/apps/api/v1/lib/helpers/safeParseJSON.ts
similarity index 100%
rename from apps/api/lib/helpers/safeParseJSON.ts
rename to apps/api/v1/lib/helpers/safeParseJSON.ts
diff --git a/apps/api/lib/helpers/verifyApiKey.ts b/apps/api/v1/lib/helpers/verifyApiKey.ts
similarity index 100%
rename from apps/api/lib/helpers/verifyApiKey.ts
rename to apps/api/v1/lib/helpers/verifyApiKey.ts
diff --git a/apps/api/lib/helpers/verifyCredentialSyncEnabled.ts b/apps/api/v1/lib/helpers/verifyCredentialSyncEnabled.ts
similarity index 100%
rename from apps/api/lib/helpers/verifyCredentialSyncEnabled.ts
rename to apps/api/v1/lib/helpers/verifyCredentialSyncEnabled.ts
diff --git a/apps/api/lib/helpers/withMiddleware.ts b/apps/api/v1/lib/helpers/withMiddleware.ts
similarity index 100%
rename from apps/api/lib/helpers/withMiddleware.ts
rename to apps/api/v1/lib/helpers/withMiddleware.ts
diff --git a/apps/api/lib/helpers/withPagination.ts b/apps/api/v1/lib/helpers/withPagination.ts
similarity index 100%
rename from apps/api/lib/helpers/withPagination.ts
rename to apps/api/v1/lib/helpers/withPagination.ts
diff --git a/apps/api/lib/types.ts b/apps/api/v1/lib/types.ts
similarity index 100%
rename from apps/api/lib/types.ts
rename to apps/api/v1/lib/types.ts
diff --git a/apps/api/lib/utils/extractUserIdsFromQuery.ts b/apps/api/v1/lib/utils/extractUserIdsFromQuery.ts
similarity index 100%
rename from apps/api/lib/utils/extractUserIdsFromQuery.ts
rename to apps/api/v1/lib/utils/extractUserIdsFromQuery.ts
diff --git a/apps/api/lib/utils/isAdmin.ts b/apps/api/v1/lib/utils/isAdmin.ts
similarity index 100%
rename from apps/api/lib/utils/isAdmin.ts
rename to apps/api/v1/lib/utils/isAdmin.ts
diff --git a/apps/api/lib/utils/isValidBase64Image.ts b/apps/api/v1/lib/utils/isValidBase64Image.ts
similarity index 100%
rename from apps/api/lib/utils/isValidBase64Image.ts
rename to apps/api/v1/lib/utils/isValidBase64Image.ts
diff --git a/apps/api/lib/utils/stringifyISODate.ts b/apps/api/v1/lib/utils/stringifyISODate.ts
similarity index 100%
rename from apps/api/lib/utils/stringifyISODate.ts
rename to apps/api/v1/lib/utils/stringifyISODate.ts
diff --git a/apps/api/lib/validations/api-key.ts b/apps/api/v1/lib/validations/api-key.ts
similarity index 100%
rename from apps/api/lib/validations/api-key.ts
rename to apps/api/v1/lib/validations/api-key.ts
diff --git a/apps/api/lib/validations/attendee.ts b/apps/api/v1/lib/validations/attendee.ts
similarity index 100%
rename from apps/api/lib/validations/attendee.ts
rename to apps/api/v1/lib/validations/attendee.ts
diff --git a/apps/api/lib/validations/availability.ts b/apps/api/v1/lib/validations/availability.ts
similarity index 100%
rename from apps/api/lib/validations/availability.ts
rename to apps/api/v1/lib/validations/availability.ts
diff --git a/apps/api/lib/validations/booking-reference.ts b/apps/api/v1/lib/validations/booking-reference.ts
similarity index 100%
rename from apps/api/lib/validations/booking-reference.ts
rename to apps/api/v1/lib/validations/booking-reference.ts
diff --git a/apps/api/lib/validations/booking.ts b/apps/api/v1/lib/validations/booking.ts
similarity index 100%
rename from apps/api/lib/validations/booking.ts
rename to apps/api/v1/lib/validations/booking.ts
diff --git a/apps/api/lib/validations/credential-sync.ts b/apps/api/v1/lib/validations/credential-sync.ts
similarity index 100%
rename from apps/api/lib/validations/credential-sync.ts
rename to apps/api/v1/lib/validations/credential-sync.ts
diff --git a/apps/api/lib/validations/destination-calendar.ts b/apps/api/v1/lib/validations/destination-calendar.ts
similarity index 100%
rename from apps/api/lib/validations/destination-calendar.ts
rename to apps/api/v1/lib/validations/destination-calendar.ts
diff --git a/apps/api/lib/validations/event-type-custom-input.ts b/apps/api/v1/lib/validations/event-type-custom-input.ts
similarity index 100%
rename from apps/api/lib/validations/event-type-custom-input.ts
rename to apps/api/v1/lib/validations/event-type-custom-input.ts
diff --git a/apps/api/lib/validations/event-type.ts b/apps/api/v1/lib/validations/event-type.ts
similarity index 100%
rename from apps/api/lib/validations/event-type.ts
rename to apps/api/v1/lib/validations/event-type.ts
diff --git a/apps/api/lib/validations/membership.ts b/apps/api/v1/lib/validations/membership.ts
similarity index 100%
rename from apps/api/lib/validations/membership.ts
rename to apps/api/v1/lib/validations/membership.ts
diff --git a/apps/api/lib/validations/payment.ts b/apps/api/v1/lib/validations/payment.ts
similarity index 100%
rename from apps/api/lib/validations/payment.ts
rename to apps/api/v1/lib/validations/payment.ts
diff --git a/apps/api/lib/validations/reminder-mail.ts b/apps/api/v1/lib/validations/reminder-mail.ts
similarity index 100%
rename from apps/api/lib/validations/reminder-mail.ts
rename to apps/api/v1/lib/validations/reminder-mail.ts
diff --git a/apps/api/lib/validations/schedule.ts b/apps/api/v1/lib/validations/schedule.ts
similarity index 100%
rename from apps/api/lib/validations/schedule.ts
rename to apps/api/v1/lib/validations/schedule.ts
diff --git a/apps/api/lib/validations/selected-calendar.ts b/apps/api/v1/lib/validations/selected-calendar.ts
similarity index 100%
rename from apps/api/lib/validations/selected-calendar.ts
rename to apps/api/v1/lib/validations/selected-calendar.ts
diff --git a/apps/api/lib/validations/shared/baseApiParams.ts b/apps/api/v1/lib/validations/shared/baseApiParams.ts
similarity index 100%
rename from apps/api/lib/validations/shared/baseApiParams.ts
rename to apps/api/v1/lib/validations/shared/baseApiParams.ts
diff --git a/apps/api/lib/validations/shared/jsonSchema.ts b/apps/api/v1/lib/validations/shared/jsonSchema.ts
similarity index 100%
rename from apps/api/lib/validations/shared/jsonSchema.ts
rename to apps/api/v1/lib/validations/shared/jsonSchema.ts
diff --git a/apps/api/lib/validations/shared/queryAttendeeEmail.ts b/apps/api/v1/lib/validations/shared/queryAttendeeEmail.ts
similarity index 100%
rename from apps/api/lib/validations/shared/queryAttendeeEmail.ts
rename to apps/api/v1/lib/validations/shared/queryAttendeeEmail.ts
diff --git a/apps/api/lib/validations/shared/queryIdString.ts b/apps/api/v1/lib/validations/shared/queryIdString.ts
similarity index 100%
rename from apps/api/lib/validations/shared/queryIdString.ts
rename to apps/api/v1/lib/validations/shared/queryIdString.ts
diff --git a/apps/api/lib/validations/shared/queryIdTransformParseInt.ts b/apps/api/v1/lib/validations/shared/queryIdTransformParseInt.ts
similarity index 100%
rename from apps/api/lib/validations/shared/queryIdTransformParseInt.ts
rename to apps/api/v1/lib/validations/shared/queryIdTransformParseInt.ts
diff --git a/apps/api/lib/validations/shared/querySlug.ts b/apps/api/v1/lib/validations/shared/querySlug.ts
similarity index 100%
rename from apps/api/lib/validations/shared/querySlug.ts
rename to apps/api/v1/lib/validations/shared/querySlug.ts
diff --git a/apps/api/lib/validations/shared/queryTeamId.ts b/apps/api/v1/lib/validations/shared/queryTeamId.ts
similarity index 100%
rename from apps/api/lib/validations/shared/queryTeamId.ts
rename to apps/api/v1/lib/validations/shared/queryTeamId.ts
diff --git a/apps/api/lib/validations/shared/queryUserEmail.ts b/apps/api/v1/lib/validations/shared/queryUserEmail.ts
similarity index 100%
rename from apps/api/lib/validations/shared/queryUserEmail.ts
rename to apps/api/v1/lib/validations/shared/queryUserEmail.ts
diff --git a/apps/api/lib/validations/shared/queryUserId.ts b/apps/api/v1/lib/validations/shared/queryUserId.ts
similarity index 100%
rename from apps/api/lib/validations/shared/queryUserId.ts
rename to apps/api/v1/lib/validations/shared/queryUserId.ts
diff --git a/apps/api/lib/validations/shared/timeZone.ts b/apps/api/v1/lib/validations/shared/timeZone.ts
similarity index 100%
rename from apps/api/lib/validations/shared/timeZone.ts
rename to apps/api/v1/lib/validations/shared/timeZone.ts
diff --git a/apps/api/lib/validations/team.ts b/apps/api/v1/lib/validations/team.ts
similarity index 100%
rename from apps/api/lib/validations/team.ts
rename to apps/api/v1/lib/validations/team.ts
diff --git a/apps/api/lib/validations/user.ts b/apps/api/v1/lib/validations/user.ts
similarity index 100%
rename from apps/api/lib/validations/user.ts
rename to apps/api/v1/lib/validations/user.ts
diff --git a/apps/api/lib/validations/webhook.ts b/apps/api/v1/lib/validations/webhook.ts
similarity index 100%
rename from apps/api/lib/validations/webhook.ts
rename to apps/api/v1/lib/validations/webhook.ts
diff --git a/apps/api/next-env.d.ts b/apps/api/v1/next-env.d.ts
similarity index 100%
rename from apps/api/next-env.d.ts
rename to apps/api/v1/next-env.d.ts
diff --git a/apps/api/next-i18next.config.js b/apps/api/v1/next-i18next.config.js
similarity index 76%
rename from apps/api/next-i18next.config.js
rename to apps/api/v1/next-i18next.config.js
index 402b72363cf401..cab1a8b008039f 100644
--- a/apps/api/next-i18next.config.js
+++ b/apps/api/v1/next-i18next.config.js
@@ -4,7 +4,7 @@ const i18nConfig = require("@calcom/config/next-i18next.config");
/** @type {import("next-i18next").UserConfig} */
const config = {
...i18nConfig,
- localePath: path.resolve("../web/public/static/locales"),
+ localePath: path.resolve("../../web/public/static/locales"),
};
module.exports = config;
diff --git a/apps/api/next.config.js b/apps/api/v1/next.config.js
similarity index 100%
rename from apps/api/next.config.js
rename to apps/api/v1/next.config.js
diff --git a/apps/api/next.d.ts b/apps/api/v1/next.d.ts
similarity index 100%
rename from apps/api/next.d.ts
rename to apps/api/v1/next.d.ts
diff --git a/apps/api/v1/package.json b/apps/api/v1/package.json
new file mode 100644
index 00000000000000..5a752cbfbd790d
--- /dev/null
+++ b/apps/api/v1/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "@calcom/api",
+ "version": "1.0.0",
+ "description": "Public API for Cal.com",
+ "main": "index.ts",
+ "repository": "git@github.com:calcom/api.git",
+ "author": "Cal.com Inc.",
+ "private": true,
+ "scripts": {
+ "build": "next build",
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
+ "dev": "PORT=3003 next dev",
+ "lint": "eslint . --ignore-path .gitignore",
+ "lint:fix": "eslint . --ext .ts,.js,.tsx,.jsx --fix",
+ "start": "PORT=3003 next start",
+ "docker-start-api": "PORT=80 next start",
+ "type-check": "tsc --pretty --noEmit"
+ },
+ "devDependencies": {
+ "@calcom/tsconfig": "*",
+ "@calcom/types": "*",
+ "node-mocks-http": "^1.11.0"
+ },
+ "dependencies": {
+ "@calcom/app-store": "*",
+ "@calcom/core": "*",
+ "@calcom/dayjs": "*",
+ "@calcom/emails": "*",
+ "@calcom/features": "*",
+ "@calcom/lib": "*",
+ "@calcom/prisma": "*",
+ "@calcom/trpc": "*",
+ "@sentry/nextjs": "^7.73.0",
+ "bcryptjs": "^2.4.3",
+ "memory-cache": "^0.2.0",
+ "next": "^13.5.4",
+ "next-api-middleware": "^1.0.1",
+ "next-axiom": "^0.17.0",
+ "next-swagger-doc": "^0.3.6",
+ "next-validations": "^0.2.0",
+ "typescript": "^4.9.4",
+ "tzdata": "^1.0.30",
+ "uuid": "^8.3.2",
+ "zod": "^3.22.4"
+ }
+}
diff --git a/apps/api/pages/api/api-keys/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/api-keys/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/api-keys/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/api-keys/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/api-keys/[id]/_delete.ts b/apps/api/v1/pages/api/api-keys/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/api-keys/[id]/_delete.ts
rename to apps/api/v1/pages/api/api-keys/[id]/_delete.ts
diff --git a/apps/api/pages/api/api-keys/[id]/_get.ts b/apps/api/v1/pages/api/api-keys/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/api-keys/[id]/_get.ts
rename to apps/api/v1/pages/api/api-keys/[id]/_get.ts
diff --git a/apps/api/pages/api/api-keys/[id]/_patch.ts b/apps/api/v1/pages/api/api-keys/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/api-keys/[id]/_patch.ts
rename to apps/api/v1/pages/api/api-keys/[id]/_patch.ts
diff --git a/apps/api/pages/api/api-keys/[id]/index.ts b/apps/api/v1/pages/api/api-keys/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/api-keys/[id]/index.ts
rename to apps/api/v1/pages/api/api-keys/[id]/index.ts
diff --git a/apps/api/pages/api/api-keys/_get.ts b/apps/api/v1/pages/api/api-keys/_get.ts
similarity index 100%
rename from apps/api/pages/api/api-keys/_get.ts
rename to apps/api/v1/pages/api/api-keys/_get.ts
diff --git a/apps/api/pages/api/api-keys/_post.ts b/apps/api/v1/pages/api/api-keys/_post.ts
similarity index 100%
rename from apps/api/pages/api/api-keys/_post.ts
rename to apps/api/v1/pages/api/api-keys/_post.ts
diff --git a/apps/api/pages/api/api-keys/index.ts b/apps/api/v1/pages/api/api-keys/index.ts
similarity index 100%
rename from apps/api/pages/api/api-keys/index.ts
rename to apps/api/v1/pages/api/api-keys/index.ts
diff --git a/apps/api/pages/api/attendees/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/attendees/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/attendees/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/attendees/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/attendees/[id]/_delete.ts b/apps/api/v1/pages/api/attendees/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/attendees/[id]/_delete.ts
rename to apps/api/v1/pages/api/attendees/[id]/_delete.ts
diff --git a/apps/api/pages/api/attendees/[id]/_get.ts b/apps/api/v1/pages/api/attendees/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/attendees/[id]/_get.ts
rename to apps/api/v1/pages/api/attendees/[id]/_get.ts
diff --git a/apps/api/pages/api/attendees/[id]/_patch.ts b/apps/api/v1/pages/api/attendees/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/attendees/[id]/_patch.ts
rename to apps/api/v1/pages/api/attendees/[id]/_patch.ts
diff --git a/apps/api/pages/api/attendees/[id]/index.ts b/apps/api/v1/pages/api/attendees/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/attendees/[id]/index.ts
rename to apps/api/v1/pages/api/attendees/[id]/index.ts
diff --git a/apps/api/pages/api/attendees/_get.ts b/apps/api/v1/pages/api/attendees/_get.ts
similarity index 100%
rename from apps/api/pages/api/attendees/_get.ts
rename to apps/api/v1/pages/api/attendees/_get.ts
diff --git a/apps/api/pages/api/attendees/_post.ts b/apps/api/v1/pages/api/attendees/_post.ts
similarity index 100%
rename from apps/api/pages/api/attendees/_post.ts
rename to apps/api/v1/pages/api/attendees/_post.ts
diff --git a/apps/api/pages/api/attendees/index.ts b/apps/api/v1/pages/api/attendees/index.ts
similarity index 100%
rename from apps/api/pages/api/attendees/index.ts
rename to apps/api/v1/pages/api/attendees/index.ts
diff --git a/apps/api/pages/api/availabilities/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/availabilities/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/availabilities/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/availabilities/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/availabilities/[id]/_delete.ts b/apps/api/v1/pages/api/availabilities/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/availabilities/[id]/_delete.ts
rename to apps/api/v1/pages/api/availabilities/[id]/_delete.ts
diff --git a/apps/api/pages/api/availabilities/[id]/_get.ts b/apps/api/v1/pages/api/availabilities/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/availabilities/[id]/_get.ts
rename to apps/api/v1/pages/api/availabilities/[id]/_get.ts
diff --git a/apps/api/pages/api/availabilities/[id]/_patch.ts b/apps/api/v1/pages/api/availabilities/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/availabilities/[id]/_patch.ts
rename to apps/api/v1/pages/api/availabilities/[id]/_patch.ts
diff --git a/apps/api/pages/api/availabilities/[id]/index.ts b/apps/api/v1/pages/api/availabilities/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/availabilities/[id]/index.ts
rename to apps/api/v1/pages/api/availabilities/[id]/index.ts
diff --git a/apps/api/pages/api/availabilities/_post.ts b/apps/api/v1/pages/api/availabilities/_post.ts
similarity index 100%
rename from apps/api/pages/api/availabilities/_post.ts
rename to apps/api/v1/pages/api/availabilities/_post.ts
diff --git a/apps/api/pages/api/availabilities/index.ts b/apps/api/v1/pages/api/availabilities/index.ts
similarity index 100%
rename from apps/api/pages/api/availabilities/index.ts
rename to apps/api/v1/pages/api/availabilities/index.ts
diff --git a/apps/api/pages/api/availability/_get.ts b/apps/api/v1/pages/api/availability/_get.ts
similarity index 100%
rename from apps/api/pages/api/availability/_get.ts
rename to apps/api/v1/pages/api/availability/_get.ts
diff --git a/apps/api/pages/api/availability/index.ts b/apps/api/v1/pages/api/availability/index.ts
similarity index 100%
rename from apps/api/pages/api/availability/index.ts
rename to apps/api/v1/pages/api/availability/index.ts
diff --git a/apps/api/pages/api/booking-references/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/booking-references/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/booking-references/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/booking-references/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/booking-references/[id]/_delete.ts b/apps/api/v1/pages/api/booking-references/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/booking-references/[id]/_delete.ts
rename to apps/api/v1/pages/api/booking-references/[id]/_delete.ts
diff --git a/apps/api/pages/api/booking-references/[id]/_get.ts b/apps/api/v1/pages/api/booking-references/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/booking-references/[id]/_get.ts
rename to apps/api/v1/pages/api/booking-references/[id]/_get.ts
diff --git a/apps/api/pages/api/booking-references/[id]/_patch.ts b/apps/api/v1/pages/api/booking-references/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/booking-references/[id]/_patch.ts
rename to apps/api/v1/pages/api/booking-references/[id]/_patch.ts
diff --git a/apps/api/pages/api/booking-references/[id]/index.ts b/apps/api/v1/pages/api/booking-references/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/booking-references/[id]/index.ts
rename to apps/api/v1/pages/api/booking-references/[id]/index.ts
diff --git a/apps/api/pages/api/booking-references/_get.ts b/apps/api/v1/pages/api/booking-references/_get.ts
similarity index 100%
rename from apps/api/pages/api/booking-references/_get.ts
rename to apps/api/v1/pages/api/booking-references/_get.ts
diff --git a/apps/api/pages/api/booking-references/_post.ts b/apps/api/v1/pages/api/booking-references/_post.ts
similarity index 100%
rename from apps/api/pages/api/booking-references/_post.ts
rename to apps/api/v1/pages/api/booking-references/_post.ts
diff --git a/apps/api/pages/api/booking-references/index.ts b/apps/api/v1/pages/api/booking-references/index.ts
similarity index 100%
rename from apps/api/pages/api/booking-references/index.ts
rename to apps/api/v1/pages/api/booking-references/index.ts
diff --git a/apps/api/pages/api/bookings/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/bookings/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/bookings/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/bookings/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/bookings/[id]/_delete.ts b/apps/api/v1/pages/api/bookings/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/bookings/[id]/_delete.ts
rename to apps/api/v1/pages/api/bookings/[id]/_delete.ts
diff --git a/apps/api/pages/api/bookings/[id]/_get.ts b/apps/api/v1/pages/api/bookings/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/bookings/[id]/_get.ts
rename to apps/api/v1/pages/api/bookings/[id]/_get.ts
diff --git a/apps/api/pages/api/bookings/[id]/_patch.ts b/apps/api/v1/pages/api/bookings/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/bookings/[id]/_patch.ts
rename to apps/api/v1/pages/api/bookings/[id]/_patch.ts
diff --git a/apps/api/pages/api/bookings/[id]/cancel.ts b/apps/api/v1/pages/api/bookings/[id]/cancel.ts
similarity index 100%
rename from apps/api/pages/api/bookings/[id]/cancel.ts
rename to apps/api/v1/pages/api/bookings/[id]/cancel.ts
diff --git a/apps/api/pages/api/bookings/[id]/index.ts b/apps/api/v1/pages/api/bookings/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/bookings/[id]/index.ts
rename to apps/api/v1/pages/api/bookings/[id]/index.ts
diff --git a/apps/api/pages/api/bookings/_get.ts b/apps/api/v1/pages/api/bookings/_get.ts
similarity index 100%
rename from apps/api/pages/api/bookings/_get.ts
rename to apps/api/v1/pages/api/bookings/_get.ts
diff --git a/apps/api/pages/api/bookings/_post.ts b/apps/api/v1/pages/api/bookings/_post.ts
similarity index 100%
rename from apps/api/pages/api/bookings/_post.ts
rename to apps/api/v1/pages/api/bookings/_post.ts
diff --git a/apps/api/pages/api/bookings/index.ts b/apps/api/v1/pages/api/bookings/index.ts
similarity index 100%
rename from apps/api/pages/api/bookings/index.ts
rename to apps/api/v1/pages/api/bookings/index.ts
diff --git a/apps/api/pages/api/credential-sync/_delete.ts b/apps/api/v1/pages/api/credential-sync/_delete.ts
similarity index 100%
rename from apps/api/pages/api/credential-sync/_delete.ts
rename to apps/api/v1/pages/api/credential-sync/_delete.ts
diff --git a/apps/api/pages/api/credential-sync/_get.ts b/apps/api/v1/pages/api/credential-sync/_get.ts
similarity index 100%
rename from apps/api/pages/api/credential-sync/_get.ts
rename to apps/api/v1/pages/api/credential-sync/_get.ts
diff --git a/apps/api/pages/api/credential-sync/_patch.ts b/apps/api/v1/pages/api/credential-sync/_patch.ts
similarity index 100%
rename from apps/api/pages/api/credential-sync/_patch.ts
rename to apps/api/v1/pages/api/credential-sync/_patch.ts
diff --git a/apps/api/pages/api/credential-sync/_post.ts b/apps/api/v1/pages/api/credential-sync/_post.ts
similarity index 100%
rename from apps/api/pages/api/credential-sync/_post.ts
rename to apps/api/v1/pages/api/credential-sync/_post.ts
diff --git a/apps/api/pages/api/credential-sync/index.ts b/apps/api/v1/pages/api/credential-sync/index.ts
similarity index 100%
rename from apps/api/pages/api/credential-sync/index.ts
rename to apps/api/v1/pages/api/credential-sync/index.ts
diff --git a/apps/api/pages/api/custom-inputs/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/custom-inputs/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/custom-inputs/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/custom-inputs/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/custom-inputs/[id]/_delete.ts b/apps/api/v1/pages/api/custom-inputs/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/custom-inputs/[id]/_delete.ts
rename to apps/api/v1/pages/api/custom-inputs/[id]/_delete.ts
diff --git a/apps/api/pages/api/custom-inputs/[id]/_get.ts b/apps/api/v1/pages/api/custom-inputs/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/custom-inputs/[id]/_get.ts
rename to apps/api/v1/pages/api/custom-inputs/[id]/_get.ts
diff --git a/apps/api/pages/api/custom-inputs/[id]/_patch.ts b/apps/api/v1/pages/api/custom-inputs/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/custom-inputs/[id]/_patch.ts
rename to apps/api/v1/pages/api/custom-inputs/[id]/_patch.ts
diff --git a/apps/api/pages/api/custom-inputs/[id]/index.ts b/apps/api/v1/pages/api/custom-inputs/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/custom-inputs/[id]/index.ts
rename to apps/api/v1/pages/api/custom-inputs/[id]/index.ts
diff --git a/apps/api/pages/api/custom-inputs/_get.ts b/apps/api/v1/pages/api/custom-inputs/_get.ts
similarity index 100%
rename from apps/api/pages/api/custom-inputs/_get.ts
rename to apps/api/v1/pages/api/custom-inputs/_get.ts
diff --git a/apps/api/pages/api/custom-inputs/_post.ts b/apps/api/v1/pages/api/custom-inputs/_post.ts
similarity index 100%
rename from apps/api/pages/api/custom-inputs/_post.ts
rename to apps/api/v1/pages/api/custom-inputs/_post.ts
diff --git a/apps/api/pages/api/custom-inputs/index.ts b/apps/api/v1/pages/api/custom-inputs/index.ts
similarity index 100%
rename from apps/api/pages/api/custom-inputs/index.ts
rename to apps/api/v1/pages/api/custom-inputs/index.ts
diff --git a/apps/api/pages/api/destination-calendars/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/destination-calendars/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/destination-calendars/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/destination-calendars/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/destination-calendars/[id]/_delete.ts b/apps/api/v1/pages/api/destination-calendars/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/destination-calendars/[id]/_delete.ts
rename to apps/api/v1/pages/api/destination-calendars/[id]/_delete.ts
diff --git a/apps/api/pages/api/destination-calendars/[id]/_get.ts b/apps/api/v1/pages/api/destination-calendars/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/destination-calendars/[id]/_get.ts
rename to apps/api/v1/pages/api/destination-calendars/[id]/_get.ts
diff --git a/apps/api/pages/api/destination-calendars/[id]/_patch.ts b/apps/api/v1/pages/api/destination-calendars/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/destination-calendars/[id]/_patch.ts
rename to apps/api/v1/pages/api/destination-calendars/[id]/_patch.ts
diff --git a/apps/api/pages/api/destination-calendars/[id]/index.ts b/apps/api/v1/pages/api/destination-calendars/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/destination-calendars/[id]/index.ts
rename to apps/api/v1/pages/api/destination-calendars/[id]/index.ts
diff --git a/apps/api/pages/api/destination-calendars/_get.ts b/apps/api/v1/pages/api/destination-calendars/_get.ts
similarity index 100%
rename from apps/api/pages/api/destination-calendars/_get.ts
rename to apps/api/v1/pages/api/destination-calendars/_get.ts
diff --git a/apps/api/pages/api/destination-calendars/_post.ts b/apps/api/v1/pages/api/destination-calendars/_post.ts
similarity index 100%
rename from apps/api/pages/api/destination-calendars/_post.ts
rename to apps/api/v1/pages/api/destination-calendars/_post.ts
diff --git a/apps/api/pages/api/destination-calendars/index.ts b/apps/api/v1/pages/api/destination-calendars/index.ts
similarity index 100%
rename from apps/api/pages/api/destination-calendars/index.ts
rename to apps/api/v1/pages/api/destination-calendars/index.ts
diff --git a/apps/api/pages/api/docs.ts b/apps/api/v1/pages/api/docs.ts
similarity index 100%
rename from apps/api/pages/api/docs.ts
rename to apps/api/v1/pages/api/docs.ts
diff --git a/apps/api/pages/api/event-types/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/event-types/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/event-types/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/event-types/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/event-types/[id]/_delete.ts b/apps/api/v1/pages/api/event-types/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/event-types/[id]/_delete.ts
rename to apps/api/v1/pages/api/event-types/[id]/_delete.ts
diff --git a/apps/api/pages/api/event-types/[id]/_get.ts b/apps/api/v1/pages/api/event-types/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/event-types/[id]/_get.ts
rename to apps/api/v1/pages/api/event-types/[id]/_get.ts
diff --git a/apps/api/pages/api/event-types/[id]/_patch.ts b/apps/api/v1/pages/api/event-types/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/event-types/[id]/_patch.ts
rename to apps/api/v1/pages/api/event-types/[id]/_patch.ts
diff --git a/apps/api/pages/api/event-types/[id]/index.ts b/apps/api/v1/pages/api/event-types/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/event-types/[id]/index.ts
rename to apps/api/v1/pages/api/event-types/[id]/index.ts
diff --git a/apps/api/pages/api/event-types/_get.ts b/apps/api/v1/pages/api/event-types/_get.ts
similarity index 100%
rename from apps/api/pages/api/event-types/_get.ts
rename to apps/api/v1/pages/api/event-types/_get.ts
diff --git a/apps/api/pages/api/event-types/_post.ts b/apps/api/v1/pages/api/event-types/_post.ts
similarity index 100%
rename from apps/api/pages/api/event-types/_post.ts
rename to apps/api/v1/pages/api/event-types/_post.ts
diff --git a/apps/api/pages/api/event-types/_utils/checkParentEventOwnership.ts b/apps/api/v1/pages/api/event-types/_utils/checkParentEventOwnership.ts
similarity index 100%
rename from apps/api/pages/api/event-types/_utils/checkParentEventOwnership.ts
rename to apps/api/v1/pages/api/event-types/_utils/checkParentEventOwnership.ts
diff --git a/apps/api/pages/api/event-types/_utils/checkTeamEventEditPermission.ts b/apps/api/v1/pages/api/event-types/_utils/checkTeamEventEditPermission.ts
similarity index 100%
rename from apps/api/pages/api/event-types/_utils/checkTeamEventEditPermission.ts
rename to apps/api/v1/pages/api/event-types/_utils/checkTeamEventEditPermission.ts
diff --git a/apps/api/pages/api/event-types/_utils/checkUserMembership.ts b/apps/api/v1/pages/api/event-types/_utils/checkUserMembership.ts
similarity index 100%
rename from apps/api/pages/api/event-types/_utils/checkUserMembership.ts
rename to apps/api/v1/pages/api/event-types/_utils/checkUserMembership.ts
diff --git a/apps/api/pages/api/event-types/_utils/ensureOnlyMembersAsHosts.ts b/apps/api/v1/pages/api/event-types/_utils/ensureOnlyMembersAsHosts.ts
similarity index 100%
rename from apps/api/pages/api/event-types/_utils/ensureOnlyMembersAsHosts.ts
rename to apps/api/v1/pages/api/event-types/_utils/ensureOnlyMembersAsHosts.ts
diff --git a/apps/api/pages/api/event-types/_utils/getCalLink.ts b/apps/api/v1/pages/api/event-types/_utils/getCalLink.ts
similarity index 100%
rename from apps/api/pages/api/event-types/_utils/getCalLink.ts
rename to apps/api/v1/pages/api/event-types/_utils/getCalLink.ts
diff --git a/apps/api/pages/api/event-types/index.ts b/apps/api/v1/pages/api/event-types/index.ts
similarity index 100%
rename from apps/api/pages/api/event-types/index.ts
rename to apps/api/v1/pages/api/event-types/index.ts
diff --git a/apps/api/pages/api/index.ts b/apps/api/v1/pages/api/index.ts
similarity index 100%
rename from apps/api/pages/api/index.ts
rename to apps/api/v1/pages/api/index.ts
diff --git a/apps/api/pages/api/invites/_post.ts b/apps/api/v1/pages/api/invites/_post.ts
similarity index 100%
rename from apps/api/pages/api/invites/_post.ts
rename to apps/api/v1/pages/api/invites/_post.ts
diff --git a/apps/api/pages/api/invites/index.ts b/apps/api/v1/pages/api/invites/index.ts
similarity index 100%
rename from apps/api/pages/api/invites/index.ts
rename to apps/api/v1/pages/api/invites/index.ts
diff --git a/apps/api/pages/api/me/_get.ts b/apps/api/v1/pages/api/me/_get.ts
similarity index 100%
rename from apps/api/pages/api/me/_get.ts
rename to apps/api/v1/pages/api/me/_get.ts
diff --git a/apps/api/pages/api/me/index.ts b/apps/api/v1/pages/api/me/index.ts
similarity index 100%
rename from apps/api/pages/api/me/index.ts
rename to apps/api/v1/pages/api/me/index.ts
diff --git a/apps/api/pages/api/memberships/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/memberships/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/memberships/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/memberships/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/memberships/[id]/_delete.ts b/apps/api/v1/pages/api/memberships/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/memberships/[id]/_delete.ts
rename to apps/api/v1/pages/api/memberships/[id]/_delete.ts
diff --git a/apps/api/pages/api/memberships/[id]/_get.ts b/apps/api/v1/pages/api/memberships/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/memberships/[id]/_get.ts
rename to apps/api/v1/pages/api/memberships/[id]/_get.ts
diff --git a/apps/api/pages/api/memberships/[id]/_patch.ts b/apps/api/v1/pages/api/memberships/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/memberships/[id]/_patch.ts
rename to apps/api/v1/pages/api/memberships/[id]/_patch.ts
diff --git a/apps/api/pages/api/memberships/[id]/index.ts b/apps/api/v1/pages/api/memberships/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/memberships/[id]/index.ts
rename to apps/api/v1/pages/api/memberships/[id]/index.ts
diff --git a/apps/api/pages/api/memberships/_get.ts b/apps/api/v1/pages/api/memberships/_get.ts
similarity index 100%
rename from apps/api/pages/api/memberships/_get.ts
rename to apps/api/v1/pages/api/memberships/_get.ts
diff --git a/apps/api/pages/api/memberships/_post.ts b/apps/api/v1/pages/api/memberships/_post.ts
similarity index 100%
rename from apps/api/pages/api/memberships/_post.ts
rename to apps/api/v1/pages/api/memberships/_post.ts
diff --git a/apps/api/pages/api/memberships/index.ts b/apps/api/v1/pages/api/memberships/index.ts
similarity index 100%
rename from apps/api/pages/api/memberships/index.ts
rename to apps/api/v1/pages/api/memberships/index.ts
diff --git a/apps/api/pages/api/payments/[id].ts b/apps/api/v1/pages/api/payments/[id].ts
similarity index 100%
rename from apps/api/pages/api/payments/[id].ts
rename to apps/api/v1/pages/api/payments/[id].ts
diff --git a/apps/api/pages/api/payments/index.ts b/apps/api/v1/pages/api/payments/index.ts
similarity index 100%
rename from apps/api/pages/api/payments/index.ts
rename to apps/api/v1/pages/api/payments/index.ts
diff --git a/apps/api/pages/api/schedules/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/schedules/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/schedules/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/schedules/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/schedules/[id]/_delete.ts b/apps/api/v1/pages/api/schedules/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/schedules/[id]/_delete.ts
rename to apps/api/v1/pages/api/schedules/[id]/_delete.ts
diff --git a/apps/api/pages/api/schedules/[id]/_get.ts b/apps/api/v1/pages/api/schedules/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/schedules/[id]/_get.ts
rename to apps/api/v1/pages/api/schedules/[id]/_get.ts
diff --git a/apps/api/pages/api/schedules/[id]/_patch.ts b/apps/api/v1/pages/api/schedules/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/schedules/[id]/_patch.ts
rename to apps/api/v1/pages/api/schedules/[id]/_patch.ts
diff --git a/apps/api/pages/api/schedules/[id]/index.ts b/apps/api/v1/pages/api/schedules/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/schedules/[id]/index.ts
rename to apps/api/v1/pages/api/schedules/[id]/index.ts
diff --git a/apps/api/pages/api/schedules/_get.ts b/apps/api/v1/pages/api/schedules/_get.ts
similarity index 100%
rename from apps/api/pages/api/schedules/_get.ts
rename to apps/api/v1/pages/api/schedules/_get.ts
diff --git a/apps/api/pages/api/schedules/_post.ts b/apps/api/v1/pages/api/schedules/_post.ts
similarity index 100%
rename from apps/api/pages/api/schedules/_post.ts
rename to apps/api/v1/pages/api/schedules/_post.ts
diff --git a/apps/api/pages/api/schedules/index.ts b/apps/api/v1/pages/api/schedules/index.ts
similarity index 100%
rename from apps/api/pages/api/schedules/index.ts
rename to apps/api/v1/pages/api/schedules/index.ts
diff --git a/apps/api/pages/api/selected-calendars/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/selected-calendars/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/selected-calendars/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/selected-calendars/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/selected-calendars/[id]/_delete.ts b/apps/api/v1/pages/api/selected-calendars/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/selected-calendars/[id]/_delete.ts
rename to apps/api/v1/pages/api/selected-calendars/[id]/_delete.ts
diff --git a/apps/api/pages/api/selected-calendars/[id]/_get.ts b/apps/api/v1/pages/api/selected-calendars/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/selected-calendars/[id]/_get.ts
rename to apps/api/v1/pages/api/selected-calendars/[id]/_get.ts
diff --git a/apps/api/pages/api/selected-calendars/[id]/_patch.ts b/apps/api/v1/pages/api/selected-calendars/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/selected-calendars/[id]/_patch.ts
rename to apps/api/v1/pages/api/selected-calendars/[id]/_patch.ts
diff --git a/apps/api/pages/api/selected-calendars/[id]/index.ts b/apps/api/v1/pages/api/selected-calendars/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/selected-calendars/[id]/index.ts
rename to apps/api/v1/pages/api/selected-calendars/[id]/index.ts
diff --git a/apps/api/pages/api/selected-calendars/_get.ts b/apps/api/v1/pages/api/selected-calendars/_get.ts
similarity index 100%
rename from apps/api/pages/api/selected-calendars/_get.ts
rename to apps/api/v1/pages/api/selected-calendars/_get.ts
diff --git a/apps/api/pages/api/selected-calendars/_post.ts b/apps/api/v1/pages/api/selected-calendars/_post.ts
similarity index 100%
rename from apps/api/pages/api/selected-calendars/_post.ts
rename to apps/api/v1/pages/api/selected-calendars/_post.ts
diff --git a/apps/api/pages/api/selected-calendars/index.ts b/apps/api/v1/pages/api/selected-calendars/index.ts
similarity index 100%
rename from apps/api/pages/api/selected-calendars/index.ts
rename to apps/api/v1/pages/api/selected-calendars/index.ts
diff --git a/apps/api/pages/api/slots/_get.test.ts b/apps/api/v1/pages/api/slots/_get.test.ts
similarity index 97%
rename from apps/api/pages/api/slots/_get.test.ts
rename to apps/api/v1/pages/api/slots/_get.test.ts
index 58e77fe290fe6e..7484d298e08f1b 100644
--- a/apps/api/pages/api/slots/_get.test.ts
+++ b/apps/api/v1/pages/api/slots/_get.test.ts
@@ -1,4 +1,4 @@
-import prismock from "../../../../../tests/libs/__mocks__/prisma";
+import prismock from "../../../../../../tests/libs/__mocks__/prisma";
import type { Request, Response } from "express";
import type { NextApiRequest, NextApiResponse } from "next";
diff --git a/apps/api/pages/api/slots/_get.ts b/apps/api/v1/pages/api/slots/_get.ts
similarity index 100%
rename from apps/api/pages/api/slots/_get.ts
rename to apps/api/v1/pages/api/slots/_get.ts
diff --git a/apps/api/pages/api/slots/index.ts b/apps/api/v1/pages/api/slots/index.ts
similarity index 100%
rename from apps/api/pages/api/slots/index.ts
rename to apps/api/v1/pages/api/slots/index.ts
diff --git a/apps/api/pages/api/teams/[teamId]/_auth-middleware.ts b/apps/api/v1/pages/api/teams/[teamId]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/teams/[teamId]/_auth-middleware.ts
rename to apps/api/v1/pages/api/teams/[teamId]/_auth-middleware.ts
diff --git a/apps/api/pages/api/teams/[teamId]/_delete.ts b/apps/api/v1/pages/api/teams/[teamId]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/teams/[teamId]/_delete.ts
rename to apps/api/v1/pages/api/teams/[teamId]/_delete.ts
diff --git a/apps/api/pages/api/teams/[teamId]/_get.ts b/apps/api/v1/pages/api/teams/[teamId]/_get.ts
similarity index 100%
rename from apps/api/pages/api/teams/[teamId]/_get.ts
rename to apps/api/v1/pages/api/teams/[teamId]/_get.ts
diff --git a/apps/api/pages/api/teams/[teamId]/_patch.ts b/apps/api/v1/pages/api/teams/[teamId]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/teams/[teamId]/_patch.ts
rename to apps/api/v1/pages/api/teams/[teamId]/_patch.ts
diff --git a/apps/api/pages/api/teams/[teamId]/availability/index.ts b/apps/api/v1/pages/api/teams/[teamId]/availability/index.ts
similarity index 100%
rename from apps/api/pages/api/teams/[teamId]/availability/index.ts
rename to apps/api/v1/pages/api/teams/[teamId]/availability/index.ts
diff --git a/apps/api/pages/api/teams/[teamId]/event-types/_get.ts b/apps/api/v1/pages/api/teams/[teamId]/event-types/_get.ts
similarity index 100%
rename from apps/api/pages/api/teams/[teamId]/event-types/_get.ts
rename to apps/api/v1/pages/api/teams/[teamId]/event-types/_get.ts
diff --git a/apps/api/pages/api/teams/[teamId]/event-types/index.ts b/apps/api/v1/pages/api/teams/[teamId]/event-types/index.ts
similarity index 100%
rename from apps/api/pages/api/teams/[teamId]/event-types/index.ts
rename to apps/api/v1/pages/api/teams/[teamId]/event-types/index.ts
diff --git a/apps/api/pages/api/teams/[teamId]/index.ts b/apps/api/v1/pages/api/teams/[teamId]/index.ts
similarity index 100%
rename from apps/api/pages/api/teams/[teamId]/index.ts
rename to apps/api/v1/pages/api/teams/[teamId]/index.ts
diff --git a/apps/api/pages/api/teams/[teamId]/publish.ts b/apps/api/v1/pages/api/teams/[teamId]/publish.ts
similarity index 100%
rename from apps/api/pages/api/teams/[teamId]/publish.ts
rename to apps/api/v1/pages/api/teams/[teamId]/publish.ts
diff --git a/apps/api/pages/api/teams/_get.ts b/apps/api/v1/pages/api/teams/_get.ts
similarity index 100%
rename from apps/api/pages/api/teams/_get.ts
rename to apps/api/v1/pages/api/teams/_get.ts
diff --git a/apps/api/pages/api/teams/_post.ts b/apps/api/v1/pages/api/teams/_post.ts
similarity index 100%
rename from apps/api/pages/api/teams/_post.ts
rename to apps/api/v1/pages/api/teams/_post.ts
diff --git a/apps/api/pages/api/teams/index.ts b/apps/api/v1/pages/api/teams/index.ts
similarity index 100%
rename from apps/api/pages/api/teams/index.ts
rename to apps/api/v1/pages/api/teams/index.ts
diff --git a/apps/api/pages/api/users/[userId]/_delete.ts b/apps/api/v1/pages/api/users/[userId]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/users/[userId]/_delete.ts
rename to apps/api/v1/pages/api/users/[userId]/_delete.ts
diff --git a/apps/api/pages/api/users/[userId]/_get.ts b/apps/api/v1/pages/api/users/[userId]/_get.ts
similarity index 100%
rename from apps/api/pages/api/users/[userId]/_get.ts
rename to apps/api/v1/pages/api/users/[userId]/_get.ts
diff --git a/apps/api/pages/api/users/[userId]/_patch.ts b/apps/api/v1/pages/api/users/[userId]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/users/[userId]/_patch.ts
rename to apps/api/v1/pages/api/users/[userId]/_patch.ts
diff --git a/apps/api/pages/api/users/[userId]/availability/index.ts b/apps/api/v1/pages/api/users/[userId]/availability/index.ts
similarity index 100%
rename from apps/api/pages/api/users/[userId]/availability/index.ts
rename to apps/api/v1/pages/api/users/[userId]/availability/index.ts
diff --git a/apps/api/pages/api/users/[userId]/index.ts b/apps/api/v1/pages/api/users/[userId]/index.ts
similarity index 100%
rename from apps/api/pages/api/users/[userId]/index.ts
rename to apps/api/v1/pages/api/users/[userId]/index.ts
diff --git a/apps/api/pages/api/users/_get.ts b/apps/api/v1/pages/api/users/_get.ts
similarity index 100%
rename from apps/api/pages/api/users/_get.ts
rename to apps/api/v1/pages/api/users/_get.ts
diff --git a/apps/api/pages/api/users/_post.ts b/apps/api/v1/pages/api/users/_post.ts
similarity index 100%
rename from apps/api/pages/api/users/_post.ts
rename to apps/api/v1/pages/api/users/_post.ts
diff --git a/apps/api/pages/api/users/index.ts b/apps/api/v1/pages/api/users/index.ts
similarity index 100%
rename from apps/api/pages/api/users/index.ts
rename to apps/api/v1/pages/api/users/index.ts
diff --git a/apps/api/pages/api/webhooks/[id]/_auth-middleware.ts b/apps/api/v1/pages/api/webhooks/[id]/_auth-middleware.ts
similarity index 100%
rename from apps/api/pages/api/webhooks/[id]/_auth-middleware.ts
rename to apps/api/v1/pages/api/webhooks/[id]/_auth-middleware.ts
diff --git a/apps/api/pages/api/webhooks/[id]/_delete.ts b/apps/api/v1/pages/api/webhooks/[id]/_delete.ts
similarity index 100%
rename from apps/api/pages/api/webhooks/[id]/_delete.ts
rename to apps/api/v1/pages/api/webhooks/[id]/_delete.ts
diff --git a/apps/api/pages/api/webhooks/[id]/_get.ts b/apps/api/v1/pages/api/webhooks/[id]/_get.ts
similarity index 100%
rename from apps/api/pages/api/webhooks/[id]/_get.ts
rename to apps/api/v1/pages/api/webhooks/[id]/_get.ts
diff --git a/apps/api/pages/api/webhooks/[id]/_patch.ts b/apps/api/v1/pages/api/webhooks/[id]/_patch.ts
similarity index 100%
rename from apps/api/pages/api/webhooks/[id]/_patch.ts
rename to apps/api/v1/pages/api/webhooks/[id]/_patch.ts
diff --git a/apps/api/pages/api/webhooks/[id]/index.ts b/apps/api/v1/pages/api/webhooks/[id]/index.ts
similarity index 100%
rename from apps/api/pages/api/webhooks/[id]/index.ts
rename to apps/api/v1/pages/api/webhooks/[id]/index.ts
diff --git a/apps/api/pages/api/webhooks/_get.ts b/apps/api/v1/pages/api/webhooks/_get.ts
similarity index 100%
rename from apps/api/pages/api/webhooks/_get.ts
rename to apps/api/v1/pages/api/webhooks/_get.ts
diff --git a/apps/api/pages/api/webhooks/_post.ts b/apps/api/v1/pages/api/webhooks/_post.ts
similarity index 100%
rename from apps/api/pages/api/webhooks/_post.ts
rename to apps/api/v1/pages/api/webhooks/_post.ts
diff --git a/apps/api/pages/api/webhooks/index.ts b/apps/api/v1/pages/api/webhooks/index.ts
similarity index 100%
rename from apps/api/pages/api/webhooks/index.ts
rename to apps/api/v1/pages/api/webhooks/index.ts
diff --git a/apps/api/scripts/vercel-deploy.sh b/apps/api/v1/scripts/vercel-deploy.sh
similarity index 100%
rename from apps/api/scripts/vercel-deploy.sh
rename to apps/api/v1/scripts/vercel-deploy.sh
diff --git a/apps/api/sentry.client.config.ts b/apps/api/v1/sentry.client.config.ts
similarity index 100%
rename from apps/api/sentry.client.config.ts
rename to apps/api/v1/sentry.client.config.ts
diff --git a/apps/api/sentry.edge.config.ts b/apps/api/v1/sentry.edge.config.ts
similarity index 100%
rename from apps/api/sentry.edge.config.ts
rename to apps/api/v1/sentry.edge.config.ts
diff --git a/apps/api/sentry.server.config.ts b/apps/api/v1/sentry.server.config.ts
similarity index 100%
rename from apps/api/sentry.server.config.ts
rename to apps/api/v1/sentry.server.config.ts
diff --git a/apps/api/test/README.md b/apps/api/v1/test/README.md
similarity index 100%
rename from apps/api/test/README.md
rename to apps/api/v1/test/README.md
diff --git a/apps/api/test/docker-compose.yml b/apps/api/v1/test/docker-compose.yml
similarity index 100%
rename from apps/api/test/docker-compose.yml
rename to apps/api/v1/test/docker-compose.yml
diff --git a/apps/api/test/jest-resolver.js b/apps/api/v1/test/jest-resolver.js
similarity index 100%
rename from apps/api/test/jest-resolver.js
rename to apps/api/v1/test/jest-resolver.js
diff --git a/apps/api/test/jest-setup.js b/apps/api/v1/test/jest-setup.js
similarity index 100%
rename from apps/api/test/jest-setup.js
rename to apps/api/v1/test/jest-setup.js
diff --git a/apps/api/test/lib/bookings/_post.test.ts b/apps/api/v1/test/lib/bookings/_post.test.ts
similarity index 99%
rename from apps/api/test/lib/bookings/_post.test.ts
rename to apps/api/v1/test/lib/bookings/_post.test.ts
index 64abddcfe3462b..e34defc601fe6a 100644
--- a/apps/api/test/lib/bookings/_post.test.ts
+++ b/apps/api/v1/test/lib/bookings/_post.test.ts
@@ -1,5 +1,5 @@
// TODO: Fix tests (These test were never running due to the vitest workspace config)
-import prismaMock from "../../../../../tests/libs/__mocks__/prisma";
+import prismaMock from "../../../../../../tests/libs/__mocks__/prismaMock";
import type { Request, Response } from "express";
import type { NextApiRequest, NextApiResponse } from "next";
diff --git a/apps/api/test/lib/middleware/addRequestId.test.ts b/apps/api/v1/test/lib/middleware/addRequestId.test.ts
similarity index 100%
rename from apps/api/test/lib/middleware/addRequestId.test.ts
rename to apps/api/v1/test/lib/middleware/addRequestId.test.ts
diff --git a/apps/api/test/lib/middleware/httpMethods.test.ts b/apps/api/v1/test/lib/middleware/httpMethods.test.ts
similarity index 100%
rename from apps/api/test/lib/middleware/httpMethods.test.ts
rename to apps/api/v1/test/lib/middleware/httpMethods.test.ts
diff --git a/apps/api/test/lib/middleware/verifyApiKey.test.ts b/apps/api/v1/test/lib/middleware/verifyApiKey.test.ts
similarity index 100%
rename from apps/api/test/lib/middleware/verifyApiKey.test.ts
rename to apps/api/v1/test/lib/middleware/verifyApiKey.test.ts
diff --git a/apps/api/tsconfig.json b/apps/api/v1/tsconfig.json
similarity index 81%
rename from apps/api/tsconfig.json
rename to apps/api/v1/tsconfig.json
index c6b3666313f6f3..7283f0b3500ee0 100644
--- a/apps/api/tsconfig.json
+++ b/apps/api/v1/tsconfig.json
@@ -13,8 +13,8 @@
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
- "../../packages/types/*.d.ts",
- "../../packages/types/next-auth.d.ts"
+ "../../../packages/types/*.d.ts",
+ "../../../packages/types/next-auth.d.ts"
],
"exclude": ["node_modules", "templates", "auth"]
}
diff --git a/apps/api/vercel.json b/apps/api/v1/vercel.json
similarity index 100%
rename from apps/api/vercel.json
rename to apps/api/v1/vercel.json
diff --git a/apps/api/v2/.dockerignore b/apps/api/v2/.dockerignore
new file mode 100644
index 00000000000000..569ce539708a55
--- /dev/null
+++ b/apps/api/v2/.dockerignore
@@ -0,0 +1,2 @@
+**/node_modules
+**/dist
\ No newline at end of file
diff --git a/apps/api/v2/.env.example b/apps/api/v2/.env.example
new file mode 100644
index 00000000000000..df907991b2a2e3
--- /dev/null
+++ b/apps/api/v2/.env.example
@@ -0,0 +1,13 @@
+NODE_ENV=
+API_PORT=
+API_URL=
+DATABASE_READ_URL=
+DATABASE_WRITE_URL=
+LOG_LEVEL=
+NEXTAUTH_SECRET=
+DATABASE_URL=
+JWT_SECRET=
+SENTRY_DNS=
+
+# KEEP THIS EMPTY, DISABLE SENTRY CLIENT INSIDE OF LIBRARIES USED BY APIv2
+NEXT_PUBLIC_SENTRY_DSN=
\ No newline at end of file
diff --git a/apps/api/v2/.eslintrc.js b/apps/api/v2/.eslintrc.js
new file mode 100644
index 00000000000000..1c4289dc6b6b03
--- /dev/null
+++ b/apps/api/v2/.eslintrc.js
@@ -0,0 +1,31 @@
+module.exports = {
+ parser: "@typescript-eslint/parser",
+ parserOptions: {
+ project: "tsconfig.json",
+ tsconfigRootDir: __dirname,
+ sourceType: "module",
+ },
+ plugins: ["@typescript-eslint/eslint-plugin"],
+ extends: ["plugin:@typescript-eslint/recommended"],
+ root: true,
+ env: {
+ node: true,
+ jest: true,
+ },
+ ignorePatterns: [".eslintrc.js"],
+ rules: {
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ },
+ overrides: [
+ {
+ files: ["./src/**/*.controller.ts"],
+ excludedFiles: "*.spec.js",
+ rules: {
+ "@typescript-eslint/explicit-function-return-type": "error",
+ },
+ },
+ ],
+};
diff --git a/apps/api/v2/.gitignore b/apps/api/v2/.gitignore
new file mode 100644
index 00000000000000..0cf21bfcd21152
--- /dev/null
+++ b/apps/api/v2/.gitignore
@@ -0,0 +1,44 @@
+# compiled output
+/dist
+/node_modules
+
+# Logs
+logs
+*.log
+npm-debug.log*
+pnpm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# OS
+.DS_Store
+
+# Tests
+/coverage
+/.nyc_output
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# local env files
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+.env.*
+!.env.example
+!.env.appStore.example
\ No newline at end of file
diff --git a/apps/api/v2/.prettierrc.js b/apps/api/v2/.prettierrc.js
new file mode 100644
index 00000000000000..ba4100a4efe045
--- /dev/null
+++ b/apps/api/v2/.prettierrc.js
@@ -0,0 +1,6 @@
+const rootConfig = require("../../../packages/config/prettier-preset");
+
+module.exports = {
+ ...rootConfig,
+ importOrderParserPlugins: ["typescript", "decorators-legacy"],
+};
diff --git a/apps/api/v2/Dockerfile b/apps/api/v2/Dockerfile
new file mode 100644
index 00000000000000..79626c1ac9a536
--- /dev/null
+++ b/apps/api/v2/Dockerfile
@@ -0,0 +1,28 @@
+FROM node:18-alpine as build
+
+ARG DATABASE_DIRECT_URL
+ARG DATABASE_URL
+
+WORKDIR /calcom
+
+RUN set -eux;
+
+ENV NODE_ENV="production"
+ENV NODE_OPTIONS="--max-old-space-size=8192"
+ENV DATABASE_DIRECT_URL=${DATABASE_DIRECT_URL}
+ENV DATABASE_URL=${DATABASE_URL}
+
+COPY . .
+
+RUN yarn install
+
+# Build prisma schema and make sure that it is linked to v2 node_modules
+RUN yarn workspace @calcom/api-v2 run generate-schemas
+RUN rm -rf apps/api/v2/node_modules
+RUN yarn install
+
+RUN yarn workspace @calcom/api-v2 run build
+
+EXPOSE 80
+
+CMD [ "yarn", "workspace", "@calcom/api-v2", "start:prod"]
diff --git a/apps/api/v2/README.md b/apps/api/v2/README.md
new file mode 100644
index 00000000000000..64af00f7fb28dd
--- /dev/null
+++ b/apps/api/v2/README.md
@@ -0,0 +1,83 @@
+
+
+
+
+[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
+[circleci-url]: https://circleci.com/gh/nestjs/nest
+
+ A progressive Node.js framework for building efficient and scalable server-side applications.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Description
+
+[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
+
+## Installation
+
+```bash
+$ yarn install
+```
+
+## Prisma setup
+
+```bash
+$ yarn prisma generate
+```
+
+## Env setup
+
+Copy `.env.example` to `.env` and fill values.
+
+## Running the app
+
+```bash
+# development
+$ yarn run start
+
+# watch mode
+$ yarn run start:dev
+
+# production mode
+$ yarn run start:prod
+```
+
+## Test
+
+```bash
+# unit tests
+$ yarn run test
+
+# e2e tests
+$ yarn run test:e2e
+
+# test coverage
+$ yarn run test:cov
+```
+
+## Support
+
+Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
+
+## Stay in touch
+
+- Author - [Kamil MyĆliwiec](https://kamilmysliwiec.com)
+- Website - [https://nestjs.com](https://nestjs.com/)
+- Twitter - [@nestframework](https://twitter.com/nestframework)
+
+## License
+
+Nest is [MIT licensed](LICENSE).
diff --git a/apps/api/v2/docker-compose.yaml b/apps/api/v2/docker-compose.yaml
new file mode 100644
index 00000000000000..cd30f07b5665b8
--- /dev/null
+++ b/apps/api/v2/docker-compose.yaml
@@ -0,0 +1,14 @@
+version: '3.8'
+
+services:
+ redis:
+ image: redis:latest
+ container_name: redis_container
+ ports:
+ - "6379:6379"
+ command: redis-server --appendonly yes
+ volumes:
+ - redis_data:/data
+
+volumes:
+ redis_data:
diff --git a/apps/api/v2/jest-e2e.json b/apps/api/v2/jest-e2e.json
new file mode 100644
index 00000000000000..3f5b2573324b3b
--- /dev/null
+++ b/apps/api/v2/jest-e2e.json
@@ -0,0 +1,14 @@
+{
+ "moduleFileExtensions": ["js", "json", "ts"],
+ "rootDir": ".",
+ "moduleNameMapper": {
+ "@/(.*)": "/src/$1",
+ "test/(.*)": "/test/$1"
+ },
+ "testEnvironment": "node",
+ "testRegex": ".e2e-spec.ts$",
+ "transform": {
+ "^.+\\.(t|j)s$": "ts-jest"
+ },
+ "setupFiles": ["/test/setEnvVars.ts"]
+}
diff --git a/apps/api/v2/jest.config.json b/apps/api/v2/jest.config.json
new file mode 100644
index 00000000000000..a7b6a8c8885869
--- /dev/null
+++ b/apps/api/v2/jest.config.json
@@ -0,0 +1,14 @@
+{
+ "moduleFileExtensions": ["js", "json", "ts"],
+ "rootDir": ".",
+ "moduleNameMapper": {
+ "@/(.*)": "/src/$1",
+ "test/(.*)": "/test/$1"
+ },
+ "testEnvironment": "node",
+ "testRegex": ".*\\.spec\\.ts$",
+ "transform": {
+ "^.+\\.ts$": "ts-jest"
+ },
+ "setupFiles": ["/test/setEnvVars.ts"]
+}
diff --git a/apps/api/v2/nest-cli.json b/apps/api/v2/nest-cli.json
new file mode 100644
index 00000000000000..69cce1547ba0f5
--- /dev/null
+++ b/apps/api/v2/nest-cli.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json.schemastore.org/nest-cli",
+ "collection": "@nestjs/schematics",
+ "sourceRoot": "src",
+ "compilerOptions": {
+ "deleteOutDir": true,
+ "plugins": [
+ {
+ "name": "@nestjs/swagger",
+ "options": { "dtoFileNameSuffix": ".input.ts", "classValidatorShim": true }
+ }
+ ]
+ }
+}
diff --git a/apps/api/v2/next-i18next.config.js b/apps/api/v2/next-i18next.config.js
new file mode 100644
index 00000000000000..a07cf209817826
--- /dev/null
+++ b/apps/api/v2/next-i18next.config.js
@@ -0,0 +1,11 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+const path = require("path");
+const i18nConfig = require("@calcom/config/next-i18next.config");
+
+/** @type {import("next-i18next").UserConfig} */
+const config = {
+ ...i18nConfig,
+ localePath: path.resolve("../../web/public/static/locales"),
+};
+
+module.exports = config;
diff --git a/apps/api/v2/package.json b/apps/api/v2/package.json
new file mode 100644
index 00000000000000..d88131c3e10fa1
--- /dev/null
+++ b/apps/api/v2/package.json
@@ -0,0 +1,88 @@
+{
+ "name": "@calcom/api-v2",
+ "version": "0.0.1",
+ "description": "Platform API for Cal.com",
+ "author": "Cal.com Inc.",
+ "private": true,
+ "license": "UNLICENSED",
+ "scripts": {
+ "build": "yarn dev:build && nest build",
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
+ "start": "nest start",
+ "dev:build:watch": "yarn workspace @calcom/platform-constants build:watch & yarn workspace @calcom/platform-utils build:watch & yarn workspace @calcom/platform-types build:watch & yarn workspace @calcom/platform-libraries build:watch",
+ "dev:build": "yarn workspace @calcom/platform-constants build && yarn workspace @calcom/platform-utils build && yarn workspace @calcom/platform-types build && yarn workspace @calcom/platform-libraries build",
+ "dev": "yarn dev:build && docker-compose up -d && yarn copy-swagger-module && nest start --watch",
+ "start:debug": "nest start --debug --watch",
+ "start:prod": "node dist/src/main",
+ "test": "yarn dev:build && jest",
+ "test:watch": "yarn dev:build && jest --watch",
+ "test:cov": "yarn dev:build && jest --coverage",
+ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
+ "test:e2e": "yarn dev:build && jest --runInBand --config ./jest-e2e.json",
+ "prisma": "yarn workspace @calcom/prisma prisma",
+ "generate-schemas": "yarn prisma generate && yarn prisma format",
+ "copy-swagger-module": "ts-node -r tsconfig-paths/register swagger/copy-swagger-module.ts"
+ },
+ "dependencies": {
+ "@calcom/platform-constants": "*",
+ "@calcom/platform-libraries": "*",
+ "@calcom/platform-types": "*",
+ "@calcom/platform-utils": "*",
+ "@calcom/prisma": "*",
+ "@golevelup/ts-jest": "^0.4.0",
+ "@nestjs/common": "^10.0.0",
+ "@nestjs/config": "^3.1.1",
+ "@nestjs/core": "^10.0.0",
+ "@nestjs/jwt": "^10.2.0",
+ "@nestjs/passport": "^10.0.2",
+ "@nestjs/platform-express": "^10.0.0",
+ "@nestjs/swagger": "^7.3.0",
+ "@nestjs/throttler": "^5.1.1",
+ "@sentry/node": "^7.86.0",
+ "@sentry/tracing": "^7.86.0",
+ "class-transformer": "^0.5.1",
+ "class-validator": "^0.14.0",
+ "cookie-parser": "^1.4.6",
+ "dotenv": "^16.3.1",
+ "fs-extra": "^11.2.0",
+ "googleapis": "^84.0.0",
+ "helmet": "^7.1.0",
+ "ioredis": "^5.3.2",
+ "luxon": "^3.4.4",
+ "nest-winston": "^1.9.4",
+ "nestjs-throttler-storage-redis": "^0.4.1",
+ "next-auth": "^4.22.1",
+ "passport": "^0.7.0",
+ "passport-jwt": "^4.0.1",
+ "reflect-metadata": "^0.1.13",
+ "rxjs": "^7.8.1",
+ "uuid": "^8.3.2",
+ "winston": "^3.11.0",
+ "zod": "^3.22.4"
+ },
+ "devDependencies": {
+ "@nestjs/cli": "^10.0.0",
+ "@nestjs/schematics": "^10.0.0",
+ "@nestjs/testing": "^10.0.0",
+ "@types/cookie-parser": "^1.4.6",
+ "@types/express": "^4.17.21",
+ "@types/fs-extra": "^11.0.4",
+ "@types/jest": "^29.5.10",
+ "@types/luxon": "^3.3.7",
+ "@types/node": "^20.3.1",
+ "@types/passport-jwt": "^3.0.13",
+ "@types/supertest": "^2.0.12",
+ "jest": "^29.7.0",
+ "prettier": "^2.8.6",
+ "source-map-support": "^0.5.21",
+ "supertest": "^6.3.3",
+ "ts-jest": "^29.1.1",
+ "ts-loader": "^9.4.3",
+ "ts-node": "^10.9.1",
+ "tsconfig-paths": "^4.1.0",
+ "typescript": "^4.9.4"
+ },
+ "prisma": {
+ "schema": "../../../packages/prisma/schema.prisma"
+ }
+}
diff --git a/apps/api/v2/src/app.controller.ts b/apps/api/v2/src/app.controller.ts
new file mode 100644
index 00000000000000..6e6e7535ab86e8
--- /dev/null
+++ b/apps/api/v2/src/app.controller.ts
@@ -0,0 +1,10 @@
+import { Controller, Get, Version, VERSION_NEUTRAL } from "@nestjs/common";
+
+@Controller()
+export class AppController {
+ @Get("health")
+ @Version(VERSION_NEUTRAL)
+ getHealth(): "OK" {
+ return "OK";
+ }
+}
diff --git a/apps/api/v2/src/app.e2e-spec.ts b/apps/api/v2/src/app.e2e-spec.ts
new file mode 100644
index 00000000000000..41bc985250b380
--- /dev/null
+++ b/apps/api/v2/src/app.e2e-spec.ts
@@ -0,0 +1,26 @@
+import { AppModule } from "@/app.module";
+import { INestApplication } from "@nestjs/common";
+import { TestingModule } from "@nestjs/testing";
+import { Test } from "@nestjs/testing";
+import * as request from "supertest";
+
+describe("AppController (e2e)", () => {
+ let app: INestApplication;
+
+ beforeEach(async () => {
+ const moduleFixture: TestingModule = await Test.createTestingModule({
+ imports: [AppModule],
+ }).compile();
+
+ app = moduleFixture.createNestApplication();
+ await app.init();
+ });
+
+ it("/ (GET)", () => {
+ return request(app.getHttpServer()).get("/health").expect("OK");
+ });
+
+ afterAll(async () => {
+ await app.close();
+ });
+});
diff --git a/apps/api/v2/src/app.logger.middleware.ts b/apps/api/v2/src/app.logger.middleware.ts
new file mode 100644
index 00000000000000..80ce4cef3e3f26
--- /dev/null
+++ b/apps/api/v2/src/app.logger.middleware.ts
@@ -0,0 +1,21 @@
+import { Injectable, NestMiddleware, Logger } from "@nestjs/common";
+import { Request, NextFunction } from "express";
+
+import { Response } from "@calcom/platform-types";
+
+@Injectable()
+export class AppLoggerMiddleware implements NestMiddleware {
+ private logger = new Logger("HTTP");
+
+ use(request: Request, response: Response, next: NextFunction): void {
+ const { ip, method, protocol, originalUrl, path: url } = request;
+ const userAgent = request.get("user-agent") || "";
+
+ response.on("close", () => {
+ const { statusCode } = response;
+ const contentLength = response.get("content-length");
+ this.logger.log(`${method} ${originalUrl} ${statusCode} ${contentLength} - ${userAgent} ${ip}`);
+ });
+ next();
+ }
+}
diff --git a/apps/api/v2/src/app.module.ts b/apps/api/v2/src/app.module.ts
new file mode 100644
index 00000000000000..6d66e0a9fe46a9
--- /dev/null
+++ b/apps/api/v2/src/app.module.ts
@@ -0,0 +1,44 @@
+import { AppLoggerMiddleware } from "@/app.logger.middleware";
+import appConfig from "@/config/app";
+import { AuthModule } from "@/modules/auth/auth.module";
+import { EndpointsModule } from "@/modules/endpoints.module";
+import { JwtModule } from "@/modules/jwt/jwt.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
+import { ConfigModule } from "@nestjs/config";
+
+import { AppController } from "./app.controller";
+
+@Module({
+ imports: [
+ ConfigModule.forRoot({
+ ignoreEnvFile: true,
+ isGlobal: true,
+ load: [appConfig],
+ }),
+ // ThrottlerModule.forRootAsync({
+ // imports: [ConfigModule],
+ // inject: [ConfigService],
+ // useFactory: (config: ConfigService) => ({
+ // throttlers: [
+ // {
+ // name: "short",
+ // ttl: seconds(10),
+ // limit: 3,
+ // },
+ // ],
+ // storage: new ThrottlerStorageRedisService(config.get("db.redisUrl", { infer: true })),
+ // }),
+ // }),
+ PrismaModule,
+ EndpointsModule,
+ AuthModule,
+ JwtModule,
+ ],
+ controllers: [AppController],
+})
+export class AppModule implements NestModule {
+ configure(consumer: MiddlewareConsumer): void {
+ consumer.apply(AppLoggerMiddleware).forRoutes("*");
+ }
+}
diff --git a/apps/api/v2/src/app.ts b/apps/api/v2/src/app.ts
new file mode 100644
index 00000000000000..84ec3593c00be1
--- /dev/null
+++ b/apps/api/v2/src/app.ts
@@ -0,0 +1,68 @@
+import { getEnv } from "@/env";
+import { HttpExceptionFilter } from "@/filters/http-exception.filter";
+import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
+import { SentryFilter } from "@/filters/sentry-exception.filter";
+import { ZodExceptionFilter } from "@/filters/zod-exception.filter";
+import type { ValidationError } from "@nestjs/common";
+import { BadRequestException, RequestMethod, ValidationPipe, VersioningType } from "@nestjs/common";
+import { HttpAdapterHost } from "@nestjs/core";
+import type { NestExpressApplication } from "@nestjs/platform-express";
+import * as Sentry from "@sentry/node";
+import * as cookieParser from "cookie-parser";
+import helmet from "helmet";
+
+import { TRPCExceptionFilter } from "./filters/trpc-exception.filter";
+
+export const bootstrap = (app: NestExpressApplication): NestExpressApplication => {
+ app.enableShutdownHooks();
+ app.enableVersioning({
+ type: VersioningType.URI,
+ prefix: "v",
+ defaultVersion: "1",
+ });
+
+ app.use(helmet());
+
+ app.enableCors({
+ origin: "*",
+ methods: ["GET", "PATCH", "DELETE", "HEAD", "POST", "PUT", "OPTIONS"],
+ allowedHeaders: ["Accept", "Authorization", "Content-Type", "Origin"],
+ maxAge: 86_400,
+ });
+
+ app.useGlobalPipes(
+ new ValidationPipe({
+ whitelist: true,
+ transform: true,
+ validationError: {
+ target: true,
+ value: true,
+ },
+ exceptionFactory(errors: ValidationError[]) {
+ return new BadRequestException({ errors });
+ },
+ })
+ );
+
+ if (process.env.SENTRY_DNS) {
+ Sentry.init({
+ dsn: getEnv("SENTRY_DNS"),
+ });
+ }
+
+ // Exception filters, new filters go at the bottom, keep the order
+ const { httpAdapter } = app.get(HttpAdapterHost);
+ app.useGlobalFilters(new SentryFilter(httpAdapter));
+ app.useGlobalFilters(new PrismaExceptionFilter());
+ app.useGlobalFilters(new ZodExceptionFilter());
+ app.useGlobalFilters(new HttpExceptionFilter());
+ app.useGlobalFilters(new TRPCExceptionFilter());
+
+ app.setGlobalPrefix("api", {
+ exclude: [{ path: "health", method: RequestMethod.GET }],
+ });
+
+ app.use(cookieParser());
+
+ return app;
+};
diff --git a/apps/api/v2/src/config/app.ts b/apps/api/v2/src/config/app.ts
new file mode 100644
index 00000000000000..f863dd953fd3ed
--- /dev/null
+++ b/apps/api/v2/src/config/app.ts
@@ -0,0 +1,28 @@
+import { getEnv } from "@/env";
+
+import type { AppConfig } from "./type";
+
+const loadConfig = (): AppConfig => {
+ return {
+ env: {
+ type: getEnv("NODE_ENV", "development"),
+ },
+ api: {
+ port: Number(getEnv("API_PORT", "5555")),
+ path: getEnv("API_URL", "http://localhost"),
+ url: `${getEnv("API_URL", "http://localhost")}${
+ process.env.API_PORT ? `:${Number(getEnv("API_PORT", "5555"))}` : ""
+ }/api/v2`,
+ },
+ db: {
+ readUrl: getEnv("DATABASE_READ_URL"),
+ writeUrl: getEnv("DATABASE_WRITE_URL"),
+ redisUrl: getEnv("REDIS_URL"),
+ },
+ next: {
+ authSecret: getEnv("NEXTAUTH_SECRET"),
+ },
+ };
+};
+
+export default loadConfig;
diff --git a/apps/api/v2/src/config/type.ts b/apps/api/v2/src/config/type.ts
new file mode 100644
index 00000000000000..e7690ac63fd675
--- /dev/null
+++ b/apps/api/v2/src/config/type.ts
@@ -0,0 +1,18 @@
+export type AppConfig = {
+ env: {
+ type: "production" | "development";
+ };
+ api: {
+ port: number;
+ path: string;
+ url: string;
+ };
+ db: {
+ readUrl: string;
+ writeUrl: string;
+ redisUrl: string;
+ };
+ next: {
+ authSecret: string;
+ };
+};
diff --git a/apps/api/v2/src/ee/LICENSE b/apps/api/v2/src/ee/LICENSE
new file mode 100644
index 00000000000000..a8c6744758303a
--- /dev/null
+++ b/apps/api/v2/src/ee/LICENSE
@@ -0,0 +1,42 @@
+The Cal.com Commercial License (the âCommercial Licenseâ)
+Copyright (c) 2020-present Cal.com, Inc
+
+With regard to the Cal.com Software:
+
+This software and associated documentation files (the "Software") may only be
+used in production, if you (and any entity that you represent) have agreed to,
+and are in compliance with, the Cal.com Subscription Terms available
+at https://cal.com/terms, or other agreements governing
+the use of the Software, as mutually agreed by you and Cal.com, Inc ("Cal.com"),
+and otherwise have a valid Cal.com Enterprise Edition subscription ("Commercial Subscription")
+for the correct number of hosts as defined in the "Commercial Terms ("Hosts"). Subject to the foregoing sentence,
+you are free to modify this Software and publish patches to the Software. You agree
+that Cal.com and/or its licensors (as applicable) retain all right, title and interest in
+and to all such modifications and/or patches, and all such modifications and/or
+patches may only be used, copied, modified, displayed, distributed, or otherwise
+exploited with a valid Commercial Subscription for the correct number of hosts.
+Notwithstanding the foregoing, you may copy and modify the Software for development
+and testing purposes, without requiring a subscription. You agree that Cal.com and/or
+its licensors (as applicable) retain all right, title and interest in and to all such
+modifications. You are not granted any other rights beyond what is expressly stated herein.
+Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
+and/or sell the Software.
+
+This Commercial License applies only to the part of this Software that is not distributed under
+the AGPLv3 license. Any part of this Software distributed under the MIT license or which
+is served client-side as an image, font, cascading stylesheet (CSS), file which produces
+or is compiled, arranged, augmented, or combined into client-side JavaScript, in whole or
+in part, is copyrighted under the AGPLv3 license. The full text of this Commercial License shall
+be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+For all third party components incorporated into the Cal.com Software, those
+components are licensed under the original license provided by the owner of the
+applicable component.
diff --git a/apps/api/v2/src/ee/bookings/bookings.module.ts b/apps/api/v2/src/ee/bookings/bookings.module.ts
new file mode 100644
index 00000000000000..8b52876bfa1323
--- /dev/null
+++ b/apps/api/v2/src/ee/bookings/bookings.module.ts
@@ -0,0 +1,13 @@
+import { BookingsController } from "@/ee/bookings/controllers/bookings.controller";
+import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule, TokensModule],
+ providers: [TokensRepository, OAuthFlowService],
+ controllers: [BookingsController],
+})
+export class BookingsModule {}
diff --git a/apps/api/v2/src/ee/bookings/controllers/bookings.controller.ts b/apps/api/v2/src/ee/bookings/controllers/bookings.controller.ts
new file mode 100644
index 00000000000000..1377970f95976e
--- /dev/null
+++ b/apps/api/v2/src/ee/bookings/controllers/bookings.controller.ts
@@ -0,0 +1,128 @@
+import { CreateBookingInput } from "@/ee/bookings/inputs/create-booking.input";
+import { CreateReccuringBookingInput } from "@/ee/bookings/inputs/create-reccuring-booking.input";
+import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
+import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
+import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
+import {
+ Controller,
+ Post,
+ Logger,
+ Req,
+ InternalServerErrorException,
+ Body,
+ HttpException,
+ UseGuards,
+} from "@nestjs/common";
+import { Request } from "express";
+import { NextApiRequest } from "next/types";
+
+import { BOOKING_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
+import {
+ handleNewBooking,
+ BookingResponse,
+ HttpError,
+ handleNewRecurringBooking,
+ handleInstantMeeting,
+} from "@calcom/platform-libraries";
+import { ApiResponse } from "@calcom/platform-types";
+
+@Controller({
+ path: "ee/bookings",
+ version: "2",
+})
+@UseGuards(PermissionsGuard)
+export class BookingsController {
+ private readonly logger = new Logger("ee bookings controller");
+
+ constructor(private readonly oAuthFlowService: OAuthFlowService) {}
+
+ @Post("/")
+ @Permissions([BOOKING_WRITE])
+ async createBooking(
+ @Req() req: Request & { userId?: number },
+ @Body() _: CreateBookingInput
+ ): Promise> {
+ req.userId = await this.getOwnerId(req);
+ req.body = { ...req.body, noEmail: true };
+ try {
+ const booking = await handleNewBooking(req as unknown as NextApiRequest & { userId?: number });
+ return {
+ status: SUCCESS_STATUS,
+ data: booking,
+ };
+ } catch (err) {
+ handleBookingErrors(err);
+ }
+ throw new InternalServerErrorException("Could not create booking.");
+ }
+
+ @Post("/reccuring")
+ @Permissions([BOOKING_WRITE])
+ async createReccuringBooking(
+ @Req() req: Request & { userId?: number },
+ @Body() _: CreateReccuringBookingInput[]
+ ): Promise> {
+ req.userId = await this.getOwnerId(req);
+ req.body = { ...req.body, noEmail: true };
+ try {
+ const createdBookings: BookingResponse[] = await handleNewRecurringBooking(
+ req as unknown as NextApiRequest & { userId?: number }
+ );
+ return {
+ status: SUCCESS_STATUS,
+ data: createdBookings,
+ };
+ } catch (err) {
+ handleBookingErrors(err, "recurring");
+ }
+ throw new InternalServerErrorException("Could not create recurring booking.");
+ }
+
+ @Post("/instant")
+ @Permissions([BOOKING_WRITE])
+ async createInstantBooking(
+ @Req() req: Request & { userId?: number },
+ @Body() _: CreateBookingInput
+ ): Promise>>> {
+ req.userId = await this.getOwnerId(req);
+ req.body = { ...req.body, noEmail: true };
+ try {
+ const instantMeeting = await handleInstantMeeting(
+ req as unknown as NextApiRequest & { userId?: number }
+ );
+ return {
+ status: SUCCESS_STATUS,
+ data: instantMeeting,
+ };
+ } catch (err) {
+ handleBookingErrors(err, "instant");
+ }
+ throw new InternalServerErrorException("Could not create instant booking.");
+ }
+
+ async getOwnerId(req: Request): Promise {
+ try {
+ const accessToken = req.get("Authorization")?.replace("Bearer ", "");
+ if (accessToken) {
+ return this.oAuthFlowService.getOwnerId(accessToken);
+ }
+ } catch (err) {
+ this.logger.error(err);
+ }
+ }
+}
+
+function handleBookingErrors(err: Error | HttpError | unknown, type?: "recurring" | `instant`): void {
+ const errMsg = `Error while creating ${type ? type + " " : ""}booking.`;
+ if (err instanceof HttpError) {
+ const httpError = err as HttpError;
+ throw new HttpException(httpError?.message ?? errMsg, httpError?.statusCode ?? 500);
+ }
+
+ if (err instanceof Error) {
+ const error = err as Error;
+ throw new InternalServerErrorException(error?.message ?? errMsg);
+ }
+
+ throw new InternalServerErrorException(errMsg);
+}
diff --git a/apps/api/v2/src/ee/bookings/inputs/create-booking.input.ts b/apps/api/v2/src/ee/bookings/inputs/create-booking.input.ts
new file mode 100644
index 00000000000000..2e1d7965cfaffc
--- /dev/null
+++ b/apps/api/v2/src/ee/bookings/inputs/create-booking.input.ts
@@ -0,0 +1,58 @@
+import { Transform } from "class-transformer";
+import { IsBoolean, IsTimeZone, IsNumber, IsString, IsOptional, IsArray } from "class-validator";
+
+export class CreateBookingInput {
+ @IsString()
+ @IsOptional()
+ end?: string;
+
+ @IsString()
+ start!: string;
+
+ @IsNumber()
+ eventTypeId!: number;
+
+ @IsString()
+ @IsOptional()
+ eventTypeSlug?: string;
+
+ @IsString()
+ @IsOptional()
+ rescheduleUid?: string;
+
+ @IsString()
+ @IsOptional()
+ recurringEventId?: string;
+
+ @IsTimeZone()
+ timeZone!: string;
+
+ @Transform(({ value }: { value: string | string[] }) => {
+ return typeof value === "string" ? [value] : value;
+ })
+ @IsOptional()
+ @IsArray()
+ @IsString({ each: true })
+ user?: string[];
+
+ @IsString()
+ language!: string;
+
+ @IsString()
+ @IsOptional()
+ bookingUid?: string;
+
+ metadata!: Record;
+
+ @IsBoolean()
+ @IsOptional()
+ hasHashedBookingLink?: boolean;
+
+ @IsString()
+ @IsOptional()
+ hashedLink!: string | null;
+
+ @IsString()
+ @IsOptional()
+ seatReferenceUid?: string;
+}
diff --git a/apps/api/v2/src/ee/bookings/inputs/create-reccuring-booking.input.ts b/apps/api/v2/src/ee/bookings/inputs/create-reccuring-booking.input.ts
new file mode 100644
index 00000000000000..1d36e5d6fe0d46
--- /dev/null
+++ b/apps/api/v2/src/ee/bookings/inputs/create-reccuring-booking.input.ts
@@ -0,0 +1,24 @@
+import { CreateBookingInput } from "@/ee/bookings/inputs/create-booking.input";
+import { IsBoolean, IsNumber, IsString, IsOptional, IsArray } from "class-validator";
+
+import type { AppsStatus } from "@calcom/platform-libraries";
+
+export class CreateReccuringBookingInput extends CreateBookingInput {
+ @IsBoolean()
+ @IsOptional()
+ noEmail?: boolean;
+
+ @IsOptional()
+ @IsNumber()
+ recurringCount?: number;
+
+ @IsOptional()
+ appsStatus?: AppsStatus[] | undefined;
+
+ @IsOptional()
+ allRecurringDates?: Record[];
+
+ @IsOptional()
+ @IsNumber()
+ currentRecurringIndex?: number;
+}
diff --git a/apps/api/v2/src/ee/calendars/calendars.module.ts b/apps/api/v2/src/ee/calendars/calendars.module.ts
new file mode 100644
index 00000000000000..fe6b382605aace
--- /dev/null
+++ b/apps/api/v2/src/ee/calendars/calendars.module.ts
@@ -0,0 +1,14 @@
+import { CalendarsController } from "@/ee/calendars/controllers/calendars.controller";
+import { CalendarsService } from "@/ee/calendars/services/calendars.service";
+import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { UsersModule } from "@/modules/users/users.module";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule, UsersModule],
+ providers: [CredentialsRepository, CalendarsService],
+ controllers: [CalendarsController],
+ exports: [CalendarsService],
+})
+export class CalendarsModule {}
diff --git a/apps/api/v2/src/ee/calendars/controllers/calendars.controller.ts b/apps/api/v2/src/ee/calendars/controllers/calendars.controller.ts
new file mode 100644
index 00000000000000..829cfa944c3388
--- /dev/null
+++ b/apps/api/v2/src/ee/calendars/controllers/calendars.controller.ts
@@ -0,0 +1,59 @@
+import { CalendarsService } from "@/ee/calendars/services/calendars.service";
+import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
+import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
+import { UserWithProfile } from "@/modules/users/users.repository";
+import { Controller, Get, Logger, UseGuards, Query } from "@nestjs/common";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { ConnectedDestinationCalendars } from "@calcom/platform-libraries";
+import { CalendarBusyTimesInput } from "@calcom/platform-types";
+import { ApiResponse } from "@calcom/platform-types";
+import { EventBusyDate } from "@calcom/types/Calendar";
+
+@Controller({
+ path: "ee/calendars",
+ version: "2",
+})
+@UseGuards(AccessTokenGuard)
+export class CalendarsController {
+ private readonly logger = new Logger("ee overlay calendars controller");
+
+ constructor(private readonly calendarsService: CalendarsService) {}
+
+ @Get("/busy-times")
+ async getBusyTimes(
+ @Query() queryParams: CalendarBusyTimesInput,
+ @GetUser() user: UserWithProfile
+ ): Promise> {
+ const { loggedInUsersTz, dateFrom, dateTo, calendarsToLoad } = queryParams;
+ if (!dateFrom || !dateTo) {
+ return {
+ status: SUCCESS_STATUS,
+ data: [],
+ };
+ }
+
+ const busyTimes = await this.calendarsService.getBusyTimes(
+ calendarsToLoad,
+ user.id,
+ dateFrom,
+ dateTo,
+ loggedInUsersTz
+ );
+
+ return {
+ status: SUCCESS_STATUS,
+ data: busyTimes,
+ };
+ }
+
+ @Get("/")
+ async getCalendars(@GetUser("id") userId: number): Promise> {
+ const calendars = await this.calendarsService.getCalendars(userId);
+
+ return {
+ status: SUCCESS_STATUS,
+ data: calendars,
+ };
+ }
+}
diff --git a/apps/api/v2/src/ee/calendars/services/calendars.service.ts b/apps/api/v2/src/ee/calendars/services/calendars.service.ts
new file mode 100644
index 00000000000000..5ad1297bddbc51
--- /dev/null
+++ b/apps/api/v2/src/ee/calendars/services/calendars.service.ts
@@ -0,0 +1,111 @@
+import {
+ CredentialsRepository,
+ CredentialsWithUserEmail,
+} from "@/modules/credentials/credentials.repository";
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { UsersRepository } from "@/modules/users/users.repository";
+import {
+ Injectable,
+ InternalServerErrorException,
+ UnauthorizedException,
+ NotFoundException,
+} from "@nestjs/common";
+import { User } from "@prisma/client";
+import { DateTime } from "luxon";
+
+import { getConnectedDestinationCalendars } from "@calcom/platform-libraries";
+import { getBusyCalendarTimes } from "@calcom/platform-libraries";
+import { Calendar } from "@calcom/platform-types";
+import { PrismaClient } from "@calcom/prisma";
+
+@Injectable()
+export class CalendarsService {
+ constructor(
+ private readonly usersRepository: UsersRepository,
+ private readonly credentialsRepository: CredentialsRepository,
+ private readonly dbRead: PrismaReadService
+ ) {}
+
+ async getCalendars(userId: number) {
+ const userWithCalendars = await this.usersRepository.findByIdWithCalendars(userId);
+ if (!userWithCalendars) {
+ throw new NotFoundException("User not found");
+ }
+
+ return getConnectedDestinationCalendars(
+ userWithCalendars,
+ false,
+ this.dbRead.prisma as unknown as PrismaClient
+ );
+ }
+
+ async getBusyTimes(
+ calendarsToLoad: Calendar[],
+ userId: User["id"],
+ dateFrom: string,
+ dateTo: string,
+ timezone: string
+ ) {
+ const credentials = await this.getUniqCalendarCredentials(calendarsToLoad, userId);
+ const composedSelectedCalendars = await this.getCalendarsWithCredentials(
+ credentials,
+ calendarsToLoad,
+ userId
+ );
+ try {
+ const calendarBusyTimes = await getBusyCalendarTimes(
+ "",
+ credentials,
+ dateFrom,
+ dateTo,
+ composedSelectedCalendars
+ );
+ const calendarBusyTimesConverted = calendarBusyTimes.map((busyTime) => {
+ const busyTimeStart = DateTime.fromJSDate(new Date(busyTime.start)).setZone(timezone);
+ const busyTimeEnd = DateTime.fromJSDate(new Date(busyTime.end)).setZone(timezone);
+ const busyTimeStartDate = busyTimeStart.toJSDate();
+ const busyTimeEndDate = busyTimeEnd.toJSDate();
+ return {
+ ...busyTime,
+ start: busyTimeStartDate,
+ end: busyTimeEndDate,
+ };
+ });
+ return calendarBusyTimesConverted;
+ } catch (error) {
+ throw new InternalServerErrorException(
+ "Unable to fetch connected calendars events. Please try again later."
+ );
+ }
+ }
+
+ async getUniqCalendarCredentials(calendarsToLoad: Calendar[], userId: User["id"]) {
+ const uniqueCredentialIds = Array.from(new Set(calendarsToLoad.map((item) => item.credentialId)));
+ const credentials = await this.credentialsRepository.getUserCredentialsByIds(userId, uniqueCredentialIds);
+
+ if (credentials.length !== uniqueCredentialIds.length) {
+ throw new UnauthorizedException("These credentials do not belong to you");
+ }
+
+ return credentials;
+ }
+
+ async getCalendarsWithCredentials(
+ credentials: CredentialsWithUserEmail,
+ calendarsToLoad: Calendar[],
+ userId: User["id"]
+ ) {
+ const composedSelectedCalendars = calendarsToLoad.map((calendar) => {
+ const credential = credentials.find((item) => item.id === calendar.credentialId);
+ if (!credential) {
+ throw new UnauthorizedException("These credentials do not belong to you");
+ }
+ return {
+ ...calendar,
+ userId,
+ integration: credential.type,
+ };
+ });
+ return composedSelectedCalendars;
+ }
+}
diff --git a/apps/api/v2/src/ee/event-types/constants/constants.ts b/apps/api/v2/src/ee/event-types/constants/constants.ts
new file mode 100644
index 00000000000000..690c1b4deceb6d
--- /dev/null
+++ b/apps/api/v2/src/ee/event-types/constants/constants.ts
@@ -0,0 +1,16 @@
+export const DEFAULT_EVENT_TYPES = {
+ thirtyMinutes: { length: 30, slug: "thirty-minutes", title: "30 Minutes" },
+ thirtyMinutesVideo: {
+ length: 30,
+ slug: "thirty-minutes-video",
+ title: "30 Minutes",
+ locations: [{ type: "integrations:daily" }],
+ },
+ sixtyMinutes: { length: 60, slug: "sixty-minutes", title: "60 Minutes" },
+ sixtyMinutesVideo: {
+ length: 60,
+ slug: "sixty-minutes-video",
+ title: "60 Minutes",
+ locations: [{ type: "integrations:daily" }],
+ },
+};
diff --git a/apps/api/v2/src/ee/event-types/controllers/event-types.controller.e2e-spec.ts b/apps/api/v2/src/ee/event-types/controllers/event-types.controller.e2e-spec.ts
new file mode 100644
index 00000000000000..821458499e7176
--- /dev/null
+++ b/apps/api/v2/src/ee/event-types/controllers/event-types.controller.e2e-spec.ts
@@ -0,0 +1,160 @@
+import { bootstrap } from "@/app";
+import { AppModule } from "@/app.module";
+import { EventTypesModule } from "@/ee/event-types/event-types.module";
+import { HttpExceptionFilter } from "@/filters/http-exception.filter";
+import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
+import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { UsersModule } from "@/modules/users/users.module";
+import { INestApplication } from "@nestjs/common";
+import { NestExpressApplication } from "@nestjs/platform-express";
+import { Test } from "@nestjs/testing";
+import { EventType, PlatformOAuthClient, Team, User } from "@prisma/client";
+import * as request from "supertest";
+import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
+import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
+import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
+import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
+import { withAccessTokenAuth } from "test/utils/withAccessTokenAuth";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { ApiSuccessResponse } from "@calcom/platform-types";
+
+describe("Event types Endpoints", () => {
+ describe("Not authenticated", () => {
+ let app: INestApplication;
+
+ beforeAll(async () => {
+ const moduleRef = await Test.createTestingModule({
+ providers: [PrismaExceptionFilter, HttpExceptionFilter],
+ imports: [AppModule, UsersModule, EventTypesModule, TokensModule],
+ })
+ .overrideGuard(PermissionsGuard)
+ .useValue({
+ canActivate: () => true,
+ })
+ .compile();
+
+ app = moduleRef.createNestApplication();
+ bootstrap(app as NestExpressApplication);
+ await app.init();
+ });
+
+ it(`/GET/:id`, () => {
+ return request(app.getHttpServer()).get("/api/v2/event-types/100").expect(401);
+ });
+
+ afterAll(async () => {
+ await app.close();
+ });
+ });
+
+ describe("User Authenticated", () => {
+ let app: INestApplication;
+
+ let oAuthClient: PlatformOAuthClient;
+ let organization: Team;
+ let userRepositoryFixture: UserRepositoryFixture;
+ let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
+ let teamRepositoryFixture: TeamRepositoryFixture;
+ let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
+
+ const userEmail = "test-e2e@api.com";
+ let eventType: EventType;
+ let user: User;
+
+ beforeAll(async () => {
+ const moduleRef = await withAccessTokenAuth(
+ userEmail,
+ Test.createTestingModule({
+ providers: [PrismaExceptionFilter, HttpExceptionFilter],
+ imports: [AppModule, UsersModule, EventTypesModule, TokensModule],
+ })
+ )
+ .overrideGuard(PermissionsGuard)
+ .useValue({
+ canActivate: () => true,
+ })
+ .compile();
+
+ app = moduleRef.createNestApplication();
+ bootstrap(app as NestExpressApplication);
+
+ oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
+ userRepositoryFixture = new UserRepositoryFixture(moduleRef);
+ teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
+
+ organization = await teamRepositoryFixture.create({ name: "organization" });
+ oAuthClient = await createOAuthClient(organization.id);
+ user = await userRepositoryFixture.create({
+ email: userEmail,
+ });
+
+ eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
+
+ eventType = await eventTypesRepositoryFixture.create(
+ {
+ length: 60,
+ title: "peer coding session",
+ slug: "peer-coding",
+ },
+ user.id
+ );
+
+ await app.init();
+ });
+
+ async function createOAuthClient(organizationId: number) {
+ const data = {
+ logo: "logo-url",
+ name: "name",
+ redirectUris: ["redirect-uri"],
+ permissions: 32,
+ };
+ const secret = "secret";
+
+ const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
+ return client;
+ }
+
+ it("should be defined", () => {
+ expect(oauthClientRepositoryFixture).toBeDefined();
+ expect(userRepositoryFixture).toBeDefined();
+ expect(oAuthClient).toBeDefined();
+ expect(user).toBeDefined();
+ });
+
+ it(`/GET/:id`, async () => {
+ const response = await request(app.getHttpServer())
+ .get(`/api/v2/event-types/${eventType.id}`)
+ // note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
+ .set("Authorization", `Bearer whatever`)
+ .expect(200);
+
+ const responseBody: ApiSuccessResponse = response.body;
+
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.id).toEqual(eventType.id);
+ expect(responseBody.data.title).toEqual(eventType.title);
+ expect(responseBody.data.slug).toEqual(eventType.slug);
+ expect(responseBody.data.userId).toEqual(user.id);
+ });
+
+ it(`/GET/:id not existing`, async () => {
+ await request(app.getHttpServer())
+ .get(`/api/v2/event-types/1000`)
+ // note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
+ .set("Authorization", `Bearer whatever`)
+ .expect(404);
+ });
+
+ afterAll(async () => {
+ await oauthClientRepositoryFixture.delete(oAuthClient.id);
+ await teamRepositoryFixture.delete(organization.id);
+ await eventTypesRepositoryFixture.delete(eventType.id);
+
+ await app.close();
+ });
+ });
+});
diff --git a/apps/api/v2/src/ee/event-types/controllers/event-types.controller.ts b/apps/api/v2/src/ee/event-types/controllers/event-types.controller.ts
new file mode 100644
index 00000000000000..3ecd826a79fd0c
--- /dev/null
+++ b/apps/api/v2/src/ee/event-types/controllers/event-types.controller.ts
@@ -0,0 +1,58 @@
+import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input";
+import { EventTypesService } from "@/ee/event-types/services/event-types.service";
+import { ForAtom } from "@/lib/atoms/decorators/for-atom.decorator";
+import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
+import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
+import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
+import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
+import { UserWithProfile } from "@/modules/users/users.repository";
+import { Controller, UseGuards, Get, Param, Post, Body, NotFoundException } from "@nestjs/common";
+import { EventType } from "@prisma/client";
+
+import { EVENT_TYPE_READ, EVENT_TYPE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { EventType as AtomEventType } from "@calcom/platform-libraries";
+import { ApiResponse, ApiSuccessResponse } from "@calcom/platform-types";
+
+@Controller({
+ path: "event-types",
+ version: "2",
+})
+@UseGuards(AccessTokenGuard, PermissionsGuard)
+export class EventTypesController {
+ constructor(private readonly eventTypesService: EventTypesService) {}
+
+ @Post("/")
+ @Permissions([EVENT_TYPE_WRITE])
+ async createEventType(
+ @Body() body: CreateEventTypeInput,
+ @GetUser() user: UserWithProfile
+ ): Promise> {
+ const eventType = await this.eventTypesService.createUserEventType(user.id, body);
+
+ return {
+ status: SUCCESS_STATUS,
+ data: eventType,
+ };
+ }
+
+ @Get("/:eventTypeId")
+ @Permissions([EVENT_TYPE_READ])
+ async getEventType(
+ @Param("eventTypeId") eventTypeId: string,
+ @ForAtom() forAtom: boolean,
+ @GetUser() user: UserWithProfile
+ ): Promise> {
+ const eventType = forAtom
+ ? await this.eventTypesService.getUserEventTypeForAtom(user, Number(eventTypeId))
+ : await this.eventTypesService.getUserEventType(user.id, Number(eventTypeId));
+
+ if (!eventType) {
+ throw new NotFoundException(`Event type with id ${eventTypeId} not found`);
+ }
+
+ return {
+ status: SUCCESS_STATUS,
+ data: eventType,
+ };
+ }
+}
diff --git a/apps/api/v2/src/ee/event-types/event-types.module.ts b/apps/api/v2/src/ee/event-types/event-types.module.ts
new file mode 100644
index 00000000000000..fa48ca0bfd0e1d
--- /dev/null
+++ b/apps/api/v2/src/ee/event-types/event-types.module.ts
@@ -0,0 +1,15 @@
+import { EventTypesController } from "@/ee/event-types/controllers/event-types.controller";
+import { EventTypesRepository } from "@/ee/event-types/event-types.repository";
+import { EventTypesService } from "@/ee/event-types/services/event-types.service";
+import { MembershipsModule } from "@/modules/memberships/memberships.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule, MembershipsModule, TokensModule],
+ providers: [EventTypesRepository, EventTypesService],
+ controllers: [EventTypesController],
+ exports: [EventTypesService, EventTypesRepository],
+})
+export class EventTypesModule {}
diff --git a/apps/api/v2/src/ee/event-types/event-types.repository.ts b/apps/api/v2/src/ee/event-types/event-types.repository.ts
new file mode 100644
index 00000000000000..ce658bf562b263
--- /dev/null
+++ b/apps/api/v2/src/ee/event-types/event-types.repository.ts
@@ -0,0 +1,62 @@
+import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input";
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { UserWithProfile } from "@/modules/users/users.repository";
+import { Injectable, NotFoundException } from "@nestjs/common";
+
+import { getEventTypeById } from "@calcom/platform-libraries";
+
+@Injectable()
+export class EventTypesRepository {
+ constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
+
+ async createUserEventType(userId: number, body: CreateEventTypeInput) {
+ return this.dbWrite.prisma.eventType.create({
+ data: {
+ ...body,
+ userId,
+ users: { connect: { id: userId } },
+ },
+ });
+ }
+
+ async getEventTypeWithSeats(eventTypeId: number) {
+ return this.dbRead.prisma.eventType.findUnique({
+ where: { id: eventTypeId },
+ select: { users: { select: { id: true } }, seatsPerTimeSlot: true },
+ });
+ }
+
+ async getUserEventType(userId: number, eventTypeId: number) {
+ return this.dbRead.prisma.eventType.findFirst({
+ where: {
+ id: eventTypeId,
+ userId,
+ },
+ });
+ }
+
+ async getUserEventTypeForAtom(
+ user: UserWithProfile,
+ isUserOrganizationAdmin: boolean,
+ eventTypeId: number
+ ) {
+ try {
+ return getEventTypeById({
+ currentOrganizationId: user.movedToProfile?.organizationId || user.organizationId,
+ eventTypeId,
+ userId: user.id,
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ prisma: this.dbRead.prisma,
+ isUserOrganizationAdmin,
+ });
+ } catch (error) {
+ throw new NotFoundException(`User with id ${user.id} has no event type with id ${eventTypeId}`);
+ }
+ }
+
+ async getEventTypeById(eventTypeId: number) {
+ return this.dbRead.prisma.eventType.findUnique({ where: { id: eventTypeId } });
+ }
+}
diff --git a/apps/api/v2/src/ee/event-types/inputs/create-event-type.input.ts b/apps/api/v2/src/ee/event-types/inputs/create-event-type.input.ts
new file mode 100644
index 00000000000000..9e58159fba8f5c
--- /dev/null
+++ b/apps/api/v2/src/ee/event-types/inputs/create-event-type.input.ts
@@ -0,0 +1,13 @@
+import { IsNumber, IsString, Min } from "class-validator";
+
+export class CreateEventTypeInput {
+ @IsNumber()
+ @Min(1)
+ length!: number;
+
+ @IsString()
+ slug!: string;
+
+ @IsString()
+ title!: string;
+}
diff --git a/apps/api/v2/src/ee/event-types/services/event-types.service.ts b/apps/api/v2/src/ee/event-types/services/event-types.service.ts
new file mode 100644
index 00000000000000..aefe23944d81e9
--- /dev/null
+++ b/apps/api/v2/src/ee/event-types/services/event-types.service.ts
@@ -0,0 +1,49 @@
+import { DEFAULT_EVENT_TYPES } from "@/ee/event-types/constants/constants";
+import { EventTypesRepository } from "@/ee/event-types/event-types.repository";
+import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input";
+import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
+import { UserWithProfile } from "@/modules/users/users.repository";
+import { Injectable } from "@nestjs/common";
+
+@Injectable()
+export class EventTypesService {
+ constructor(
+ private readonly eventTypesRepository: EventTypesRepository,
+ private readonly membershipsRepository: MembershipsRepository
+ ) {}
+
+ async createUserEventType(userId: number, body: CreateEventTypeInput) {
+ return this.eventTypesRepository.createUserEventType(userId, body);
+ }
+
+ async getUserEventType(userId: number, eventTypeId: number) {
+ return this.eventTypesRepository.getUserEventType(userId, eventTypeId);
+ }
+
+ async getUserEventTypeForAtom(user: UserWithProfile, eventTypeId: number) {
+ const organizationId = user.movedToProfile?.organizationId || user.organizationId;
+
+ const isUserOrganizationAdmin = organizationId
+ ? await this.membershipsRepository.isUserOrganizationAdmin(user.id, organizationId)
+ : false;
+
+ return this.eventTypesRepository.getUserEventTypeForAtom(user, isUserOrganizationAdmin, eventTypeId);
+ }
+
+ async createUserDefaultEventTypes(userId: number) {
+ const thirtyMinutes = DEFAULT_EVENT_TYPES.thirtyMinutes;
+ const thirtyMinutesVideo = DEFAULT_EVENT_TYPES.thirtyMinutesVideo;
+
+ const sixtyMinutes = DEFAULT_EVENT_TYPES.sixtyMinutes;
+ const sixtyMinutesVideo = DEFAULT_EVENT_TYPES.sixtyMinutesVideo;
+
+ const defaultEventTypes = await Promise.all([
+ this.eventTypesRepository.createUserEventType(userId, thirtyMinutes),
+ this.eventTypesRepository.createUserEventType(userId, sixtyMinutes),
+ this.eventTypesRepository.createUserEventType(userId, thirtyMinutesVideo),
+ this.eventTypesRepository.createUserEventType(userId, sixtyMinutesVideo),
+ ]);
+
+ return defaultEventTypes;
+ }
+}
diff --git a/apps/api/v2/src/ee/gcal/gcal.controller.e2e-spec.ts b/apps/api/v2/src/ee/gcal/gcal.controller.e2e-spec.ts
new file mode 100644
index 00000000000000..470a25cd247726
--- /dev/null
+++ b/apps/api/v2/src/ee/gcal/gcal.controller.e2e-spec.ts
@@ -0,0 +1,166 @@
+import { bootstrap } from "@/app";
+import { AppModule } from "@/app.module";
+import { HttpExceptionFilter } from "@/filters/http-exception.filter";
+import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
+import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { UsersModule } from "@/modules/users/users.module";
+import { INestApplication } from "@nestjs/common";
+import { NestExpressApplication } from "@nestjs/platform-express";
+import { Test } from "@nestjs/testing";
+import { PlatformOAuthClient, Team, User, Credential } from "@prisma/client";
+import * as request from "supertest";
+import { CredentialsRepositoryFixture } from "test/fixtures/repository/credentials.repository.fixture";
+import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
+import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
+import { TokensRepositoryFixture } from "test/fixtures/repository/tokens.repository.fixture";
+import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
+
+const CLIENT_REDIRECT_URI = "http://localhost:5555";
+
+describe("Platform Gcal Endpoints", () => {
+ let app: INestApplication;
+
+ let oAuthClient: PlatformOAuthClient;
+ let organization: Team;
+ let userRepositoryFixture: UserRepositoryFixture;
+ let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
+ let teamRepositoryFixture: TeamRepositoryFixture;
+ let tokensRepositoryFixture: TokensRepositoryFixture;
+ let credentialsRepositoryFixture: CredentialsRepositoryFixture;
+ let user: User;
+ let gcalCredentials: Credential;
+ let accessTokenSecret: string;
+ let refreshTokenSecret: string;
+ beforeAll(async () => {
+ const moduleRef = await Test.createTestingModule({
+ providers: [PrismaExceptionFilter, HttpExceptionFilter],
+ imports: [AppModule, UsersModule, TokensModule],
+ })
+ .overrideGuard(PermissionsGuard)
+ .useValue({
+ canActivate: () => true,
+ })
+ .compile();
+
+ app = moduleRef.createNestApplication();
+ bootstrap(app as NestExpressApplication);
+
+ oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
+ userRepositoryFixture = new UserRepositoryFixture(moduleRef);
+ teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
+ tokensRepositoryFixture = new TokensRepositoryFixture(moduleRef);
+ credentialsRepositoryFixture = new CredentialsRepositoryFixture(moduleRef);
+ organization = await teamRepositoryFixture.create({ name: "organization" });
+ oAuthClient = await createOAuthClient(organization.id);
+ user = await userRepositoryFixture.createOAuthManagedUser("gcal-connect@gmail.com", oAuthClient.id);
+ const tokens = await tokensRepositoryFixture.createTokens(user.id, oAuthClient.id);
+ accessTokenSecret = tokens.accessToken;
+ refreshTokenSecret = tokens.refreshToken;
+ await app.init();
+ });
+
+ async function createOAuthClient(organizationId: number) {
+ const data = {
+ logo: "logo-url",
+ name: "name",
+ redirectUris: [CLIENT_REDIRECT_URI],
+ permissions: 32,
+ };
+ const secret = "secret";
+
+ const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
+ return client;
+ }
+
+ it("should be defined", () => {
+ expect(oauthClientRepositoryFixture).toBeDefined();
+ expect(userRepositoryFixture).toBeDefined();
+ expect(oAuthClient).toBeDefined();
+ expect(accessTokenSecret).toBeDefined();
+ expect(refreshTokenSecret).toBeDefined();
+ expect(user).toBeDefined();
+ });
+
+ it(`/GET/platform/gcal/oauth/auth-url: it should respond 401 with invalid access token`, async () => {
+ await request(app.getHttpServer())
+ .get(`/api/v2/platform/gcal/oauth/auth-url`)
+ .set("Authorization", `Bearer invalid_access_token`)
+ .expect(401);
+ });
+
+ it(`/GET/platform/gcal/oauth/auth-url: it should auth-url to google oauth with valid access token `, async () => {
+ const response = await request(app.getHttpServer())
+ .get(`/api/v2/platform/gcal/oauth/auth-url`)
+ .set("Authorization", `Bearer ${accessTokenSecret}`)
+ .set("Origin", CLIENT_REDIRECT_URI)
+ .expect(200);
+ const data = response.body.data;
+ expect(data.authUrl).toBeDefined();
+ });
+
+ it(`/GET/platform/gcal/oauth/save: without oauth code`, async () => {
+ await request(app.getHttpServer())
+ .get(
+ `/api/v2/platform/gcal/oauth/save?state=accessToken=${accessTokenSecret}&origin%3D${CLIENT_REDIRECT_URI}&scope=https://www.googleapis.com/auth/calendar.readonly%20https://www.googleapis.com/auth/calendar.events`
+ )
+ .expect(400);
+ });
+
+ it(`/GET/platform/gcal/oauth/save: without access token`, async () => {
+ await request(app.getHttpServer())
+ .get(
+ `/api/v2/platform/gcal/oauth/save?state=origin%3D${CLIENT_REDIRECT_URI}&code=4/0AfJohXmBuT7QVrEPlAJLBu4ZcSnyj5jtDoJqSW_riPUhPXQ70RPGkOEbVO3xs-OzQwpPQw&scope=https://www.googleapis.com/auth/calendar.readonly%20https://www.googleapis.com/auth/calendar.events`
+ )
+ .expect(400);
+ });
+
+ it(`/GET/platform/gcal/oauth/save: without origin`, async () => {
+ await request(app.getHttpServer())
+ .get(
+ `/api/v2/platform/gcal/oauth/save?state=accessToken=${accessTokenSecret}&code=4/0AfJohXmBuT7QVrEPlAJLBu4ZcSnyj5jtDoJqSW_riPUhPXQ70RPGkOEbVO3xs-OzQwpPQw&scope=https://www.googleapis.com/auth/calendar.readonly%20https://www.googleapis.com/auth/calendar.events`
+ )
+ .expect(400);
+ });
+
+ it(`/GET/platform/gcal/check with access token but without origin`, async () => {
+ await request(app.getHttpServer())
+ .get(`/api/v2/platform/gcal/check`)
+ .set("Authorization", `Bearer ${accessTokenSecret}`)
+ .expect(400);
+ });
+
+ it(`/GET/platform/gcal/check without access token`, async () => {
+ await request(app.getHttpServer()).get(`/api/v2/platform/gcal/check`).expect(401);
+ });
+
+ it(`/GET/platform/gcal/check with access token and origin but no credentials`, async () => {
+ await request(app.getHttpServer())
+ .get(`/api/v2/platform/gcal/check`)
+ .set("Authorization", `Bearer ${accessTokenSecret}`)
+ .set("Origin", CLIENT_REDIRECT_URI)
+ .expect(400);
+ });
+
+ it(`/GET/platform/gcal/check with access token and origin and gcal credentials`, async () => {
+ gcalCredentials = await credentialsRepositoryFixture.create(
+ "google_calendar",
+ {},
+ user.id,
+ "google-calendar"
+ );
+ await request(app.getHttpServer())
+ .get(`/api/v2/platform/gcal/check`)
+ .set("Authorization", `Bearer ${accessTokenSecret}`)
+ .set("Origin", CLIENT_REDIRECT_URI)
+ .expect(200);
+ });
+
+ afterAll(async () => {
+ await oauthClientRepositoryFixture.delete(oAuthClient.id);
+ await teamRepositoryFixture.delete(organization.id);
+ await credentialsRepositoryFixture.delete(gcalCredentials.id);
+ await userRepositoryFixture.deleteByEmail(user.email);
+ await app.close();
+ });
+});
diff --git a/apps/api/v2/src/ee/gcal/gcal.controller.ts b/apps/api/v2/src/ee/gcal/gcal.controller.ts
new file mode 100644
index 00000000000000..9d5e1a76e2ac8c
--- /dev/null
+++ b/apps/api/v2/src/ee/gcal/gcal.controller.ts
@@ -0,0 +1,151 @@
+import { AppsRepository } from "@/modules/apps/apps.repository";
+import { GcalService } from "@/modules/apps/services/gcal.service";
+import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
+import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
+import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
+import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
+import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
+import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import {
+ BadRequestException,
+ Controller,
+ Get,
+ HttpCode,
+ HttpStatus,
+ Logger,
+ Query,
+ Redirect,
+ Req,
+ UnauthorizedException,
+ UseGuards,
+ Headers,
+} from "@nestjs/common";
+import { ConfigService } from "@nestjs/config";
+import { Request } from "express";
+import { google } from "googleapis";
+import { z } from "zod";
+
+import {
+ APPS_READ,
+ GOOGLE_CALENDAR_ID,
+ GOOGLE_CALENDAR_TYPE,
+ SUCCESS_STATUS,
+} from "@calcom/platform-constants";
+import { ApiRedirectResponseType, ApiResponse } from "@calcom/platform-types";
+
+const CALENDAR_SCOPES = [
+ "https://www.googleapis.com/auth/calendar.readonly",
+ "https://www.googleapis.com/auth/calendar.events",
+];
+
+@Controller({
+ path: "platform/gcal",
+ version: "2",
+})
+export class GcalController {
+ private readonly logger = new Logger("Platform Gcal Provider");
+
+ constructor(
+ private readonly appRepository: AppsRepository,
+ private readonly credentialRepository: CredentialsRepository,
+ private readonly tokensRepository: TokensRepository,
+ private readonly selectedCalendarsRepository: SelectedCalendarsRepository,
+ private readonly config: ConfigService,
+ private readonly gcalService: GcalService
+ ) {}
+
+ private redirectUri = `${this.config.get("api.url")}/platform/gcal/oauth/save`;
+
+ @Get("/oauth/auth-url")
+ @HttpCode(HttpStatus.OK)
+ @UseGuards(AccessTokenGuard)
+ async redirect(
+ @Headers("Authorization") authorization: string,
+ @Req() req: Request
+ ): Promise> {
+ const oAuth2Client = await this.gcalService.getOAuthClient(this.redirectUri);
+ const accessToken = authorization.replace("Bearer ", "");
+ const origin = req.get("origin") ?? req.get("host");
+ const authUrl = oAuth2Client.generateAuthUrl({
+ access_type: "offline",
+ scope: CALENDAR_SCOPES,
+ prompt: "consent",
+ state: `accessToken=${accessToken}&origin=${origin}`,
+ });
+ return { status: SUCCESS_STATUS, data: { authUrl } };
+ }
+
+ @Get("/oauth/save")
+ @Redirect(undefined, 301)
+ @HttpCode(HttpStatus.OK)
+ async save(@Query("state") state: string, @Query("code") code: string): Promise {
+ const stateParams = new URLSearchParams(state);
+ const { accessToken, origin } = z
+ .object({ accessToken: z.string(), origin: z.string() })
+ .parse({ accessToken: stateParams.get("accessToken"), origin: stateParams.get("origin") });
+
+ // User chose not to authorize your app or didn't authorize your app
+ // redirect directly without oauth code
+ if (!code) {
+ return { url: origin };
+ }
+
+ const parsedCode = z.string().parse(code);
+
+ const ownerId = await this.tokensRepository.getAccessTokenOwnerId(accessToken);
+
+ if (!ownerId) {
+ throw new UnauthorizedException("Invalid Access token.");
+ }
+
+ const oAuth2Client = await this.gcalService.getOAuthClient(this.redirectUri);
+ const token = await oAuth2Client.getToken(parsedCode);
+ const key = token.res?.data;
+ const credential = await this.credentialRepository.createAppCredential(
+ GOOGLE_CALENDAR_TYPE,
+ key,
+ ownerId
+ );
+
+ oAuth2Client.setCredentials(key);
+
+ const calendar = google.calendar({
+ version: "v3",
+ auth: oAuth2Client,
+ });
+
+ const cals = await calendar.calendarList.list({ fields: "items(id,summary,primary,accessRole)" });
+
+ const primaryCal = cals.data.items?.find((cal) => cal.primary);
+
+ if (primaryCal?.id) {
+ await this.selectedCalendarsRepository.createSelectedCalendar(
+ primaryCal.id,
+ credential.id,
+ ownerId,
+ GOOGLE_CALENDAR_ID
+ );
+ }
+
+ return { url: origin };
+ }
+
+ @Get("/check")
+ @HttpCode(HttpStatus.OK)
+ @UseGuards(AccessTokenGuard, PermissionsGuard)
+ @Permissions([APPS_READ])
+ async check(@GetUser("id") userId: number): Promise {
+ const gcalCredentials = await this.credentialRepository.getByTypeAndUserId("google_calendar", userId);
+
+ if (!gcalCredentials) {
+ throw new BadRequestException("Credentials for google_calendar not found.");
+ }
+
+ if (gcalCredentials.invalid) {
+ throw new BadRequestException("Invalid google oauth credentials.");
+ }
+
+ return { status: SUCCESS_STATUS };
+ }
+}
diff --git a/apps/api/v2/src/ee/gcal/gcal.module.ts b/apps/api/v2/src/ee/gcal/gcal.module.ts
new file mode 100644
index 00000000000000..238fb90ca29c7d
--- /dev/null
+++ b/apps/api/v2/src/ee/gcal/gcal.module.ts
@@ -0,0 +1,17 @@
+import { GcalController } from "@/ee/gcal/gcal.controller";
+import { AppsRepository } from "@/modules/apps/apps.repository";
+import { GcalService } from "@/modules/apps/services/gcal.service";
+import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
+import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { Module } from "@nestjs/common";
+import { ConfigService } from "@nestjs/config";
+
+@Module({
+ imports: [PrismaModule, TokensModule, OAuthClientModule],
+ providers: [AppsRepository, ConfigService, CredentialsRepository, SelectedCalendarsRepository, GcalService],
+ controllers: [GcalController],
+})
+export class GcalModule {}
diff --git a/apps/api/v2/src/ee/me/me.controller.e2e-spec.ts b/apps/api/v2/src/ee/me/me.controller.e2e-spec.ts
new file mode 100644
index 00000000000000..b5cf3c8e4b4ef0
--- /dev/null
+++ b/apps/api/v2/src/ee/me/me.controller.e2e-spec.ts
@@ -0,0 +1,131 @@
+import { bootstrap } from "@/app";
+import { AppModule } from "@/app.module";
+import { SchedulesRepository } from "@/ee/schedules/schedules.repository";
+import { SchedulesService } from "@/ee/schedules/services/schedules.service";
+import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
+import { AvailabilitiesModule } from "@/modules/availabilities/availabilities.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { UpdateUserInput } from "@/modules/users/inputs/update-user.input";
+import { UsersModule } from "@/modules/users/users.module";
+import { INestApplication } from "@nestjs/common";
+import { NestExpressApplication } from "@nestjs/platform-express";
+import { Test } from "@nestjs/testing";
+import { User } from "@prisma/client";
+import * as request from "supertest";
+import { SchedulesRepositoryFixture } from "test/fixtures/repository/schedules.repository.fixture";
+import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
+import { withAccessTokenAuth } from "test/utils/withAccessTokenAuth";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { UserResponse } from "@calcom/platform-types";
+import { ApiSuccessResponse } from "@calcom/platform-types";
+
+describe("Me Endpoints", () => {
+ describe("User Authentication", () => {
+ let app: INestApplication;
+
+ let userRepositoryFixture: UserRepositoryFixture;
+ let schedulesRepositoryFixture: SchedulesRepositoryFixture;
+
+ const userEmail = "me-controller-e2e@api.com";
+ let user: User;
+
+ beforeAll(async () => {
+ const moduleRef = await withAccessTokenAuth(
+ userEmail,
+ Test.createTestingModule({
+ imports: [AppModule, PrismaModule, AvailabilitiesModule, UsersModule, TokensModule],
+ providers: [SchedulesRepository, SchedulesService],
+ })
+ )
+ .overrideGuard(PermissionsGuard)
+ .useValue({
+ canActivate: () => true,
+ })
+ .compile();
+
+ userRepositoryFixture = new UserRepositoryFixture(moduleRef);
+ schedulesRepositoryFixture = new SchedulesRepositoryFixture(moduleRef);
+
+ user = await userRepositoryFixture.create({
+ email: userEmail,
+ });
+
+ app = moduleRef.createNestApplication();
+ bootstrap(app as NestExpressApplication);
+
+ await app.init();
+ });
+
+ it("should be defined", () => {
+ expect(userRepositoryFixture).toBeDefined();
+ expect(user).toBeDefined();
+ });
+
+ it("should get user associated with access token", async () => {
+ return request(app.getHttpServer())
+ .get("/api/v2/me")
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+
+ expect(responseBody.data.id).toEqual(user.id);
+ expect(responseBody.data.email).toEqual(user.email);
+ expect(responseBody.data.timeFormat).toEqual(user.timeFormat);
+ expect(responseBody.data.defaultScheduleId).toEqual(user.defaultScheduleId);
+ expect(responseBody.data.weekStart).toEqual(user.weekStart);
+ expect(responseBody.data.timeZone).toEqual(user.timeZone);
+ });
+ });
+
+ it("should update user associated with access token", async () => {
+ const body: UpdateUserInput = { timeZone: "Europe/Rome" };
+
+ return request(app.getHttpServer())
+ .patch("/api/v2/me")
+ .send(body)
+ .expect(200)
+ .then(async (response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+
+ expect(responseBody.data.id).toEqual(user.id);
+ expect(responseBody.data.email).toEqual(user.email);
+ expect(responseBody.data.timeFormat).toEqual(user.timeFormat);
+ expect(responseBody.data.defaultScheduleId).toEqual(user.defaultScheduleId);
+ expect(responseBody.data.weekStart).toEqual(user.weekStart);
+ expect(responseBody.data.timeZone).toEqual(body.timeZone);
+
+ if (user.defaultScheduleId) {
+ const defaultSchedule = await schedulesRepositoryFixture.getById(user.defaultScheduleId);
+ expect(defaultSchedule?.timeZone).toEqual(body.timeZone);
+ }
+ });
+ });
+
+ it("should not update user associated with access token given invalid timezone", async () => {
+ const bodyWithIncorrectTimeZone: UpdateUserInput = { timeZone: "Narnia/Woods" };
+
+ return request(app.getHttpServer()).patch("/api/v2/me").send(bodyWithIncorrectTimeZone).expect(400);
+ });
+
+ it("should not update user associated with access token given invalid time format", async () => {
+ const bodyWithIncorrectTimeFormat: UpdateUserInput = { timeFormat: 100 };
+
+ return request(app.getHttpServer()).patch("/api/v2/me").send(bodyWithIncorrectTimeFormat).expect(400);
+ });
+
+ it("should not update user associated with access token given invalid week start", async () => {
+ const bodyWithIncorrectWeekStart: UpdateUserInput = { weekStart: "waba luba dub dub" };
+
+ return request(app.getHttpServer()).patch("/api/v2/me").send(bodyWithIncorrectWeekStart).expect(400);
+ });
+
+ afterAll(async () => {
+ await userRepositoryFixture.deleteByEmail(user.email);
+ await app.close();
+ });
+ });
+});
diff --git a/apps/api/v2/src/ee/me/me.controller.ts b/apps/api/v2/src/ee/me/me.controller.ts
new file mode 100644
index 00000000000000..81cda17469d0e5
--- /dev/null
+++ b/apps/api/v2/src/ee/me/me.controller.ts
@@ -0,0 +1,56 @@
+import { SchedulesService } from "@/ee/schedules/services/schedules.service";
+import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
+import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
+import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
+import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
+import { UpdateUserInput } from "@/modules/users/inputs/update-user.input";
+import { UserWithProfile, UsersRepository } from "@/modules/users/users.repository";
+import { Controller, UseGuards, Get, Patch, Body } from "@nestjs/common";
+
+import { PROFILE_READ, PROFILE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
+import { UserResponse, userSchemaResponse } from "@calcom/platform-types";
+import { ApiResponse } from "@calcom/platform-types";
+
+@Controller({
+ path: "me",
+ version: "2",
+})
+@UseGuards(AccessTokenGuard, PermissionsGuard)
+export class MeController {
+ constructor(
+ private readonly usersRepository: UsersRepository,
+ private readonly schedulesRepository: SchedulesService
+ ) {}
+
+ @Get("/")
+ @Permissions([PROFILE_READ])
+ async getMe(@GetUser() user: UserWithProfile): Promise> {
+ const me = userSchemaResponse.parse(user);
+
+ return {
+ status: SUCCESS_STATUS,
+ data: me,
+ };
+ }
+
+ @Patch("/")
+ @Permissions([PROFILE_WRITE])
+ async updateMe(
+ @GetUser() user: UserWithProfile,
+ @Body() bodySchedule: UpdateUserInput
+ ): Promise> {
+ const updatedUser = await this.usersRepository.update(user.id, bodySchedule);
+ if (bodySchedule.timeZone && user.defaultScheduleId) {
+ await this.schedulesRepository.updateUserSchedule(user.id, user.defaultScheduleId, {
+ timeZone: bodySchedule.timeZone,
+ });
+ }
+
+ const me = userSchemaResponse.parse(updatedUser);
+
+ return {
+ status: SUCCESS_STATUS,
+ data: me,
+ };
+ }
+}
diff --git a/apps/api/v2/src/ee/me/me.module.ts b/apps/api/v2/src/ee/me/me.module.ts
new file mode 100644
index 00000000000000..b8802914f6c7d1
--- /dev/null
+++ b/apps/api/v2/src/ee/me/me.module.ts
@@ -0,0 +1,11 @@
+import { MeController } from "@/ee/me/me.controller";
+import { SchedulesModule } from "@/ee/schedules/schedules.module";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { UsersModule } from "@/modules/users/users.module";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [UsersModule, SchedulesModule, TokensModule],
+ controllers: [MeController],
+})
+export class MeModule {}
diff --git a/apps/api/v2/src/ee/platform-endpoints-module.ts b/apps/api/v2/src/ee/platform-endpoints-module.ts
new file mode 100644
index 00000000000000..fdf3ef34bb995d
--- /dev/null
+++ b/apps/api/v2/src/ee/platform-endpoints-module.ts
@@ -0,0 +1,29 @@
+import { BookingsModule } from "@/ee/bookings/bookings.module";
+import { CalendarsModule } from "@/ee/calendars/calendars.module";
+import { EventTypesModule } from "@/ee/event-types/event-types.module";
+import { GcalModule } from "@/ee/gcal/gcal.module";
+import { MeModule } from "@/ee/me/me.module";
+import { ProviderModule } from "@/ee/provider/provider.module";
+import { SchedulesModule } from "@/ee/schedules/schedules.module";
+import { SlotsModule } from "@/modules/slots/slots.module";
+import type { MiddlewareConsumer, NestModule } from "@nestjs/common";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [
+ GcalModule,
+ ProviderModule,
+ SchedulesModule,
+ MeModule,
+ EventTypesModule,
+ CalendarsModule,
+ BookingsModule,
+ SlotsModule,
+ ],
+})
+export class PlatformEndpointsModule implements NestModule {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ configure(_consumer: MiddlewareConsumer) {
+ // TODO: apply ratelimits
+ }
+}
diff --git a/apps/api/v2/src/ee/provider/provider.controller.ts b/apps/api/v2/src/ee/provider/provider.controller.ts
new file mode 100644
index 00000000000000..192459e1f6105b
--- /dev/null
+++ b/apps/api/v2/src/ee/provider/provider.controller.ts
@@ -0,0 +1,68 @@
+import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
+import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
+import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import { UserWithProfile } from "@/modules/users/users.repository";
+import {
+ BadRequestException,
+ Controller,
+ Get,
+ HttpCode,
+ HttpStatus,
+ Logger,
+ NotFoundException,
+ Param,
+ UnauthorizedException,
+ UseGuards,
+} from "@nestjs/common";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { ApiResponse } from "@calcom/platform-types";
+
+@Controller({
+ path: "platform/provider",
+ version: "2",
+})
+export class CalProviderController {
+ private readonly logger = new Logger("Platform Provider Controller");
+
+ constructor(
+ private readonly tokensRepository: TokensRepository,
+ private readonly oauthClientRepository: OAuthClientRepository
+ ) {}
+
+ @Get("/:clientId")
+ @HttpCode(HttpStatus.OK)
+ async verifyClientId(@Param("clientId") clientId: string): Promise {
+ if (!clientId) {
+ throw new NotFoundException();
+ }
+ const oAuthClient = await this.oauthClientRepository.getOAuthClient(clientId);
+
+ if (!oAuthClient) throw new UnauthorizedException();
+
+ return {
+ status: SUCCESS_STATUS,
+ };
+ }
+
+ @Get("/:clientId/access-token")
+ @HttpCode(HttpStatus.OK)
+ @UseGuards(AccessTokenGuard)
+ async verifyAccessToken(
+ @Param("clientId") clientId: string,
+ @GetUser() user: UserWithProfile
+ ): Promise {
+ if (!clientId) {
+ throw new BadRequestException();
+ }
+
+ if (!user) {
+ throw new UnauthorizedException();
+ }
+
+ return {
+ status: SUCCESS_STATUS,
+ };
+ }
+}
diff --git a/apps/api/v2/src/ee/provider/provider.module.ts b/apps/api/v2/src/ee/provider/provider.module.ts
new file mode 100644
index 00000000000000..d96be50d3a6fbb
--- /dev/null
+++ b/apps/api/v2/src/ee/provider/provider.module.ts
@@ -0,0 +1,13 @@
+import { CalProviderController } from "@/ee/provider/provider.controller";
+import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
+import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule, TokensModule, OAuthClientModule],
+ providers: [CredentialsRepository],
+ controllers: [CalProviderController],
+})
+export class ProviderModule {}
diff --git a/apps/api/v2/src/ee/schedules/controllers/schedules.controller.e2e-spec.ts b/apps/api/v2/src/ee/schedules/controllers/schedules.controller.e2e-spec.ts
new file mode 100644
index 00000000000000..253d52a55059fd
--- /dev/null
+++ b/apps/api/v2/src/ee/schedules/controllers/schedules.controller.e2e-spec.ts
@@ -0,0 +1,366 @@
+import { bootstrap } from "@/app";
+import { AppModule } from "@/app.module";
+import { SchedulesRepository } from "@/ee/schedules/schedules.repository";
+import { SchedulesService } from "@/ee/schedules/services/schedules.service";
+import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
+import { AvailabilitiesModule } from "@/modules/availabilities/availabilities.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { UsersModule } from "@/modules/users/users.module";
+import { INestApplication } from "@nestjs/common";
+import { NestExpressApplication } from "@nestjs/platform-express";
+import { Test } from "@nestjs/testing";
+import { User } from "@prisma/client";
+import * as request from "supertest";
+import { SchedulesRepositoryFixture } from "test/fixtures/repository/schedules.repository.fixture";
+import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
+import { withAccessTokenAuth } from "test/utils/withAccessTokenAuth";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import {
+ ScheduleWithAvailabilities,
+ ScheduleWithAvailabilitiesForWeb,
+ UpdateScheduleOutputType,
+} from "@calcom/platform-libraries";
+import { ScheduleResponse, UpdateScheduleInput } from "@calcom/platform-types";
+import { ApiSuccessResponse } from "@calcom/platform-types";
+
+describe("Schedules Endpoints", () => {
+ describe("User Authentication", () => {
+ let app: INestApplication;
+
+ let userRepositoryFixture: UserRepositoryFixture;
+ let scheduleRepositoryFixture: SchedulesRepositoryFixture;
+
+ const userEmail = "schedules-controller-e2e@api.com";
+ let user: User;
+
+ let createdSchedule: ScheduleResponse;
+
+ beforeAll(async () => {
+ const moduleRef = await withAccessTokenAuth(
+ userEmail,
+ Test.createTestingModule({
+ imports: [AppModule, PrismaModule, AvailabilitiesModule, UsersModule, TokensModule],
+ providers: [SchedulesRepository, SchedulesService],
+ })
+ )
+ .overrideGuard(PermissionsGuard)
+ .useValue({
+ canActivate: () => true,
+ })
+ .compile();
+
+ userRepositoryFixture = new UserRepositoryFixture(moduleRef);
+ scheduleRepositoryFixture = new SchedulesRepositoryFixture(moduleRef);
+ user = await userRepositoryFixture.create({
+ email: userEmail,
+ });
+
+ app = moduleRef.createNestApplication();
+ bootstrap(app as NestExpressApplication);
+
+ await app.init();
+ });
+
+ it("should be defined", () => {
+ expect(userRepositoryFixture).toBeDefined();
+ expect(user).toBeDefined();
+ });
+
+ it("should create a default schedule", async () => {
+ const scheduleName = "schedule-name";
+ const scheduleTimeZone = "Europe/Rome";
+
+ const body = {
+ name: scheduleName,
+ timeZone: scheduleTimeZone,
+ };
+
+ return request(app.getHttpServer())
+ .post("/api/v2/schedules")
+ .send(body)
+ .expect(201)
+ .then(async (response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.id).toBeDefined();
+ expect(responseBody.data.userId).toEqual(user.id);
+ expect(responseBody.data.name).toEqual(scheduleName);
+ expect(responseBody.data.timeZone).toEqual(scheduleTimeZone);
+
+ expect(responseBody.data.availability).toBeDefined();
+ expect(responseBody.data.availability?.length).toEqual(1);
+ const defaultAvailabilityDays = [1, 2, 3, 4, 5];
+ const defaultAvailabilityStartTime = "09:00:00";
+ const defaultAvailabilityEndTime = "17:00:00";
+
+ expect(responseBody.data.availability?.[0]?.days).toEqual(defaultAvailabilityDays);
+ expect(responseBody.data.availability?.[0]?.startTime).toEqual(defaultAvailabilityStartTime);
+ expect(responseBody.data.availability?.[0]?.endTime).toEqual(defaultAvailabilityEndTime);
+
+ const scheduleUser = await userRepositoryFixture.get(responseBody.data.userId);
+ expect(scheduleUser?.defaultScheduleId).toEqual(responseBody.data.id);
+ await scheduleRepositoryFixture.deleteById(responseBody.data.id);
+ await scheduleRepositoryFixture.deleteAvailabilities(responseBody.data.id);
+ });
+ });
+
+ it("should create a schedule", async () => {
+ const scheduleName = "schedule-name";
+ const scheduleTimeZone = "Europe/Rome";
+ const availabilityDays = [1, 2, 3, 4, 5, 6];
+ const availabilityStartTime = "11:00:00";
+ const availabilityEndTime = "14:00:00";
+
+ const body = {
+ name: scheduleName,
+ timeZone: scheduleTimeZone,
+ availabilities: [
+ {
+ days: availabilityDays,
+ startTime: availabilityStartTime,
+ endTime: availabilityEndTime,
+ },
+ ],
+ };
+
+ return request(app.getHttpServer())
+ .post("/api/v2/schedules")
+ .send(body)
+ .expect(201)
+ .then(async (response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.id).toBeDefined();
+ expect(responseBody.data.userId).toEqual(user.id);
+ expect(responseBody.data.name).toEqual(scheduleName);
+ expect(responseBody.data.timeZone).toEqual(scheduleTimeZone);
+
+ expect(responseBody.data.availability).toBeDefined();
+ expect(responseBody.data.availability?.length).toEqual(1);
+ expect(responseBody.data.availability?.[0]?.days).toEqual(availabilityDays);
+ expect(responseBody.data.availability?.[0]?.startTime).toEqual(availabilityStartTime);
+ expect(responseBody.data.availability?.[0]?.endTime).toEqual(availabilityEndTime);
+
+ createdSchedule = responseBody.data;
+
+ const scheduleUser = await userRepositoryFixture.get(responseBody.data.userId);
+ expect(scheduleUser?.defaultScheduleId).toEqual(responseBody.data.id);
+ });
+ });
+
+ it("should get default schedule", async () => {
+ return request(app.getHttpServer())
+ .get("/api/v2/schedules/default")
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.id).toBeDefined();
+ expect(responseBody.data.userId).toEqual(createdSchedule.userId);
+ expect(responseBody.data.name).toEqual(createdSchedule.name);
+ expect(responseBody.data.timeZone).toEqual(createdSchedule.timeZone);
+
+ expect(responseBody.data.availability).toBeDefined();
+ expect(responseBody.data.availability?.length).toEqual(1);
+
+ expect(responseBody.data.availability?.[0]?.days).toEqual(createdSchedule.availability?.[0]?.days);
+ expect(responseBody.data.availability?.[0]?.startTime).toEqual(
+ createdSchedule.availability?.[0]?.startTime
+ );
+ expect(responseBody.data.availability?.[0]?.endTime).toEqual(
+ createdSchedule.availability?.[0]?.endTime
+ );
+ });
+ });
+
+ it("should get schedule", async () => {
+ return request(app.getHttpServer())
+ .get(`/api/v2/schedules/${createdSchedule.id}`)
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.id).toBeDefined();
+ expect(
+ (responseBody.data as unknown as ScheduleWithAvailabilitiesForWeb).dateOverrides
+ ).toBeFalsy();
+ expect(responseBody.data.userId).toEqual(createdSchedule.userId);
+ expect(responseBody.data.name).toEqual(createdSchedule.name);
+ expect(responseBody.data.timeZone).toEqual(createdSchedule.timeZone);
+
+ expect(responseBody.data.availability).toBeDefined();
+ expect(responseBody.data.availability?.length).toEqual(1);
+
+ expect(responseBody.data.availability?.[0]?.days).toEqual(createdSchedule.availability?.[0]?.days);
+ expect(responseBody.data.availability?.[0]?.startTime).toEqual(
+ createdSchedule.availability?.[0]?.startTime
+ );
+ expect(responseBody.data.availability?.[0]?.endTime).toEqual(
+ createdSchedule.availability?.[0]?.endTime
+ );
+ });
+ });
+
+ it("should get schedule for atom", async () => {
+ const forAtomQueryParam = "?for=atom";
+ return request(app.getHttpServer())
+ .get(`/api/v2/schedules/${createdSchedule.id}${forAtomQueryParam}`)
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.id).toBeDefined();
+ expect(responseBody.data.dateOverrides).toBeDefined();
+ expect((responseBody.data as unknown as ScheduleWithAvailabilities).userId).toBeFalsy();
+ expect(responseBody.data.name).toEqual(createdSchedule.name);
+ expect(responseBody.data.timeZone).toEqual(createdSchedule.timeZone);
+
+ expect(responseBody.data.availability).toBeDefined();
+ expect(responseBody.data.availability?.length).toEqual(7);
+
+ const dateStart = new Date(responseBody.data.availability?.[1]?.[0]?.start);
+ const dateEnd = new Date(responseBody.data.availability?.[1]?.[0]?.end);
+
+ const dateStartHours = dateStart.getUTCHours().toString().padStart(2, "0");
+ const dateStartMinutes = dateStart.getUTCMinutes().toString().padStart(2, "0");
+ const dateStartSeconds = dateStart.getUTCSeconds().toString().padStart(2, "0");
+ const dateEndHours = dateEnd.getUTCHours().toString().padStart(2, "0");
+ const dateEndMinutes = dateEnd.getUTCMinutes().toString().padStart(2, "0");
+ const dateEndSeconds = dateEnd.getUTCSeconds().toString().padStart(2, "0");
+
+ const expectedStart = `${dateStartHours}:${dateStartMinutes}:${dateStartSeconds}`;
+ const expectedEnd = `${dateEndHours}:${dateEndMinutes}:${dateEndSeconds}`;
+
+ expect(expectedStart).toEqual(createdSchedule.availability?.[0]?.startTime);
+ expect(expectedEnd).toEqual(createdSchedule.availability?.[0]?.endTime);
+ });
+ });
+
+ it("should get schedules", async () => {
+ return request(app.getHttpServer())
+ .get(`/api/v2/schedules`)
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.length).toEqual(1);
+
+ const fetchedSchedule = responseBody.data[0];
+ expect(fetchedSchedule).toBeDefined();
+ expect(fetchedSchedule.userId).toEqual(createdSchedule.userId);
+ expect(fetchedSchedule.name).toEqual(createdSchedule.name);
+ expect(fetchedSchedule.timeZone).toEqual(createdSchedule.timeZone);
+
+ expect(fetchedSchedule.availability).toBeDefined();
+ expect(fetchedSchedule.availability?.length).toEqual(1);
+
+ expect(fetchedSchedule.availability?.[0]?.days).toEqual(createdSchedule.availability?.[0]?.days);
+ expect(fetchedSchedule.availability?.[0]?.startTime).toEqual(
+ createdSchedule.availability?.[0]?.startTime
+ );
+ expect(fetchedSchedule.availability?.[0]?.endTime).toEqual(
+ createdSchedule.availability?.[0]?.endTime
+ );
+ });
+ });
+
+ it("should update schedule name", async () => {
+ const newScheduleName = "new-schedule-name";
+
+ const schedule = [
+ [],
+ [
+ {
+ start: new Date("2024-03-05T11:00:00.000Z"),
+ end: new Date("2024-03-05T14:00:00.000Z"),
+ },
+ ],
+ [
+ {
+ start: new Date("2024-03-05T11:00:00.000Z"),
+ end: new Date("2024-03-05T14:00:00.000Z"),
+ },
+ ],
+ [
+ {
+ start: new Date("2024-03-05T11:00:00.000Z"),
+ end: new Date("2024-03-05T14:00:00.000Z"),
+ },
+ ],
+ [
+ {
+ start: new Date("2024-03-05T11:00:00.000Z"),
+ end: new Date("2024-03-05T14:00:00.000Z"),
+ },
+ ],
+ [
+ {
+ start: new Date("2024-03-05T11:00:00.000Z"),
+ end: new Date("2024-03-05T14:00:00.000Z"),
+ },
+ ],
+ [
+ {
+ start: new Date("2024-03-05T11:00:00.000Z"),
+ end: new Date("2024-03-05T14:00:00.000Z"),
+ },
+ ],
+ ];
+
+ const body: UpdateScheduleInput = {
+ scheduleId: createdSchedule.id,
+ name: newScheduleName,
+ schedule,
+ };
+
+ return request(app.getHttpServer())
+ .patch(`/api/v2/schedules/${createdSchedule.id}`)
+ .send(body)
+ .expect(200)
+ .then((response: any) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.schedule.id).toBeDefined();
+ expect(responseBody.data.schedule.userId).toEqual(createdSchedule.userId);
+ expect(responseBody.data.schedule.name).toEqual(newScheduleName);
+ expect(responseBody.data.timeZone).toEqual(createdSchedule.timeZone);
+
+ expect(responseBody.data.availability).toBeDefined();
+ const availabilities = responseBody.data?.availability?.filter(
+ (availability: any[]) => availability.length
+ );
+ expect(availabilities?.length).toEqual(6);
+
+ expect(responseBody.data.schedule.availability?.[0]?.days).toEqual(
+ createdSchedule.availability?.[0]?.days
+ );
+ expect(responseBody.data.schedule.availability?.[0]?.startTime).toEqual(
+ `1970-01-01T${createdSchedule.availability?.[0]?.startTime}.000Z`
+ );
+ expect(responseBody.data.schedule.availability?.[0]?.endTime).toEqual(
+ `1970-01-01T${createdSchedule.availability?.[0]?.endTime}.000Z`
+ );
+
+ createdSchedule.name = newScheduleName;
+ });
+ });
+
+ it("should delete schedule", async () => {
+ return request(app.getHttpServer()).delete(`/api/v2/schedules/${createdSchedule.id}`).expect(200);
+ });
+
+ afterAll(async () => {
+ await userRepositoryFixture.deleteByEmail(user.email);
+ await app.close();
+ });
+ });
+});
diff --git a/apps/api/v2/src/ee/schedules/controllers/schedules.controller.ts b/apps/api/v2/src/ee/schedules/controllers/schedules.controller.ts
new file mode 100644
index 00000000000000..9598ef830be770
--- /dev/null
+++ b/apps/api/v2/src/ee/schedules/controllers/schedules.controller.ts
@@ -0,0 +1,148 @@
+import { ResponseService } from "@/ee/schedules/services/response/response.service";
+import { SchedulesService } from "@/ee/schedules/services/schedules.service";
+import { ForAtom } from "@/lib/atoms/decorators/for-atom.decorator";
+import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
+import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
+import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
+import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
+import { UserWithProfile } from "@/modules/users/users.repository";
+import {
+ Body,
+ Controller,
+ Delete,
+ Get,
+ HttpCode,
+ HttpStatus,
+ Param,
+ Post,
+ Patch,
+ UseGuards,
+} from "@nestjs/common";
+
+import { SCHEDULE_READ, SCHEDULE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { ScheduleWithAvailabilitiesForWeb } from "@calcom/platform-libraries";
+import type { CityTimezones } from "@calcom/platform-libraries";
+import { updateScheduleHandler } from "@calcom/platform-libraries";
+import type { UpdateScheduleOutputType } from "@calcom/platform-libraries";
+import { ApiSuccessResponse, ScheduleResponse, UpdateScheduleInput } from "@calcom/platform-types";
+import { ApiResponse } from "@calcom/platform-types";
+
+import { CreateScheduleInput } from "../inputs/create-schedule.input";
+
+@Controller({
+ path: "schedules",
+ version: "2",
+})
+@UseGuards(AccessTokenGuard, PermissionsGuard)
+export class SchedulesController {
+ constructor(
+ private readonly schedulesService: SchedulesService,
+ private readonly schedulesResponseService: ResponseService
+ ) {}
+
+ @Post("/")
+ @Permissions([SCHEDULE_WRITE])
+ async createSchedule(
+ @GetUser() user: UserWithProfile,
+ @Body() bodySchedule: CreateScheduleInput,
+ @ForAtom() forAtom: boolean
+ ): Promise> {
+ const schedule = await this.schedulesService.createUserSchedule(user.id, bodySchedule);
+ const scheduleFormatted = await this.schedulesResponseService.formatSchedule(forAtom, user, schedule);
+
+ return {
+ status: SUCCESS_STATUS,
+ data: scheduleFormatted,
+ };
+ }
+
+ @Get("/default")
+ @Permissions([SCHEDULE_READ])
+ async getDefaultSchedule(
+ @GetUser() user: UserWithProfile,
+ @ForAtom() forAtom: boolean
+ ): Promise> {
+ const schedule = await this.schedulesService.getUserScheduleDefault(user.id);
+ const scheduleFormatted = schedule
+ ? await this.schedulesResponseService.formatSchedule(forAtom, user, schedule)
+ : null;
+
+ return {
+ status: SUCCESS_STATUS,
+ data: scheduleFormatted,
+ };
+ }
+
+ @Get("/time-zones")
+ async getTimeZones(): Promise> {
+ const timeZones = await this.schedulesService.getSchedulePossibleTimeZones();
+
+ return {
+ status: SUCCESS_STATUS,
+ data: timeZones,
+ };
+ }
+
+ @Get("/:scheduleId")
+ @Permissions([SCHEDULE_READ])
+ async getSchedule(
+ @GetUser() user: UserWithProfile,
+ @Param("scheduleId") scheduleId: number,
+ @ForAtom() forAtom: boolean
+ ): Promise> {
+ const schedule = await this.schedulesService.getUserSchedule(user.id, scheduleId);
+ const scheduleFormatted = await this.schedulesResponseService.formatSchedule(forAtom, user, schedule);
+
+ return {
+ status: SUCCESS_STATUS,
+ data: scheduleFormatted,
+ };
+ }
+
+ @Get("/")
+ @Permissions([SCHEDULE_READ])
+ async getSchedules(
+ @GetUser() user: UserWithProfile,
+ @ForAtom() forAtom: boolean
+ ): Promise> {
+ const schedules = await this.schedulesService.getUserSchedules(user.id);
+ const schedulesFormatted = await this.schedulesResponseService.formatSchedules(forAtom, user, schedules);
+
+ return {
+ status: SUCCESS_STATUS,
+ data: schedulesFormatted,
+ };
+ }
+
+ // note(Lauris): currently this endpoint is atoms only
+ @Patch("/:scheduleId")
+ @Permissions([SCHEDULE_WRITE])
+ async updateSchedule(
+ @GetUser() user: UserWithProfile,
+ @Body() bodySchedule: UpdateScheduleInput
+ ): Promise> {
+ const updatedSchedule: UpdateScheduleOutputType = await updateScheduleHandler({
+ input: bodySchedule,
+ ctx: { user },
+ });
+
+ return {
+ status: SUCCESS_STATUS,
+ data: updatedSchedule,
+ };
+ }
+
+ @Delete("/:scheduleId")
+ @HttpCode(HttpStatus.OK)
+ @Permissions([SCHEDULE_WRITE])
+ async deleteSchedule(
+ @GetUser("id") userId: number,
+ @Param("scheduleId") scheduleId: number
+ ): Promise {
+ await this.schedulesService.deleteUserSchedule(userId, scheduleId);
+
+ return {
+ status: SUCCESS_STATUS,
+ };
+ }
+}
diff --git a/apps/api/v2/src/ee/schedules/inputs/create-schedule.input.ts b/apps/api/v2/src/ee/schedules/inputs/create-schedule.input.ts
new file mode 100644
index 00000000000000..3c40e768e12126
--- /dev/null
+++ b/apps/api/v2/src/ee/schedules/inputs/create-schedule.input.ts
@@ -0,0 +1,21 @@
+import { CreateAvailabilityInput } from "@/modules/availabilities/inputs/create-availability.input";
+import { Type } from "class-transformer";
+import { IsArray, IsBoolean, IsTimeZone, IsOptional, IsString, ValidateNested } from "class-validator";
+
+export class CreateScheduleInput {
+ @IsString()
+ name!: string;
+
+ @IsTimeZone()
+ timeZone!: string;
+
+ @IsArray()
+ @ValidateNested({ each: true })
+ @Type(() => CreateAvailabilityInput)
+ @IsOptional()
+ availabilities?: CreateAvailabilityInput[];
+
+ @IsBoolean()
+ @IsOptional()
+ isDefault = true;
+}
diff --git a/apps/api/v2/src/ee/schedules/inputs/update-schedule.input.ts b/apps/api/v2/src/ee/schedules/inputs/update-schedule.input.ts
new file mode 100644
index 00000000000000..6cc740ceb52988
--- /dev/null
+++ b/apps/api/v2/src/ee/schedules/inputs/update-schedule.input.ts
@@ -0,0 +1,22 @@
+import { CreateAvailabilityInput } from "@/modules/availabilities/inputs/create-availability.input";
+import { Type } from "class-transformer";
+import { IsBoolean, IsOptional, IsString, IsTimeZone, ValidateNested } from "class-validator";
+
+export class UpdateScheduleInput {
+ @IsString()
+ @IsOptional()
+ name?: string;
+
+ @IsTimeZone()
+ @IsOptional()
+ timeZone?: string;
+
+ @ValidateNested({ each: true })
+ @Type(() => CreateAvailabilityInput)
+ @IsOptional()
+ availabilities?: CreateAvailabilityInput[];
+
+ @IsBoolean()
+ @IsOptional()
+ isDefault?: boolean;
+}
diff --git a/apps/api/v2/src/ee/schedules/schedules.module.ts b/apps/api/v2/src/ee/schedules/schedules.module.ts
new file mode 100644
index 00000000000000..5e70f1503a28cb
--- /dev/null
+++ b/apps/api/v2/src/ee/schedules/schedules.module.ts
@@ -0,0 +1,17 @@
+import { SchedulesController } from "@/ee/schedules/controllers/schedules.controller";
+import { SchedulesRepository } from "@/ee/schedules/schedules.repository";
+import { ResponseService } from "@/ee/schedules/services/response/response.service";
+import { SchedulesService } from "@/ee/schedules/services/schedules.service";
+import { AvailabilitiesModule } from "@/modules/availabilities/availabilities.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { UsersModule } from "@/modules/users/users.module";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule, AvailabilitiesModule, UsersModule, TokensModule],
+ providers: [SchedulesRepository, SchedulesService, ResponseService],
+ controllers: [SchedulesController],
+ exports: [SchedulesService],
+})
+export class SchedulesModule {}
diff --git a/apps/api/v2/src/ee/schedules/schedules.repository.ts b/apps/api/v2/src/ee/schedules/schedules.repository.ts
new file mode 100644
index 00000000000000..28a63e49b09635
--- /dev/null
+++ b/apps/api/v2/src/ee/schedules/schedules.repository.ts
@@ -0,0 +1,141 @@
+import { CreateScheduleInput } from "@/ee/schedules/inputs/create-schedule.input";
+import { UpdateScheduleInput } from "@/ee/schedules/inputs/update-schedule.input";
+import { CreateAvailabilityInput } from "@/modules/availabilities/inputs/create-availability.input";
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { BadRequestException, Injectable } from "@nestjs/common";
+import { Prisma } from "@prisma/client";
+
+@Injectable()
+export class SchedulesRepository {
+ constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
+
+ async createScheduleWithAvailabilities(
+ userId: number,
+ schedule: CreateScheduleInput,
+ availabilities: CreateAvailabilityInput[]
+ ) {
+ const createScheduleData: Prisma.ScheduleCreateInput = {
+ user: {
+ connect: {
+ id: userId,
+ },
+ },
+ name: schedule.name,
+ timeZone: schedule.timeZone,
+ };
+
+ if (availabilities.length > 0) {
+ createScheduleData.availability = {
+ createMany: {
+ data: availabilities.map((availability) => {
+ return {
+ days: availability.days,
+ startTime: availability.startTime,
+ endTime: availability.endTime,
+ userId,
+ };
+ }),
+ },
+ };
+ }
+
+ const createdSchedule = await this.dbWrite.prisma.schedule.create({
+ data: {
+ ...createScheduleData,
+ },
+ include: {
+ availability: true,
+ },
+ });
+
+ return createdSchedule;
+ }
+
+ async getScheduleById(scheduleId: number) {
+ const schedule = await this.dbRead.prisma.schedule.findUnique({
+ where: {
+ id: scheduleId,
+ },
+ include: {
+ availability: true,
+ },
+ });
+
+ return schedule;
+ }
+
+ async getSchedulesByUserId(userId: number) {
+ const schedules = await this.dbRead.prisma.schedule.findMany({
+ where: {
+ userId,
+ },
+ include: {
+ availability: true,
+ },
+ });
+
+ return schedules;
+ }
+
+ async updateScheduleWithAvailabilities(scheduleId: number, schedule: UpdateScheduleInput) {
+ const existingSchedule = await this.dbRead.prisma.schedule.findUnique({
+ where: { id: scheduleId },
+ });
+
+ if (!existingSchedule) {
+ throw new BadRequestException(`Schedule with ID=${scheduleId} not found`);
+ }
+
+ const updatedScheduleData: Prisma.ScheduleUpdateInput = {
+ user: {
+ connect: {
+ id: existingSchedule.userId,
+ },
+ },
+ };
+ if (schedule.name) updatedScheduleData.name = schedule.name;
+ if (schedule.timeZone) updatedScheduleData.timeZone = schedule.timeZone;
+
+ if (schedule.availabilities && schedule.availabilities.length > 0) {
+ await this.dbWrite.prisma.availability.deleteMany({
+ where: { scheduleId },
+ });
+
+ updatedScheduleData.availability = {
+ createMany: {
+ data: schedule.availabilities.map((availability) => ({
+ ...availability,
+ userId: existingSchedule.userId,
+ })),
+ },
+ };
+ }
+
+ const updatedSchedule = await this.dbWrite.prisma.schedule.update({
+ where: { id: scheduleId },
+ data: updatedScheduleData,
+ include: {
+ availability: true,
+ },
+ });
+
+ return updatedSchedule;
+ }
+
+ async deleteScheduleById(scheduleId: number) {
+ return this.dbWrite.prisma.schedule.delete({
+ where: {
+ id: scheduleId,
+ },
+ });
+ }
+
+ async getUserSchedulesCount(userId: number) {
+ return this.dbRead.prisma.schedule.count({
+ where: {
+ userId,
+ },
+ });
+ }
+}
diff --git a/apps/api/v2/src/ee/schedules/services/response/response.service.ts b/apps/api/v2/src/ee/schedules/services/response/response.service.ts
new file mode 100644
index 00000000000000..c070314e5521e2
--- /dev/null
+++ b/apps/api/v2/src/ee/schedules/services/response/response.service.ts
@@ -0,0 +1,68 @@
+import { SchedulesRepository } from "@/ee/schedules/schedules.repository";
+import { Injectable } from "@nestjs/common";
+import { User } from "@prisma/client";
+
+import {
+ transformWorkingHoursForClient,
+ transformAvailabilityForClient,
+ transformDateOverridesForClient,
+} from "@calcom/platform-libraries";
+import type {
+ ScheduleWithAvailabilities,
+ ScheduleWithAvailabilitiesForWeb,
+} from "@calcom/platform-libraries";
+import { ScheduleResponse, schemaScheduleResponse } from "@calcom/platform-types";
+
+@Injectable()
+export class ResponseService {
+ constructor(private readonly schedulesRepository: SchedulesRepository) {}
+
+ async formatSchedule(
+ forAtom: boolean,
+ user: User,
+ schedule: ScheduleWithAvailabilities
+ ): Promise {
+ if (forAtom) {
+ const usersSchedulesCount = await this.schedulesRepository.getUserSchedulesCount(user.id);
+ return this.transformScheduleForAtom(schedule, usersSchedulesCount, user);
+ }
+
+ return schemaScheduleResponse.parse(schedule);
+ }
+
+ async formatSchedules(
+ forAtom: boolean,
+ user: User,
+ schedules: ScheduleWithAvailabilities[]
+ ): Promise {
+ if (forAtom) {
+ const usersSchedulesCount = await this.schedulesRepository.getUserSchedulesCount(user.id);
+ return Promise.all(
+ schedules.map((schedule) => this.transformScheduleForAtom(schedule, usersSchedulesCount, user))
+ );
+ }
+ return schedules.map((schedule) => schemaScheduleResponse.parse(schedule));
+ }
+
+ async transformScheduleForAtom(
+ schedule: ScheduleWithAvailabilities,
+ userSchedulesCount: number,
+ user: Pick
+ ): Promise {
+ const timeZone = schedule.timeZone || user.timeZone;
+
+ return {
+ id: schedule.id,
+ name: schedule.name,
+ isManaged: schedule.userId !== user.id,
+ workingHours: transformWorkingHoursForClient(schedule),
+ schedule: schedule.availability,
+ availability: transformAvailabilityForClient(schedule),
+ timeZone,
+ dateOverrides: transformDateOverridesForClient(schedule, timeZone),
+ isDefault: user.defaultScheduleId === schedule.id,
+ isLastSchedule: userSchedulesCount <= 1,
+ readOnly: schedule.userId !== user.id,
+ };
+ }
+}
diff --git a/apps/api/v2/src/ee/schedules/services/schedules.service.ts b/apps/api/v2/src/ee/schedules/services/schedules.service.ts
new file mode 100644
index 00000000000000..68e787f6f0ee16
--- /dev/null
+++ b/apps/api/v2/src/ee/schedules/services/schedules.service.ts
@@ -0,0 +1,103 @@
+import { CreateScheduleInput } from "@/ee/schedules/inputs/create-schedule.input";
+import { UpdateScheduleInput } from "@/ee/schedules/inputs/update-schedule.input";
+import { SchedulesRepository } from "@/ee/schedules/schedules.repository";
+import { AvailabilitiesService } from "@/modules/availabilities/availabilities.service";
+import { UsersRepository } from "@/modules/users/users.repository";
+import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common";
+import { Schedule } from "@prisma/client";
+
+import { cityTimezonesHandler } from "@calcom/platform-libraries";
+
+@Injectable()
+export class SchedulesService {
+ constructor(
+ private readonly schedulesRepository: SchedulesRepository,
+ private readonly availabilitiesService: AvailabilitiesService,
+ private readonly usersRepository: UsersRepository
+ ) {}
+
+ async createUserSchedule(userId: number, schedule: CreateScheduleInput) {
+ const availabilities = schedule.availabilities?.length
+ ? schedule.availabilities
+ : [this.availabilitiesService.getDefaultAvailabilityInput()];
+
+ const createdSchedule = await this.schedulesRepository.createScheduleWithAvailabilities(
+ userId,
+ schedule,
+ availabilities
+ );
+
+ if (schedule.isDefault) {
+ await this.usersRepository.setDefaultSchedule(userId, createdSchedule.id);
+ }
+
+ return createdSchedule;
+ }
+
+ async getUserScheduleDefault(userId: number) {
+ const user = await this.usersRepository.findById(userId);
+
+ if (!user?.defaultScheduleId) return null;
+
+ return this.schedulesRepository.getScheduleById(user.defaultScheduleId);
+ }
+
+ async getUserSchedule(userId: number, scheduleId: number) {
+ const existingSchedule = await this.schedulesRepository.getScheduleById(scheduleId);
+
+ if (!existingSchedule) {
+ throw new NotFoundException(`Schedule with ID=${scheduleId} does not exist.`);
+ }
+
+ this.checkUserOwnsSchedule(userId, existingSchedule);
+
+ return existingSchedule;
+ }
+
+ async getUserSchedules(userId: number) {
+ return this.schedulesRepository.getSchedulesByUserId(userId);
+ }
+
+ async updateUserSchedule(userId: number, scheduleId: number, schedule: UpdateScheduleInput) {
+ const existingSchedule = await this.schedulesRepository.getScheduleById(scheduleId);
+
+ if (!existingSchedule) {
+ throw new NotFoundException(`Schedule with ID=${scheduleId} does not exist.`);
+ }
+
+ this.checkUserOwnsSchedule(userId, existingSchedule);
+
+ const updatedSchedule = await this.schedulesRepository.updateScheduleWithAvailabilities(
+ scheduleId,
+ schedule
+ );
+
+ if (schedule.isDefault) {
+ await this.usersRepository.setDefaultSchedule(userId, updatedSchedule.id);
+ }
+
+ return updatedSchedule;
+ }
+
+ async deleteUserSchedule(userId: number, scheduleId: number) {
+ const existingSchedule = await this.schedulesRepository.getScheduleById(scheduleId);
+
+ if (!existingSchedule) {
+ throw new BadRequestException(`Schedule with ID=${scheduleId} does not exist.`);
+ }
+
+ this.checkUserOwnsSchedule(userId, existingSchedule);
+
+ return this.schedulesRepository.deleteScheduleById(scheduleId);
+ }
+
+ checkUserOwnsSchedule(userId: number, schedule: Pick) {
+ if (userId !== schedule.userId) {
+ throw new ForbiddenException(`User with ID=${userId} does not own schedule with ID=${schedule.id}`);
+ }
+ }
+
+ async getSchedulePossibleTimeZones() {
+ return cityTimezonesHandler();
+ }
+}
diff --git a/apps/api/v2/src/env.ts b/apps/api/v2/src/env.ts
new file mode 100644
index 00000000000000..97d51d381338c4
--- /dev/null
+++ b/apps/api/v2/src/env.ts
@@ -0,0 +1,28 @@
+import { logLevels } from "@/lib/logger";
+
+export type Environment = {
+ NODE_ENV: "development" | "production";
+ API_PORT: string;
+ API_URL: string;
+ DATABASE_READ_URL: string;
+ DATABASE_WRITE_URL: string;
+ NEXTAUTH_SECRET: string;
+ DATABASE_URL: string;
+ JWT_SECRET: string;
+ SENTRY_DNS: string;
+ LOG_LEVEL: keyof typeof logLevels;
+ REDIS_URL: string;
+};
+
+export const getEnv = (key: K, fallback?: Environment[K]): Environment[K] => {
+ const value = process.env[key] as Environment[K] | undefined;
+
+ if (typeof value === "undefined") {
+ if (typeof fallback !== "undefined") {
+ return fallback;
+ }
+ throw new Error(`Missing environment variable: ${key}.`);
+ }
+
+ return value;
+};
diff --git a/apps/api/v2/src/filters/http-exception.filter.ts b/apps/api/v2/src/filters/http-exception.filter.ts
new file mode 100644
index 00000000000000..3cd808a8e29040
--- /dev/null
+++ b/apps/api/v2/src/filters/http-exception.filter.ts
@@ -0,0 +1,25 @@
+import { ArgumentsHost, Catch, ExceptionFilter, HttpException, Logger } from "@nestjs/common";
+
+import { ERROR_STATUS } from "@calcom/platform-constants";
+import { Response } from "@calcom/platform-types";
+
+@Catch(HttpException)
+export class HttpExceptionFilter implements ExceptionFilter {
+ private readonly logger = new Logger("HttpExceptionFilter");
+
+ catch(exception: HttpException, host: ArgumentsHost) {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+ const statusCode = exception.getStatus();
+ this.logger.error(`Http Exception Filter: ${exception?.message}`, {
+ exception,
+ });
+ response.status(statusCode).json({
+ status: ERROR_STATUS,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ error: { code: exception.name, message: exception.message },
+ });
+ }
+}
diff --git a/apps/api/v2/src/filters/prisma-exception.filter.ts b/apps/api/v2/src/filters/prisma-exception.filter.ts
new file mode 100644
index 00000000000000..bd59b1a9ddf4ab
--- /dev/null
+++ b/apps/api/v2/src/filters/prisma-exception.filter.ts
@@ -0,0 +1,45 @@
+import type { ArgumentsHost, ExceptionFilter } from "@nestjs/common";
+import { Catch, HttpStatus, Logger } from "@nestjs/common";
+import {
+ PrismaClientInitializationError,
+ PrismaClientKnownRequestError,
+ PrismaClientRustPanicError,
+ PrismaClientUnknownRequestError,
+ PrismaClientValidationError,
+} from "@prisma/client/runtime/library";
+
+import { ERROR_STATUS, INTERNAL_SERVER_ERROR } from "@calcom/platform-constants";
+import { Response } from "@calcom/platform-types";
+
+type PrismaError =
+ | PrismaClientInitializationError
+ | PrismaClientKnownRequestError
+ | PrismaClientRustPanicError
+ | PrismaClientUnknownRequestError
+ | PrismaClientValidationError;
+
+@Catch(
+ PrismaClientInitializationError,
+ PrismaClientKnownRequestError,
+ PrismaClientRustPanicError,
+ PrismaClientUnknownRequestError,
+ PrismaClientValidationError
+)
+export class PrismaExceptionFilter implements ExceptionFilter {
+ private readonly logger = new Logger("PrismaExceptionFilter");
+
+ catch(error: PrismaError, host: ArgumentsHost) {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+ this.logger.error(`PrismaError: ${error.message}`, {
+ error,
+ });
+ response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
+ status: ERROR_STATUS,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ error: { code: INTERNAL_SERVER_ERROR, message: "There was an error, please try again later." },
+ });
+ }
+}
diff --git a/apps/api/v2/src/filters/sentry-exception.filter.ts b/apps/api/v2/src/filters/sentry-exception.filter.ts
new file mode 100644
index 00000000000000..5cc106a5b77756
--- /dev/null
+++ b/apps/api/v2/src/filters/sentry-exception.filter.ts
@@ -0,0 +1,32 @@
+import { ArgumentsHost, Catch, Logger, HttpStatus } from "@nestjs/common";
+import { BaseExceptionFilter } from "@nestjs/core";
+import * as Sentry from "@sentry/node";
+
+import { ERROR_STATUS, INTERNAL_SERVER_ERROR } from "@calcom/platform-constants";
+import { Response } from "@calcom/platform-types";
+
+@Catch()
+export class SentryFilter extends BaseExceptionFilter {
+ private readonly logger = new Logger("SentryExceptionFilter");
+
+ handleUnknownError(exception: any, host: ArgumentsHost): void {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+
+ this.logger.error(`Sentry Exception Filter: ${exception?.message}`, {
+ exception,
+ });
+
+ // capture if client has been init
+ if (Boolean(Sentry.getCurrentHub().getClient())) {
+ Sentry.captureException(exception);
+ }
+ response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
+ status: ERROR_STATUS,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ error: { code: INTERNAL_SERVER_ERROR, message: "Internal server error." },
+ });
+ }
+}
diff --git a/apps/api/v2/src/filters/trpc-exception.filter.ts b/apps/api/v2/src/filters/trpc-exception.filter.ts
new file mode 100644
index 00000000000000..7316f49c8cb673
--- /dev/null
+++ b/apps/api/v2/src/filters/trpc-exception.filter.ts
@@ -0,0 +1,51 @@
+import { ArgumentsHost, Catch, ExceptionFilter, Logger } from "@nestjs/common";
+
+import { ERROR_STATUS } from "@calcom/platform-constants";
+import { TRPCError } from "@calcom/platform-libraries";
+import { Response } from "@calcom/platform-types";
+
+@Catch(TRPCError)
+export class TRPCExceptionFilter implements ExceptionFilter {
+ private readonly logger = new Logger("TRPCExceptionFilter");
+
+ catch(exception: TRPCError, host: ArgumentsHost) {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+ let statusCode = 500;
+ switch (exception.code) {
+ case "UNAUTHORIZED":
+ statusCode = 401;
+ break;
+ case "FORBIDDEN":
+ statusCode = 403;
+ break;
+ case "NOT_FOUND":
+ statusCode = 404;
+ break;
+ case "INTERNAL_SERVER_ERROR":
+ statusCode = 500;
+ break;
+ case "BAD_REQUEST":
+ statusCode = 400;
+ break;
+ case "CONFLICT":
+ statusCode = 409;
+ break;
+ case "TOO_MANY_REQUESTS":
+ statusCode = 429;
+ default:
+ statusCode = 500;
+ break;
+ }
+ this.logger.error(`TRPC Exception Filter: ${exception?.message}`, {
+ exception,
+ });
+ response.status(statusCode).json({
+ status: ERROR_STATUS,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ error: { code: exception.name, message: exception.message },
+ });
+ }
+}
diff --git a/apps/api/v2/src/filters/zod-exception.filter.ts b/apps/api/v2/src/filters/zod-exception.filter.ts
new file mode 100644
index 00000000000000..408ba4aea5a124
--- /dev/null
+++ b/apps/api/v2/src/filters/zod-exception.filter.ts
@@ -0,0 +1,32 @@
+import type { ArgumentsHost, ExceptionFilter } from "@nestjs/common";
+import { Catch, HttpStatus, Logger } from "@nestjs/common";
+import { ZodError } from "zod";
+
+import { BAD_REQUEST, ERROR_STATUS } from "@calcom/platform-constants";
+import { Response } from "@calcom/platform-types";
+
+@Catch(ZodError)
+export class ZodExceptionFilter implements ExceptionFilter {
+ private readonly logger = new Logger("ZodExceptionFilter");
+
+ catch(error: ZodError, host: ArgumentsHost) {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+ this.logger.error(`ZodError: ${error.message}`, {
+ error,
+ });
+ response.status(HttpStatus.BAD_REQUEST).json({
+ status: ERROR_STATUS,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ error: {
+ code: BAD_REQUEST,
+ message: error.issues.reduce(
+ (acc, issue) => `${issue.path.join(".")} - ${issue.message}, ${acc}`,
+ ""
+ ),
+ },
+ });
+ }
+}
diff --git a/apps/api/v2/src/lib/api-key/index.ts b/apps/api/v2/src/lib/api-key/index.ts
new file mode 100644
index 00000000000000..d292bd06e8bdce
--- /dev/null
+++ b/apps/api/v2/src/lib/api-key/index.ts
@@ -0,0 +1,3 @@
+import { createHash } from "crypto";
+
+export const hashAPIKey = (apiKey: string): string => createHash("sha256").update(apiKey).digest("hex");
diff --git a/apps/api/v2/src/lib/atoms/decorators/for-atom.decorator.ts b/apps/api/v2/src/lib/atoms/decorators/for-atom.decorator.ts
new file mode 100644
index 00000000000000..d07bff7f35d324
--- /dev/null
+++ b/apps/api/v2/src/lib/atoms/decorators/for-atom.decorator.ts
@@ -0,0 +1,6 @@
+import { createParamDecorator, ExecutionContext } from "@nestjs/common";
+
+export const ForAtom = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
+ const request = ctx.switchToHttp().getRequest();
+ return request.query.for === "atom";
+});
diff --git a/apps/api/v2/src/lib/logger.ts b/apps/api/v2/src/lib/logger.ts
new file mode 100644
index 00000000000000..43378b13bc076e
--- /dev/null
+++ b/apps/api/v2/src/lib/logger.ts
@@ -0,0 +1,50 @@
+import type { LoggerOptions } from "winston";
+import { format, transports } from "winston";
+
+const formattedTimestamp = format.timestamp({
+ format: "YYYY-MM-DD HH:mm:ss.SSS",
+});
+
+const colorizer = format.colorize({
+ colors: {
+ fatal: "red",
+ error: "red",
+ warn: "yellow",
+ info: "blue",
+ debug: "white",
+ trace: "grey",
+ },
+});
+
+const WINSTON_DEV_FORMAT = format.combine(
+ format.errors({ stack: true }),
+ colorizer,
+ formattedTimestamp,
+ format.simple()
+);
+const WINSTON_PROD_FORMAT = format.combine(format.errors({ stack: true }), formattedTimestamp, format.json());
+
+export const logLevels = {
+ fatal: 0,
+ error: 1,
+ warn: 2,
+ info: 3,
+ debug: 4,
+ trace: 5,
+} as const;
+
+export const loggerConfig = (): LoggerOptions => {
+ const isProduction = process.env.NODE_ENV === "production";
+
+ return {
+ levels: logLevels,
+ level: process.env.LOG_LEVEL ?? "info",
+ format: isProduction ? WINSTON_PROD_FORMAT : WINSTON_DEV_FORMAT,
+ transports: [new transports.Console()],
+ exceptionHandlers: [new transports.Console()],
+ rejectionHandlers: [new transports.Console()],
+ defaultMeta: {
+ service: "cal-platform-api",
+ },
+ };
+};
diff --git a/apps/api/v2/src/lib/passport/strategies/types.ts b/apps/api/v2/src/lib/passport/strategies/types.ts
new file mode 100644
index 00000000000000..4f9667397e899c
--- /dev/null
+++ b/apps/api/v2/src/lib/passport/strategies/types.ts
@@ -0,0 +1,4 @@
+export class BaseStrategy {
+ success!: (user: unknown) => void;
+ error!: (error: Error) => void;
+}
diff --git a/apps/api/v2/src/lib/response/response.dto.ts b/apps/api/v2/src/lib/response/response.dto.ts
new file mode 100644
index 00000000000000..e4ec569e6318f3
--- /dev/null
+++ b/apps/api/v2/src/lib/response/response.dto.ts
@@ -0,0 +1,29 @@
+import { ApiProperty } from "@nestjs/swagger";
+import { IsNotEmpty, IsString } from "class-validator";
+
+export class BaseApiResponseDto {
+ @ApiProperty({ example: "success" })
+ @IsString()
+ @IsNotEmpty()
+ status: string;
+
+ @ApiProperty({
+ description: "The payload of the response, which can be any type of data.",
+ })
+ data: T;
+
+ constructor(status: string, data: T) {
+ this.status = status;
+ this.data = data;
+ }
+}
+
+export class OAuthClientDto {
+ @ApiProperty({ example: "abc123" })
+ @IsString()
+ clientId!: string;
+
+ @ApiProperty({ example: "secretKey123" })
+ @IsString()
+ clientSecret!: string;
+}
diff --git a/apps/api/v2/src/main.ts b/apps/api/v2/src/main.ts
new file mode 100644
index 00000000000000..2226c477b5eb5f
--- /dev/null
+++ b/apps/api/v2/src/main.ts
@@ -0,0 +1,59 @@
+import type { AppConfig } from "@/config/type";
+import { Logger } from "@nestjs/common";
+import { ConfigService } from "@nestjs/config";
+import { NestFactory } from "@nestjs/core";
+import type { NestExpressApplication } from "@nestjs/platform-express";
+import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
+import "dotenv/config";
+import * as fs from "fs";
+import { Server } from "http";
+import { WinstonModule } from "nest-winston";
+
+import { bootstrap } from "./app";
+import { AppModule } from "./app.module";
+import { loggerConfig } from "./lib/logger";
+
+const run = async () => {
+ const app = await NestFactory.create(AppModule, {
+ logger: WinstonModule.createLogger(loggerConfig()),
+ });
+
+ const logger = new Logger("App");
+
+ try {
+ bootstrap(app);
+ const port = app.get(ConfigService).get("api.port", { infer: true });
+ generateSwagger(app);
+ await app.listen(port);
+ logger.log(`Application started on port: ${port}`);
+ } catch (error) {
+ logger.error("Application crashed", {
+ error,
+ });
+ }
+};
+
+async function generateSwagger(app: NestExpressApplication) {
+ const logger = new Logger("App");
+ logger.log(`Generating Swagger documentation...\n`);
+
+ const config = new DocumentBuilder().setTitle("Cal.com v2 API").build();
+
+ const document = SwaggerModule.createDocument(app, config);
+
+ const outputFile = "./swagger/documentation.json";
+
+ if (fs.existsSync(outputFile)) {
+ fs.unlinkSync(outputFile);
+ }
+
+ fs.writeFileSync(outputFile, JSON.stringify(document, null, 2), { encoding: "utf8" });
+ SwaggerModule.setup("docs", app, document);
+
+ logger.log(`Swagger documentation available in the "/docs" endpoint\n`);
+}
+
+run().catch((error: Error) => {
+ console.error("Failed to start Cal Platform API", { error: error.stack });
+ process.exit(1);
+});
diff --git a/apps/api/v2/src/modules/api-key/api-key.module.ts b/apps/api/v2/src/modules/api-key/api-key.module.ts
new file mode 100644
index 00000000000000..6c3d86ba058c7c
--- /dev/null
+++ b/apps/api/v2/src/modules/api-key/api-key.module.ts
@@ -0,0 +1,10 @@
+import { ApiKeyService } from "@/modules/api-key/api-key.service";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule],
+ providers: [ApiKeyService],
+ exports: [ApiKeyService],
+})
+export class ApiKeyModule {}
diff --git a/apps/api/v2/src/modules/api-key/api-key.service.ts b/apps/api/v2/src/modules/api-key/api-key.service.ts
new file mode 100644
index 00000000000000..c381fa99be0dc6
--- /dev/null
+++ b/apps/api/v2/src/modules/api-key/api-key.service.ts
@@ -0,0 +1,25 @@
+import { hashAPIKey } from "@/lib/api-key";
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { Injectable } from "@nestjs/common";
+import type { Request } from "express";
+
+@Injectable()
+export class ApiKeyService {
+ constructor(private readonly dbRead: PrismaReadService) {}
+
+ async retrieveApiKey(request: Request) {
+ const apiKey = request.get("Authorization")?.replace("Bearer ", "");
+
+ if (!apiKey) {
+ return null;
+ }
+
+ const hashedKey = hashAPIKey(apiKey.replace("cal_", ""));
+
+ return this.dbRead.prisma.apiKey.findUniqueOrThrow({
+ where: {
+ hashedKey,
+ },
+ });
+ }
+}
diff --git a/apps/api/v2/src/modules/apps/apps.module.ts b/apps/api/v2/src/modules/apps/apps.module.ts
new file mode 100644
index 00000000000000..587b18ce8d91cf
--- /dev/null
+++ b/apps/api/v2/src/modules/apps/apps.module.ts
@@ -0,0 +1,14 @@
+import { AppsRepository } from "@/modules/apps/apps.repository";
+import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { Module } from "@nestjs/common";
+import { ConfigService } from "@nestjs/config";
+
+@Module({
+ imports: [PrismaModule, TokensModule],
+ providers: [AppsRepository, ConfigService, CredentialsRepository, SelectedCalendarsRepository],
+ exports: [],
+})
+export class AppsModule {}
diff --git a/apps/api/v2/src/modules/apps/apps.repository.ts b/apps/api/v2/src/modules/apps/apps.repository.ts
new file mode 100644
index 00000000000000..c2a74e1421d304
--- /dev/null
+++ b/apps/api/v2/src/modules/apps/apps.repository.ts
@@ -0,0 +1,13 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { Injectable } from "@nestjs/common";
+import { App } from "@prisma/client";
+
+@Injectable()
+export class AppsRepository {
+ constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
+
+ async getAppBySlug(slug: string): Promise {
+ return await this.dbRead.prisma.app.findUnique({ where: { slug } });
+ }
+}
diff --git a/apps/api/v2/src/modules/apps/services/gcal.service.ts b/apps/api/v2/src/modules/apps/services/gcal.service.ts
new file mode 100644
index 00000000000000..45b3406363091c
--- /dev/null
+++ b/apps/api/v2/src/modules/apps/services/gcal.service.ts
@@ -0,0 +1,27 @@
+import { AppsRepository } from "@/modules/apps/apps.repository";
+import { Injectable, Logger, NotFoundException } from "@nestjs/common";
+import { google } from "googleapis";
+import { z } from "zod";
+
+@Injectable()
+export class GcalService {
+ private logger = new Logger("GcalService");
+
+ constructor(private readonly appsRepository: AppsRepository) {}
+
+ async getOAuthClient(redirectUri: string) {
+ this.logger.log("Getting Google Calendar OAuth Client");
+ const app = await this.appsRepository.getAppBySlug("google-calendar");
+
+ if (!app) {
+ throw new NotFoundException();
+ }
+
+ const { client_id, client_secret } = z
+ .object({ client_id: z.string(), client_secret: z.string() })
+ .parse(app.keys);
+
+ const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirectUri);
+ return oAuth2Client;
+ }
+}
diff --git a/apps/api/v2/src/modules/auth/auth.module.ts b/apps/api/v2/src/modules/auth/auth.module.ts
new file mode 100644
index 00000000000000..aae58abb100e8a
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/auth.module.ts
@@ -0,0 +1,26 @@
+import { ApiKeyModule } from "@/modules/api-key/api-key.module";
+import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
+import { NextAuthGuard } from "@/modules/auth/guards/next-auth/next-auth.guard";
+import { AccessTokenStrategy } from "@/modules/auth/strategies/access-token/access-token.strategy";
+import { ApiKeyAuthStrategy } from "@/modules/auth/strategies/api-key-auth/api-key-auth.strategy";
+import { NextAuthStrategy } from "@/modules/auth/strategies/next-auth/next-auth.strategy";
+import { MembershipsModule } from "@/modules/memberships/memberships.module";
+import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
+import { TokensModule } from "@/modules/tokens/tokens.module";
+import { UsersModule } from "@/modules/users/users.module";
+import { Module } from "@nestjs/common";
+import { PassportModule } from "@nestjs/passport";
+
+@Module({
+ imports: [PassportModule, ApiKeyModule, UsersModule, MembershipsModule, TokensModule],
+ providers: [
+ ApiKeyAuthStrategy,
+ NextAuthGuard,
+ NextAuthStrategy,
+ AccessTokenGuard,
+ AccessTokenStrategy,
+ OAuthFlowService,
+ ],
+ exports: [NextAuthGuard, AccessTokenGuard],
+})
+export class AuthModule {}
diff --git a/apps/api/v2/src/modules/auth/decorators/get-user/get-user.decorator.ts b/apps/api/v2/src/modules/auth/decorators/get-user/get-user.decorator.ts
new file mode 100644
index 00000000000000..28ec9d5e3a34ed
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/decorators/get-user/get-user.decorator.ts
@@ -0,0 +1,27 @@
+import { ExecutionContext } from "@nestjs/common";
+import { createParamDecorator } from "@nestjs/common";
+import { User } from "@prisma/client";
+
+export const GetUser = createParamDecorator((data, ctx) => {
+ const request = ctx.switchToHttp().getRequest();
+ const user = request.user as User;
+
+ if (!user) {
+ throw new Error("GetUser decorator : User not found");
+ }
+
+ if (Array.isArray(data)) {
+ return data.reduce((prev, curr) => {
+ return {
+ ...prev,
+ [curr]: request.user[curr],
+ };
+ }, {});
+ }
+
+ if (data) {
+ return request.user[data];
+ }
+
+ return user;
+});
diff --git a/apps/api/v2/src/modules/auth/decorators/permissions/permissions.decorator.ts b/apps/api/v2/src/modules/auth/decorators/permissions/permissions.decorator.ts
new file mode 100644
index 00000000000000..425a3006daa6ea
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/decorators/permissions/permissions.decorator.ts
@@ -0,0 +1,5 @@
+import { Reflector } from "@nestjs/core";
+
+import { PERMISSIONS } from "@calcom/platform-constants";
+
+export const Permissions = Reflector.createDecorator<(typeof PERMISSIONS)[number][]>();
diff --git a/apps/api/v2/src/modules/auth/decorators/roles/roles.decorator.ts b/apps/api/v2/src/modules/auth/decorators/roles/roles.decorator.ts
new file mode 100644
index 00000000000000..d1a4511770d1f0
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/decorators/roles/roles.decorator.ts
@@ -0,0 +1,4 @@
+import { Reflector } from "@nestjs/core";
+import { MembershipRole } from "@prisma/client";
+
+export const Roles = Reflector.createDecorator();
diff --git a/apps/api/v2/src/modules/auth/guards/access-token/access-token.guard.ts b/apps/api/v2/src/modules/auth/guards/access-token/access-token.guard.ts
new file mode 100644
index 00000000000000..2543c644549ae1
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/guards/access-token/access-token.guard.ts
@@ -0,0 +1,7 @@
+import { AuthGuard } from "@nestjs/passport";
+
+export class AccessTokenGuard extends AuthGuard("access-token") {
+ constructor() {
+ super();
+ }
+}
diff --git a/apps/api/v2/src/modules/auth/guards/access-token/token-expired.exception.ts b/apps/api/v2/src/modules/auth/guards/access-token/token-expired.exception.ts
new file mode 100644
index 00000000000000..1e3f4eea837ec1
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/guards/access-token/token-expired.exception.ts
@@ -0,0 +1,9 @@
+import { HttpException } from "@nestjs/common";
+
+import { ACCESS_TOKEN_EXPIRED, HTTP_CODE_TOKEN_EXPIRED } from "@calcom/platform-constants";
+
+export class TokenExpiredException extends HttpException {
+ constructor() {
+ super(ACCESS_TOKEN_EXPIRED, HTTP_CODE_TOKEN_EXPIRED);
+ }
+}
diff --git a/apps/api/v2/src/modules/auth/guards/next-auth/next-auth.guard.ts b/apps/api/v2/src/modules/auth/guards/next-auth/next-auth.guard.ts
new file mode 100644
index 00000000000000..a2597709da7e8d
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/guards/next-auth/next-auth.guard.ts
@@ -0,0 +1,7 @@
+import { AuthGuard } from "@nestjs/passport";
+
+export class NextAuthGuard extends AuthGuard("next-auth") {
+ constructor() {
+ super();
+ }
+}
diff --git a/apps/api/v2/src/modules/auth/guards/organization-roles/organization-roles.guard.ts b/apps/api/v2/src/modules/auth/guards/organization-roles/organization-roles.guard.ts
new file mode 100644
index 00000000000000..1063cec335489f
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/guards/organization-roles/organization-roles.guard.ts
@@ -0,0 +1,32 @@
+import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
+import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
+import { UserWithProfile } from "@/modules/users/users.repository";
+import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
+import { Reflector } from "@nestjs/core";
+
+@Injectable()
+export class OrganizationRolesGuard implements CanActivate {
+ constructor(private reflector: Reflector, private membershipRepository: MembershipsRepository) {}
+
+ async canActivate(context: ExecutionContext): Promise {
+ const requiredRoles = this.reflector.get(Roles, context.getHandler());
+
+ if (!requiredRoles?.length || !Object.keys(requiredRoles)?.length) {
+ return true;
+ }
+
+ const request = context.switchToHttp().getRequest();
+ const user: UserWithProfile = request.user;
+ const organizationId = user?.movedToProfile?.organizationId || user?.organizationId;
+
+ if (!user || !organizationId) {
+ return false;
+ }
+
+ const membership = await this.membershipRepository.findOrgUserMembership(organizationId, user.id);
+ const isAccepted = membership.accepted;
+ const hasRequiredRole = requiredRoles.includes(membership.role);
+
+ return isAccepted && hasRequiredRole;
+ }
+}
diff --git a/apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.spec.ts b/apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.spec.ts
new file mode 100644
index 00000000000000..0bd07651deeb6f
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.spec.ts
@@ -0,0 +1,99 @@
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import { createMock } from "@golevelup/ts-jest";
+import { ExecutionContext } from "@nestjs/common";
+import { Reflector } from "@nestjs/core";
+
+import { APPS_WRITE, SCHEDULE_READ, SCHEDULE_WRITE } from "@calcom/platform-constants";
+
+import { PermissionsGuard } from "./permissions.guard";
+
+describe("PermissionsGuard", () => {
+ let guard: PermissionsGuard;
+ let reflector: Reflector;
+
+ beforeEach(async () => {
+ reflector = new Reflector();
+ guard = new PermissionsGuard(reflector, createMock());
+ });
+
+ it("should be defined", () => {
+ expect(guard).toBeDefined();
+ });
+
+ describe("when access token is missing", () => {
+ it("should return false", async () => {
+ const mockContext = createMockExecutionContext({});
+ jest.spyOn(reflector, "get").mockReturnValue([SCHEDULE_WRITE]);
+ jest.spyOn(guard, "getOAuthClientPermissions").mockResolvedValue(0);
+
+ await expect(guard.canActivate(mockContext)).resolves.toBe(false);
+ });
+ });
+
+ describe("when access token is provided", () => {
+ it("should return true for valid permissions", async () => {
+ const mockContext = createMockExecutionContext({ Authorization: "Bearer token" });
+ jest.spyOn(reflector, "get").mockReturnValue([SCHEDULE_WRITE]);
+
+ let oAuthClientPermissions = 0;
+ oAuthClientPermissions |= SCHEDULE_WRITE;
+ jest.spyOn(guard, "getOAuthClientPermissions").mockResolvedValue(oAuthClientPermissions);
+ await expect(guard.canActivate(mockContext)).resolves.toBe(true);
+ });
+
+ it("should return true for multiple valid permissions", async () => {
+ const mockContext = createMockExecutionContext({ Authorization: "Bearer token" });
+ jest.spyOn(reflector, "get").mockReturnValue([SCHEDULE_WRITE, SCHEDULE_READ]);
+
+ let oAuthClientPermissions = 0;
+ oAuthClientPermissions |= SCHEDULE_WRITE;
+ oAuthClientPermissions |= SCHEDULE_READ;
+ jest.spyOn(guard, "getOAuthClientPermissions").mockResolvedValue(oAuthClientPermissions);
+
+ await expect(guard.canActivate(mockContext)).resolves.toBe(true);
+ });
+
+ it("should return true for empty Permissions decorator", async () => {
+ const mockContext = createMockExecutionContext({ Authorization: "Bearer token" });
+ jest.spyOn(reflector, "get").mockReturnValue([]);
+
+ let oAuthClientPermissions = 0;
+ oAuthClientPermissions |= SCHEDULE_WRITE;
+ jest.spyOn(guard, "getOAuthClientPermissions").mockResolvedValue(oAuthClientPermissions);
+ await expect(guard.canActivate(mockContext)).resolves.toBe(true);
+ });
+
+ it("should return false for invalid permissions", async () => {
+ const mockContext = createMockExecutionContext({ Authorization: "Bearer token" });
+ jest.spyOn(reflector, "get").mockReturnValue([SCHEDULE_WRITE]);
+
+ let oAuthClientPermissions = 0;
+ oAuthClientPermissions |= APPS_WRITE;
+ jest.spyOn(guard, "getOAuthClientPermissions").mockResolvedValue(oAuthClientPermissions);
+
+ await expect(guard.canActivate(mockContext)).resolves.toBe(false);
+ });
+
+ it("should return false for a missing permission", async () => {
+ const mockContext = createMockExecutionContext({ Authorization: "Bearer token" });
+ jest.spyOn(reflector, "get").mockReturnValue([SCHEDULE_WRITE, SCHEDULE_READ]);
+
+ let oAuthClientPermissions = 0;
+ oAuthClientPermissions |= SCHEDULE_WRITE;
+ jest.spyOn(guard, "getOAuthClientPermissions").mockResolvedValue(oAuthClientPermissions);
+
+ await expect(guard.canActivate(mockContext)).resolves.toBe(false);
+ });
+ });
+
+ function createMockExecutionContext(headers: Record): ExecutionContext {
+ return createMock({
+ switchToHttp: () => ({
+ getRequest: () => ({
+ headers,
+ get: (headerName: string) => headers[headerName],
+ }),
+ }),
+ });
+ }
+});
diff --git a/apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.ts b/apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.ts
new file mode 100644
index 00000000000000..4fec7f87009d03
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.ts
@@ -0,0 +1,39 @@
+import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
+import { Reflector } from "@nestjs/core";
+
+import { hasPermissions } from "@calcom/platform-utils";
+
+@Injectable()
+export class PermissionsGuard implements CanActivate {
+ constructor(private reflector: Reflector, private tokensRepository: TokensRepository) {}
+
+ async canActivate(context: ExecutionContext): Promise {
+ const requiredPermissions = this.reflector.get(Permissions, context.getHandler());
+
+ if (!requiredPermissions?.length || !Object.keys(requiredPermissions)?.length) {
+ return true;
+ }
+
+ const request = context.switchToHttp().getRequest();
+ const accessToken = request.get("Authorization")?.replace("Bearer ", "");
+
+ if (!accessToken) {
+ return false;
+ }
+
+ const oAuthClientPermissions = await this.getOAuthClientPermissions(accessToken);
+
+ if (!oAuthClientPermissions) {
+ return false;
+ }
+
+ return hasPermissions(oAuthClientPermissions, [...requiredPermissions]);
+ }
+
+ async getOAuthClientPermissions(accessToken: string) {
+ const oAuthClient = await this.tokensRepository.getAccessTokenClient(accessToken);
+ return oAuthClient?.permissions;
+ }
+}
diff --git a/apps/api/v2/src/modules/auth/strategies/access-token/access-token.strategy.ts b/apps/api/v2/src/modules/auth/strategies/access-token/access-token.strategy.ts
new file mode 100644
index 00000000000000..53e055d2d29fc3
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/strategies/access-token/access-token.strategy.ts
@@ -0,0 +1,58 @@
+import { BaseStrategy } from "@/lib/passport/strategies/types";
+import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import { UserWithProfile, UsersRepository } from "@/modules/users/users.repository";
+import { Injectable, UnauthorizedException } from "@nestjs/common";
+import { PassportStrategy } from "@nestjs/passport";
+import type { Request } from "express";
+
+import { INVALID_ACCESS_TOKEN } from "@calcom/platform-constants";
+
+@Injectable()
+export class AccessTokenStrategy extends PassportStrategy(BaseStrategy, "access-token") {
+ constructor(
+ private readonly oauthFlowService: OAuthFlowService,
+ private readonly tokensRepository: TokensRepository,
+ private readonly userRepository: UsersRepository
+ ) {
+ super();
+ }
+
+ async authenticate(request: Request) {
+ try {
+ const accessToken = request.get("Authorization")?.replace("Bearer ", "");
+ const requestOrigin = request.get("Origin");
+
+ if (!accessToken) {
+ throw new UnauthorizedException(INVALID_ACCESS_TOKEN);
+ }
+
+ await this.oauthFlowService.validateAccessToken(accessToken);
+
+ const client = await this.tokensRepository.getAccessTokenClient(accessToken);
+ if (!client) {
+ throw new UnauthorizedException("OAuth client not found given the access token");
+ }
+
+ if (requestOrigin && !client.redirectUris.some((uri) => uri.startsWith(requestOrigin))) {
+ throw new UnauthorizedException("Invalid request origin");
+ }
+
+ const ownerId = await this.tokensRepository.getAccessTokenOwnerId(accessToken);
+
+ if (!ownerId) {
+ throw new UnauthorizedException(INVALID_ACCESS_TOKEN);
+ }
+
+ const user: UserWithProfile | null = await this.userRepository.findByIdWithProfile(ownerId);
+
+ if (!user) {
+ throw new UnauthorizedException(INVALID_ACCESS_TOKEN);
+ }
+
+ return this.success(user);
+ } catch (error) {
+ if (error instanceof Error) return this.error(error);
+ }
+ }
+}
diff --git a/apps/api/v2/src/modules/auth/strategies/api-key-auth/api-key-auth.strategy.ts b/apps/api/v2/src/modules/auth/strategies/api-key-auth/api-key-auth.strategy.ts
new file mode 100644
index 00000000000000..40c7f1c970ca00
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/strategies/api-key-auth/api-key-auth.strategy.ts
@@ -0,0 +1,39 @@
+import { BaseStrategy } from "@/lib/passport/strategies/types";
+import { ApiKeyService } from "@/modules/api-key/api-key.service";
+import { UsersRepository } from "@/modules/users/users.repository";
+import { Injectable, NotFoundException, UnauthorizedException } from "@nestjs/common";
+import { PassportStrategy } from "@nestjs/passport";
+import type { Request } from "express";
+
+@Injectable()
+export class ApiKeyAuthStrategy extends PassportStrategy(BaseStrategy, "api-key") {
+ constructor(
+ private readonly apiKeyService: ApiKeyService,
+ private readonly userRepository: UsersRepository
+ ) {
+ super();
+ }
+
+ async authenticate(req: Request) {
+ try {
+ const apiKey = await this.apiKeyService.retrieveApiKey(req);
+
+ if (!apiKey) {
+ throw new UnauthorizedException("Authorization token is missing.");
+ }
+
+ if (apiKey.expiresAt && new Date() > apiKey.expiresAt) {
+ throw new UnauthorizedException("The API key is expired.");
+ }
+
+ const user = await this.userRepository.findById(apiKey.userId);
+ if (!user) {
+ throw new NotFoundException("User not found.");
+ }
+
+ this.success(user);
+ } catch (error) {
+ if (error instanceof Error) return this.error(error);
+ }
+ }
+}
diff --git a/apps/api/v2/src/modules/auth/strategies/next-auth/next-auth.strategy.ts b/apps/api/v2/src/modules/auth/strategies/next-auth/next-auth.strategy.ts
new file mode 100644
index 00000000000000..1827e59e9093bd
--- /dev/null
+++ b/apps/api/v2/src/modules/auth/strategies/next-auth/next-auth.strategy.ts
@@ -0,0 +1,38 @@
+import { BaseStrategy } from "@/lib/passport/strategies/types";
+import { UsersRepository } from "@/modules/users/users.repository";
+import { Injectable, UnauthorizedException } from "@nestjs/common";
+import { ConfigService } from "@nestjs/config";
+import { PassportStrategy } from "@nestjs/passport";
+import type { Request } from "express";
+import { getToken } from "next-auth/jwt";
+
+@Injectable()
+export class NextAuthStrategy extends PassportStrategy(BaseStrategy, "next-auth") {
+ constructor(private readonly userRepository: UsersRepository, private readonly config: ConfigService) {
+ super();
+ }
+
+ async authenticate(req: Request) {
+ try {
+ const nextAuthSecret = this.config.get("next.authSecret", { infer: true });
+ const payload = await getToken({ req, secret: nextAuthSecret });
+
+ if (!payload) {
+ throw new UnauthorizedException("Authentication token is missing or invalid.");
+ }
+
+ if (!payload.email) {
+ throw new UnauthorizedException("Email not found in the authentication token.");
+ }
+
+ const user = await this.userRepository.findByEmail(payload.email);
+ if (!user) {
+ throw new UnauthorizedException("User associated with the authentication token email not found.");
+ }
+
+ return this.success(user);
+ } catch (error) {
+ if (error instanceof Error) return this.error(error);
+ }
+ }
+}
diff --git a/apps/api/v2/src/modules/availabilities/availabilities.module.ts b/apps/api/v2/src/modules/availabilities/availabilities.module.ts
new file mode 100644
index 00000000000000..f3fe35bf6a7314
--- /dev/null
+++ b/apps/api/v2/src/modules/availabilities/availabilities.module.ts
@@ -0,0 +1,10 @@
+import { AvailabilitiesService } from "@/modules/availabilities/availabilities.service";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule],
+ providers: [AvailabilitiesService],
+ exports: [AvailabilitiesService],
+})
+export class AvailabilitiesModule {}
diff --git a/apps/api/v2/src/modules/availabilities/availabilities.service.ts b/apps/api/v2/src/modules/availabilities/availabilities.service.ts
new file mode 100644
index 00000000000000..fc7dc14ec494c3
--- /dev/null
+++ b/apps/api/v2/src/modules/availabilities/availabilities.service.ts
@@ -0,0 +1,16 @@
+import { CreateAvailabilityInput } from "@/modules/availabilities/inputs/create-availability.input";
+import { Injectable } from "@nestjs/common";
+
+@Injectable()
+export class AvailabilitiesService {
+ getDefaultAvailabilityInput(): CreateAvailabilityInput {
+ const startTime = new Date(new Date().setUTCHours(9, 0, 0, 0));
+ const endTime = new Date(new Date().setUTCHours(17, 0, 0, 0));
+
+ return {
+ days: [1, 2, 3, 4, 5],
+ startTime,
+ endTime,
+ };
+ }
+}
diff --git a/apps/api/v2/src/modules/availabilities/inputs/create-availability.input.ts b/apps/api/v2/src/modules/availabilities/inputs/create-availability.input.ts
new file mode 100644
index 00000000000000..a3050ae334c16d
--- /dev/null
+++ b/apps/api/v2/src/modules/availabilities/inputs/create-availability.input.ts
@@ -0,0 +1,41 @@
+import { BadRequestException } from "@nestjs/common";
+import { Transform, TransformFnParams } from "class-transformer";
+import { IsArray, IsDate, IsNumber } from "class-validator";
+
+export class CreateAvailabilityInput {
+ @IsArray()
+ @IsNumber({}, { each: true })
+ days!: number[];
+
+ @IsDate()
+ @Transform(({ value, key }: TransformFnParams) => transformStringToDate(value, key))
+ startTime!: Date;
+
+ @IsDate()
+ @Transform(({ value, key }: TransformFnParams) => transformStringToDate(value, key))
+ endTime!: Date;
+}
+
+function transformStringToDate(value: string, key: string): Date {
+ const parts = value.split(":");
+
+ if (parts.length !== 3) {
+ throw new BadRequestException(`Invalid ${key} format. Expected format: HH:MM:SS. Received ${value}`);
+ }
+
+ const [hours, minutes, seconds] = parts.map(Number);
+
+ if (hours < 0 || hours > 23) {
+ throw new BadRequestException(`Invalid ${key} hours. Expected value between 0 and 23`);
+ }
+
+ if (minutes < 0 || minutes > 59) {
+ throw new BadRequestException(`Invalid ${key} minutes. Expected value between 0 and 59`);
+ }
+
+ if (seconds < 0 || seconds > 59) {
+ throw new BadRequestException(`Invalid ${key} seconds. Expected value between 0 and 59`);
+ }
+
+ return new Date(new Date().setUTCHours(hours, minutes, seconds, 0));
+}
diff --git a/apps/api/v2/src/modules/credentials/credential.module.ts b/apps/api/v2/src/modules/credentials/credential.module.ts
new file mode 100644
index 00000000000000..c837e49328880d
--- /dev/null
+++ b/apps/api/v2/src/modules/credentials/credential.module.ts
@@ -0,0 +1,9 @@
+import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [],
+ providers: [CredentialsRepository],
+ exports: [CredentialsRepository],
+})
+export class CredentialsModule {}
diff --git a/apps/api/v2/src/modules/credentials/credentials.repository.ts b/apps/api/v2/src/modules/credentials/credentials.repository.ts
new file mode 100644
index 00000000000000..6b7a8978761440
--- /dev/null
+++ b/apps/api/v2/src/modules/credentials/credentials.repository.ts
@@ -0,0 +1,54 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { Injectable } from "@nestjs/common";
+import { Prisma } from "@prisma/client";
+
+import { APPS_TYPE_ID_MAPPING } from "@calcom/platform-constants";
+
+@Injectable()
+export class CredentialsRepository {
+ constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
+
+ createAppCredential(type: keyof typeof APPS_TYPE_ID_MAPPING, key: Prisma.InputJsonValue, userId: number) {
+ return this.dbWrite.prisma.credential.create({
+ data: {
+ type,
+ key,
+ userId,
+ appId: APPS_TYPE_ID_MAPPING[type],
+ },
+ });
+ }
+ getByTypeAndUserId(type: string, userId: number) {
+ return this.dbWrite.prisma.credential.findFirst({ where: { type, userId } });
+ }
+
+ getUserCredentialsByIds(userId: number, credentialIds: number[]) {
+ return this.dbRead.prisma.credential.findMany({
+ where: {
+ id: {
+ in: credentialIds,
+ },
+ userId: userId,
+ },
+ select: {
+ id: true,
+ type: true,
+ key: true,
+ userId: true,
+ teamId: true,
+ appId: true,
+ invalid: true,
+ user: {
+ select: {
+ email: true,
+ },
+ },
+ },
+ });
+ }
+}
+
+export type CredentialsWithUserEmail = Awaited<
+ ReturnType
+>;
diff --git a/apps/api/v2/src/modules/endpoints.module.ts b/apps/api/v2/src/modules/endpoints.module.ts
new file mode 100644
index 00000000000000..6f3b72d8e14893
--- /dev/null
+++ b/apps/api/v2/src/modules/endpoints.module.ts
@@ -0,0 +1,15 @@
+import { PlatformEndpointsModule } from "@/ee/platform-endpoints-module";
+import { EventsModule } from "@/modules/events/events.module";
+import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
+import type { MiddlewareConsumer, NestModule } from "@nestjs/common";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [EventsModule, OAuthClientModule, PlatformEndpointsModule],
+})
+export class EndpointsModule implements NestModule {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ configure(_consumer: MiddlewareConsumer) {
+ // TODO: apply ratelimits
+ }
+}
diff --git a/apps/api/v2/src/modules/events/controllers/events.controller.ts b/apps/api/v2/src/modules/events/controllers/events.controller.ts
new file mode 100644
index 00000000000000..b1c0c2567dcbd9
--- /dev/null
+++ b/apps/api/v2/src/modules/events/controllers/events.controller.ts
@@ -0,0 +1,38 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { Controller, Get, NotFoundException, InternalServerErrorException, Query } from "@nestjs/common";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { getPublicEvent } from "@calcom/platform-libraries";
+import type { PublicEventType } from "@calcom/platform-libraries";
+import { ApiResponse, GetPublicEventInput } from "@calcom/platform-types";
+import { PrismaClient } from "@calcom/prisma";
+
+@Controller({
+ path: "events",
+ version: "2",
+})
+export class EventsController {
+ constructor(private readonly prismaReadService: PrismaReadService) {}
+
+ @Get("/public")
+ async getPublicEvent(@Query() queryParams: GetPublicEventInput): Promise> {
+ try {
+ const event = await getPublicEvent(
+ queryParams.username,
+ queryParams.eventSlug,
+ queryParams.isTeamEvent,
+ queryParams.org || null,
+ this.prismaReadService.prisma as unknown as PrismaClient
+ );
+ return {
+ data: event,
+ status: SUCCESS_STATUS,
+ };
+ } catch (err) {
+ if (err instanceof Error) {
+ throw new NotFoundException(err.message);
+ }
+ }
+ throw new InternalServerErrorException("Could not find public event.");
+ }
+}
diff --git a/apps/api/v2/src/modules/events/events.module.ts b/apps/api/v2/src/modules/events/events.module.ts
new file mode 100644
index 00000000000000..57c8053271168c
--- /dev/null
+++ b/apps/api/v2/src/modules/events/events.module.ts
@@ -0,0 +1,9 @@
+import { EventsController } from "@/modules/events/controllers/events.controller";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule],
+ controllers: [EventsController],
+})
+export class EventsModule {}
diff --git a/apps/api/v2/src/modules/jwt/jwt.module.ts b/apps/api/v2/src/modules/jwt/jwt.module.ts
new file mode 100644
index 00000000000000..ef12816e2fdf73
--- /dev/null
+++ b/apps/api/v2/src/modules/jwt/jwt.module.ts
@@ -0,0 +1,12 @@
+import { getEnv } from "@/env";
+import { JwtService } from "@/modules/jwt/jwt.service";
+import { Global, Module } from "@nestjs/common";
+import { JwtModule as NestJwtModule } from "@nestjs/jwt";
+
+@Global()
+@Module({
+ imports: [NestJwtModule.register({ secret: getEnv("JWT_SECRET") })],
+ providers: [JwtService],
+ exports: [JwtService],
+})
+export class JwtModule {}
diff --git a/apps/api/v2/src/modules/jwt/jwt.service.ts b/apps/api/v2/src/modules/jwt/jwt.service.ts
new file mode 100644
index 00000000000000..c50e53ddb4d9e9
--- /dev/null
+++ b/apps/api/v2/src/modules/jwt/jwt.service.ts
@@ -0,0 +1,31 @@
+import { Injectable } from "@nestjs/common";
+import { JwtService as NestJwtService } from "@nestjs/jwt";
+
+@Injectable()
+export class JwtService {
+ constructor(private readonly nestJwtService: NestJwtService) {}
+
+ signAccessToken(payload: Payload) {
+ const accessToken = this.sign({ type: "access_token", ...payload });
+ return accessToken;
+ }
+
+ signRefreshToken(payload: Payload) {
+ const refreshToken = this.sign({ type: "refresh_token", ...payload });
+ return refreshToken;
+ }
+
+ sign(payload: Payload) {
+ const issuedAtTime = this.getIssuedAtTime();
+
+ const token = this.nestJwtService.sign({ ...payload, iat: issuedAtTime });
+ return token;
+ }
+
+ getIssuedAtTime() {
+ // divided by 1000 because iat (issued at time) is in seconds (not milliseconds) as informed by JWT speficication
+ return Math.floor(Date.now() / 1000);
+ }
+}
+
+type Payload = Record;
diff --git a/apps/api/v2/src/modules/memberships/memberships.module.ts b/apps/api/v2/src/modules/memberships/memberships.module.ts
new file mode 100644
index 00000000000000..418e1f6d1605d7
--- /dev/null
+++ b/apps/api/v2/src/modules/memberships/memberships.module.ts
@@ -0,0 +1,10 @@
+import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule],
+ providers: [MembershipsRepository],
+ exports: [MembershipsRepository],
+})
+export class MembershipsModule {}
diff --git a/apps/api/v2/src/modules/memberships/memberships.repository.ts b/apps/api/v2/src/modules/memberships/memberships.repository.ts
new file mode 100644
index 00000000000000..2a8e7d1a47598e
--- /dev/null
+++ b/apps/api/v2/src/modules/memberships/memberships.repository.ts
@@ -0,0 +1,33 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { Injectable } from "@nestjs/common";
+
+@Injectable()
+export class MembershipsRepository {
+ constructor(private readonly dbRead: PrismaReadService) {}
+
+ async findOrgUserMembership(organizationId: number, userId: number) {
+ const membership = await this.dbRead.prisma.membership.findUniqueOrThrow({
+ where: {
+ userId_teamId: {
+ userId: userId,
+ teamId: organizationId,
+ },
+ },
+ });
+
+ return membership;
+ }
+
+ async isUserOrganizationAdmin(userId: number, organizationId: number) {
+ const adminMembership = await this.dbRead.prisma.membership.findFirst({
+ where: {
+ userId,
+ teamId: organizationId,
+ accepted: true,
+ OR: [{ role: "ADMIN" }, { role: "OWNER" }],
+ },
+ });
+
+ return !!adminMembership;
+ }
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts
new file mode 100644
index 00000000000000..d1abb8d3aade5d
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.e2e-spec.ts
@@ -0,0 +1,228 @@
+import { bootstrap } from "@/app";
+import { AppModule } from "@/app.module";
+import { DEFAULT_EVENT_TYPES } from "@/ee/event-types/constants/constants";
+import { HttpExceptionFilter } from "@/filters/http-exception.filter";
+import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
+import {
+ CreateUserResponse,
+ UserReturned,
+} from "@/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller";
+import { CreateUserInput } from "@/modules/users/inputs/create-user.input";
+import { UpdateUserInput } from "@/modules/users/inputs/update-user.input";
+import { UsersModule } from "@/modules/users/users.module";
+import { INestApplication } from "@nestjs/common";
+import { NestExpressApplication } from "@nestjs/platform-express";
+import { Test } from "@nestjs/testing";
+import { PlatformOAuthClient, Team, User } from "@prisma/client";
+import * as request from "supertest";
+import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
+import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
+import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
+import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { ApiSuccessResponse } from "@calcom/platform-types";
+
+const CLIENT_REDIRECT_URI = "http://localhost:4321";
+
+describe("OAuth Client Users Endpoints", () => {
+ describe("Not authenticated", () => {
+ let app: INestApplication;
+
+ beforeAll(async () => {
+ const moduleRef = await Test.createTestingModule({
+ providers: [PrismaExceptionFilter, HttpExceptionFilter],
+ imports: [AppModule, UsersModule],
+ }).compile();
+
+ app = moduleRef.createNestApplication();
+ bootstrap(app as NestExpressApplication);
+ await app.init();
+ });
+
+ describe("secret header not set", () => {
+ it(`/POST`, () => {
+ return request(app.getHttpServer())
+ .post("/api/v2/oauth-clients/100/users")
+ .send({ email: "bob@gmail.com" })
+ .expect(401);
+ });
+ });
+
+ describe("Bearer access token not set", () => {
+ it(`/GET/:id`, () => {
+ return request(app.getHttpServer()).get("/api/v2/oauth-clients/100/users/200").expect(401);
+ });
+ it(`/PUT/:id`, () => {
+ return request(app.getHttpServer()).patch("/api/v2/oauth-clients/100/users/200").expect(401);
+ });
+ it(`/DELETE/:id`, () => {
+ return request(app.getHttpServer()).delete("/api/v2/oauth-clients/100/users/200").expect(401);
+ });
+ });
+
+ afterAll(async () => {
+ await app.close();
+ });
+ });
+
+ describe("User Authenticated", () => {
+ let app: INestApplication;
+
+ let oAuthClient: PlatformOAuthClient;
+ let organization: Team;
+ let userRepositoryFixture: UserRepositoryFixture;
+ let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
+ let teamRepositoryFixture: TeamRepositoryFixture;
+ let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
+
+ let postResponseData: CreateUserResponse;
+
+ beforeAll(async () => {
+ const moduleRef = await Test.createTestingModule({
+ providers: [PrismaExceptionFilter, HttpExceptionFilter],
+ imports: [AppModule, UsersModule],
+ }).compile();
+
+ app = moduleRef.createNestApplication();
+ bootstrap(app as NestExpressApplication);
+
+ oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
+ userRepositoryFixture = new UserRepositoryFixture(moduleRef);
+ teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
+ eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
+ organization = await teamRepositoryFixture.create({ name: "organization" });
+ oAuthClient = await createOAuthClient(organization.id);
+
+ await app.init();
+ });
+
+ async function createOAuthClient(organizationId: number) {
+ const data = {
+ logo: "logo-url",
+ name: "name",
+ redirectUris: [CLIENT_REDIRECT_URI],
+ permissions: 32,
+ };
+ const secret = "secret";
+
+ const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
+ return client;
+ }
+
+ it("should be defined", () => {
+ expect(oauthClientRepositoryFixture).toBeDefined();
+ expect(userRepositoryFixture).toBeDefined();
+ expect(oAuthClient).toBeDefined();
+ });
+
+ it(`should fail /POST with incorrect timeZone`, async () => {
+ const requestBody: CreateUserInput = {
+ email: "oauth-client-user@gmail.com",
+ timeZone: "incorrect-time-zone",
+ };
+
+ await request(app.getHttpServer())
+ .post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
+ .set("x-cal-secret-key", oAuthClient.secret)
+ .send(requestBody)
+ .expect(400);
+ });
+
+ it(`/POST`, async () => {
+ const requestBody: CreateUserInput = {
+ email: "oauth-client-user@gmail.com",
+ };
+
+ const response = await request(app.getHttpServer())
+ .post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
+ .set("x-cal-secret-key", oAuthClient.secret)
+ .send(requestBody)
+ .expect(201);
+
+ const responseBody: ApiSuccessResponse<{
+ user: Omit;
+ accessToken: string;
+ refreshToken: string;
+ }> = response.body;
+
+ postResponseData = responseBody.data;
+
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.user.email).toEqual(requestBody.email);
+ expect(responseBody.data.accessToken).toBeDefined();
+ expect(responseBody.data.refreshToken).toBeDefined();
+
+ await userConnectedToOAuth(responseBody.data.user.email);
+ await userHasDefaultEventTypes(responseBody.data.user.id);
+ });
+
+ async function userConnectedToOAuth(userEmail: string) {
+ const oAuthUsers = await oauthClientRepositoryFixture.getUsers(oAuthClient.id);
+ const newOAuthUser = oAuthUsers?.find((user) => user.email === userEmail);
+
+ expect(oAuthUsers?.length).toEqual(1);
+ expect(newOAuthUser?.email).toEqual(userEmail);
+ }
+
+ async function userHasDefaultEventTypes(userId: number) {
+ const defaultEventTypes = await eventTypesRepositoryFixture.getAllUserEventTypes(userId);
+
+ expect(defaultEventTypes?.length).toEqual(2);
+ expect(
+ defaultEventTypes?.find((eventType) => eventType.length === DEFAULT_EVENT_TYPES.thirtyMinutes.length)
+ ).toBeTruthy();
+ expect(
+ defaultEventTypes?.find((eventType) => eventType.length === DEFAULT_EVENT_TYPES.sixtyMinutes.length)
+ ).toBeTruthy();
+ }
+
+ it(`/GET/:id`, async () => {
+ const response = await request(app.getHttpServer())
+ .get(`/api/v2/oauth-clients/${oAuthClient.id}/users/${postResponseData.user.id}`)
+ .set("Authorization", `Bearer ${postResponseData.accessToken}`)
+ .set("Origin", `${CLIENT_REDIRECT_URI}`)
+ .expect(200);
+
+ const responseBody: ApiSuccessResponse = response.body;
+
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.email).toEqual(postResponseData.user.email);
+ });
+
+ it(`/PUT/:id`, async () => {
+ const userUpdatedEmail = "pineapple-pizza@gmail.com";
+ const body: UpdateUserInput = { email: userUpdatedEmail };
+
+ const response = await request(app.getHttpServer())
+ .patch(`/api/v2/oauth-clients/${oAuthClient.id}/users/${postResponseData.user.id}`)
+ .set("Authorization", `Bearer ${postResponseData.accessToken}`)
+ .set("Origin", `${CLIENT_REDIRECT_URI}`)
+ .send(body)
+ .expect(200);
+
+ const responseBody: ApiSuccessResponse> = response.body;
+
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.email).toEqual(userUpdatedEmail);
+ });
+
+ it(`/DELETE/:id`, () => {
+ return request(app.getHttpServer())
+ .delete(`/api/v2/oauth-clients/${oAuthClient.id}/users/${postResponseData.user.id}`)
+ .set("Authorization", `Bearer ${postResponseData.accessToken}`)
+ .set("Origin", `${CLIENT_REDIRECT_URI}`)
+ .expect(200);
+ });
+
+ afterAll(async () => {
+ await oauthClientRepositoryFixture.delete(oAuthClient.id);
+ await teamRepositoryFixture.delete(organization.id);
+
+ await app.close();
+ });
+ });
+});
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts
new file mode 100644
index 00000000000000..56faad2d37ec1c
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts
@@ -0,0 +1,178 @@
+import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
+import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
+import { OAuthClientCredentialsGuard } from "@/modules/oauth-clients/guards/oauth-client-credentials/oauth-client-credentials.guard";
+import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
+import { OAuthClientUsersService } from "@/modules/oauth-clients/services/oauth-clients-users.service";
+import { CreateUserInput } from "@/modules/users/inputs/create-user.input";
+import { UpdateUserInput } from "@/modules/users/inputs/update-user.input";
+import { UsersRepository } from "@/modules/users/users.repository";
+import {
+ Body,
+ Controller,
+ Post,
+ Logger,
+ UseGuards,
+ Get,
+ HttpCode,
+ HttpStatus,
+ NotFoundException,
+ Param,
+ Patch,
+ BadRequestException,
+ Delete,
+} from "@nestjs/common";
+import { User } from "@prisma/client";
+import * as crypto from "crypto";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { ApiResponse } from "@calcom/platform-types";
+
+@Controller({
+ path: "oauth-clients/:clientId/users",
+ version: "2",
+})
+export class OAuthClientUsersController {
+ private readonly logger = new Logger("UserController");
+
+ constructor(
+ private readonly userRepository: UsersRepository,
+ private readonly oAuthClientUsersService: OAuthClientUsersService,
+ private readonly oauthRepository: OAuthClientRepository
+ ) {}
+
+ @Post("/")
+ @UseGuards(OAuthClientCredentialsGuard)
+ async createUser(
+ @Param("clientId") oAuthClientId: string,
+ @Body() body: CreateUserInput
+ ): Promise> {
+ this.logger.log(
+ `Creating user with data: ${JSON.stringify(body, null, 2)} for OAuth Client with ID ${oAuthClientId}`
+ );
+ const existingUser = await this.userRepository.findByEmail(body.email);
+
+ if (existingUser) {
+ throw new BadRequestException("A user with the provided email already exists.");
+ }
+ const client = await this.oauthRepository.getOAuthClient(oAuthClientId);
+
+ const { user, tokens } = await this.oAuthClientUsersService.createOauthClientUser(
+ oAuthClientId,
+ body,
+ client?.organizationId
+ );
+
+ return {
+ status: SUCCESS_STATUS,
+ data: {
+ user: {
+ id: user.id,
+ email: user.email,
+ username: user.username,
+ },
+ accessToken: tokens.accessToken,
+ refreshToken: tokens.refreshToken,
+ },
+ };
+ }
+
+ @Get("/:userId")
+ @HttpCode(HttpStatus.OK)
+ @UseGuards(AccessTokenGuard)
+ async getUserById(
+ // @Param("clientId") is added to generate OpenAPI schema correctly: clientId is in @Controller path, and unless
+ // also added here as @Param, then it does not appear in OpenAPI schema.
+ @Param("clientId") _: string,
+ @GetUser("id") accessTokenUserId: number,
+ @Param("userId") userId: number
+ ): Promise> {
+ if (accessTokenUserId !== userId) {
+ throw new BadRequestException("userId parameter does not match access token");
+ }
+
+ const user = await this.userRepository.findById(userId);
+ if (!user) {
+ throw new NotFoundException(`User with ID ${userId} not found.`);
+ }
+
+ return {
+ status: SUCCESS_STATUS,
+ data: {
+ id: user.id,
+ email: user.email,
+ username: user.username,
+ },
+ };
+ }
+
+ @Patch("/:userId")
+ @HttpCode(HttpStatus.OK)
+ @UseGuards(AccessTokenGuard)
+ async updateUser(
+ // @Param("clientId") is added to generate OpenAPI schema correctly: clientId is in @Controller path, and unless
+ // also added here as @Param, then it does not appear in OpenAPI schema.
+ @Param("clientId") _: string,
+ @GetUser("id") accessTokenUserId: number,
+ @Param("userId") userId: number,
+ @Body() body: UpdateUserInput
+ ): Promise> {
+ if (accessTokenUserId !== userId) {
+ throw new BadRequestException("userId parameter does not match access token");
+ }
+
+ this.logger.log(`Updating user with ID ${userId}: ${JSON.stringify(body, null, 2)}`);
+
+ const user = await this.userRepository.update(userId, body);
+
+ return {
+ status: SUCCESS_STATUS,
+ data: {
+ id: user.id,
+ email: user.email,
+ username: user.username,
+ },
+ };
+ }
+
+ @Delete("/:userId")
+ @HttpCode(HttpStatus.OK)
+ @UseGuards(AccessTokenGuard)
+ async deleteUser(
+ // @Param("clientId") is added to generate OpenAPI schema correctly: clientId is in @Controller path, and unless
+ // also added here as @Param, then it does not appear in OpenAPI schema.
+ @Param("clientId") _: string,
+ @GetUser("id") accessTokenUserId: number,
+ @Param("userId") userId: number
+ ): Promise> {
+ if (accessTokenUserId !== userId) {
+ throw new BadRequestException("userId parameter does not match access token");
+ }
+
+ this.logger.log(`Deleting user with ID: ${userId}`);
+
+ const existingUser = await this.userRepository.findById(userId);
+
+ if (!existingUser) {
+ throw new NotFoundException(`User with ${userId} does not exist`);
+ }
+
+ if (existingUser.username) {
+ throw new BadRequestException("Cannot delete a non manually-managed user");
+ }
+
+ const user = await this.userRepository.delete(userId);
+
+ return {
+ status: SUCCESS_STATUS,
+ data: {
+ id: user.id,
+ email: user.email,
+ username: user.username,
+ },
+ };
+ }
+}
+
+export type UserReturned = Pick;
+
+export type CreateUserResponse = { user: UserReturned; accessToken: string; refreshToken: string };
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.e2e-spec.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.e2e-spec.ts
new file mode 100644
index 00000000000000..0bebd3c54ed9b8
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.e2e-spec.ts
@@ -0,0 +1,311 @@
+import { bootstrap } from "@/app";
+import { AppModule } from "@/app.module";
+import { HttpExceptionFilter } from "@/filters/http-exception.filter";
+import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
+import { AuthModule } from "@/modules/auth/auth.module";
+import { NextAuthStrategy } from "@/modules/auth/strategies/next-auth/next-auth.strategy";
+import { UpdateOAuthClientInput } from "@/modules/oauth-clients/inputs/update-oauth-client.input";
+import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { UsersModule } from "@/modules/users/users.module";
+import { INestApplication } from "@nestjs/common";
+import { NestExpressApplication } from "@nestjs/platform-express";
+import { Test } from "@nestjs/testing";
+import { Membership, PlatformOAuthClient, Team, User } from "@prisma/client";
+import * as request from "supertest";
+import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
+import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
+import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
+import { NextAuthMockStrategy } from "test/mocks/next-auth-mock.strategy";
+import { withNextAuth } from "test/utils/withNextAuth";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { CreateOAuthClientInput } from "@calcom/platform-types";
+import { ApiSuccessResponse } from "@calcom/platform-types";
+
+describe("OAuth Clients Endpoints", () => {
+ describe("User Not Authenticated", () => {
+ let appWithoutAuth: INestApplication;
+
+ beforeAll(async () => {
+ const moduleRef = await Test.createTestingModule({
+ providers: [PrismaExceptionFilter, HttpExceptionFilter],
+ imports: [AppModule, OAuthClientModule, UsersModule, AuthModule, PrismaModule],
+ }).compile();
+ appWithoutAuth = moduleRef.createNestApplication();
+ bootstrap(appWithoutAuth as NestExpressApplication);
+ await appWithoutAuth.init();
+ });
+
+ it(`/GET`, () => {
+ return request(appWithoutAuth.getHttpServer()).get("/api/v2/oauth-clients").expect(401);
+ });
+ it(`/GET/:id`, () => {
+ return request(appWithoutAuth.getHttpServer()).get("/api/v2/oauth-clients/1234").expect(401);
+ });
+ it(`/POST`, () => {
+ return request(appWithoutAuth.getHttpServer()).post("/api/v2/oauth-clients").expect(401);
+ });
+ it(`/PUT/:id`, () => {
+ return request(appWithoutAuth.getHttpServer()).patch("/api/v2/oauth-clients/1234").expect(401);
+ });
+ it(`/DELETE/:id`, () => {
+ return request(appWithoutAuth.getHttpServer()).delete("/api/v2/oauth-clients/1234").expect(401);
+ });
+
+ afterAll(async () => {
+ await appWithoutAuth.close();
+ });
+ });
+
+ describe("User Is Authenticated", () => {
+ let usersFixtures: UserRepositoryFixture;
+ let membershipFixtures: MembershipRepositoryFixture;
+ let teamFixtures: TeamRepositoryFixture;
+ let user: User;
+ let org: Team;
+ let app: INestApplication;
+ const userEmail = "test-e2e@api.com";
+
+ beforeAll(async () => {
+ const moduleRef = await withNextAuth(
+ userEmail,
+ Test.createTestingModule({
+ providers: [PrismaExceptionFilter, HttpExceptionFilter],
+ imports: [AppModule, OAuthClientModule, UsersModule, AuthModule, PrismaModule],
+ })
+ ).compile();
+ const strategy = moduleRef.get(NextAuthStrategy);
+ expect(strategy).toBeInstanceOf(NextAuthMockStrategy);
+ usersFixtures = new UserRepositoryFixture(moduleRef);
+ membershipFixtures = new MembershipRepositoryFixture(moduleRef);
+ teamFixtures = new TeamRepositoryFixture(moduleRef);
+ user = await usersFixtures.create({
+ email: userEmail,
+ });
+ org = await teamFixtures.create({
+ name: "apiOrg",
+ metadata: {
+ isOrganization: true,
+ orgAutoAcceptEmail: "api.com",
+ isOrganizationVerified: true,
+ isOrganizationConfigured: true,
+ },
+ });
+ app = moduleRef.createNestApplication();
+ bootstrap(app as NestExpressApplication);
+ await app.init();
+ });
+
+ describe("User is not in an organization", () => {
+ it(`/GET`, () => {
+ return request(app.getHttpServer()).get("/api/v2/oauth-clients").expect(403);
+ });
+ it(`/GET/:id`, () => {
+ return request(app.getHttpServer()).get("/api/v2/oauth-clients/1234").expect(403);
+ });
+ it(`/POST`, () => {
+ return request(app.getHttpServer()).post("/api/v2/oauth-clients").expect(403);
+ });
+ it(`/PUT/:id`, () => {
+ return request(app.getHttpServer()).patch("/api/v2/oauth-clients/1234").expect(403);
+ });
+ it(`/DELETE/:id`, () => {
+ return request(app.getHttpServer()).delete("/api/v2/oauth-clients/1234").expect(403);
+ });
+ });
+
+ describe("User is part of an organization as Member", () => {
+ let membership: Membership;
+ beforeAll(async () => {
+ membership = await membershipFixtures.addUserToOrg(user, org, "MEMBER", true);
+ });
+
+ it(`/GET`, () => {
+ return request(app.getHttpServer()).get("/api/v2/oauth-clients").expect(200);
+ });
+ it(`/GET/:id - oAuth client does not exist`, () => {
+ return request(app.getHttpServer()).get("/api/v2/oauth-clients/1234").expect(404);
+ });
+ it(`/POST`, () => {
+ return request(app.getHttpServer()).post("/api/v2/oauth-clients").expect(403);
+ });
+ it(`/PUT/:id`, () => {
+ return request(app.getHttpServer()).patch("/api/v2/oauth-clients/1234").expect(403);
+ });
+ it(`/DELETE/:id`, () => {
+ return request(app.getHttpServer()).delete("/api/v2/oauth-clients/1234").expect(403);
+ });
+
+ afterAll(async () => {
+ await membershipFixtures.delete(membership.id);
+ });
+ });
+
+ describe("User is part of an organization as Admin", () => {
+ let membership: Membership;
+ let client: { clientId: string; clientSecret: string };
+ const oAuthClientName = "test-oauth-client-admin";
+
+ beforeAll(async () => {
+ membership = await membershipFixtures.addUserToOrg(user, org, "ADMIN", true);
+ });
+
+ it(`/POST`, () => {
+ const body: CreateOAuthClientInput = {
+ name: oAuthClientName,
+ redirectUris: ["http://test-oauth-client.com"],
+ permissions: 32,
+ };
+ return request(app.getHttpServer())
+ .post("/api/v2/oauth-clients")
+ .send(body)
+ .expect(201)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse<{ clientId: string; clientSecret: string }> =
+ response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.clientId).toBeDefined();
+ expect(responseBody.data.clientSecret).toBeDefined();
+ client = {
+ clientId: responseBody.data.clientId,
+ clientSecret: responseBody.data.clientSecret,
+ };
+ });
+ });
+ it(`/GET`, () => {
+ return request(app.getHttpServer())
+ .get("/api/v2/oauth-clients")
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data).toBeInstanceOf(Array);
+ expect(responseBody.data[0].name).toEqual(oAuthClientName);
+ });
+ });
+ it(`/GET/:id`, () => {
+ return request(app.getHttpServer())
+ .get(`/api/v2/oauth-clients/${client.clientId}`)
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.name).toEqual(oAuthClientName);
+ });
+ });
+ it(`/PUT/:id`, () => {
+ const clientUpdatedName = "test-oauth-client-updated";
+ const body: UpdateOAuthClientInput = { name: clientUpdatedName };
+ return request(app.getHttpServer())
+ .patch(`/api/v2/oauth-clients/${client.clientId}`)
+ .send(body)
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.name).toEqual(clientUpdatedName);
+ });
+ });
+ it(`/DELETE/:id`, () => {
+ return request(app.getHttpServer()).delete(`/api/v2/oauth-clients/${client.clientId}`).expect(200);
+ });
+
+ afterAll(async () => {
+ await membershipFixtures.delete(membership.id);
+ });
+ });
+
+ describe("User is part of an organization as Owner", () => {
+ let membership: Membership;
+ let client: { clientId: string; clientSecret: string };
+ const oAuthClientName = "test-oauth-client-owner";
+ const oAuthClientPermissions = 32;
+
+ beforeAll(async () => {
+ membership = await membershipFixtures.addUserToOrg(user, org, "OWNER", true);
+ });
+
+ it(`/POST`, () => {
+ const body: CreateOAuthClientInput = {
+ name: oAuthClientName,
+ redirectUris: ["http://test-oauth-client.com"],
+ permissions: 32,
+ };
+ return request(app.getHttpServer())
+ .post("/api/v2/oauth-clients")
+ .send(body)
+ .expect(201)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse<{ clientId: string; clientSecret: string }> =
+ response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.clientId).toBeDefined();
+ expect(responseBody.data.clientSecret).toBeDefined();
+ client = {
+ clientId: responseBody.data.clientId,
+ clientSecret: responseBody.data.clientSecret,
+ };
+ });
+ });
+
+ it(`/GET`, () => {
+ return request(app.getHttpServer())
+ .get("/api/v2/oauth-clients")
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data).toBeInstanceOf(Array);
+ expect(responseBody.data[0].name).toEqual(oAuthClientName);
+ expect(responseBody.data[0].permissions).toEqual(oAuthClientPermissions);
+ });
+ });
+ it(`/GET/:id`, () => {
+ return request(app.getHttpServer())
+ .get(`/api/v2/oauth-clients/${client.clientId}`)
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.name).toEqual(oAuthClientName);
+ expect(responseBody.data.permissions).toEqual(oAuthClientPermissions);
+ });
+ });
+ it(`/PUT/:id`, () => {
+ const clientUpdatedName = "test-oauth-client-updated";
+ const body: UpdateOAuthClientInput = { name: clientUpdatedName };
+ return request(app.getHttpServer())
+ .patch(`/api/v2/oauth-clients/${client.clientId}`)
+ .send(body)
+ .expect(200)
+ .then((response) => {
+ const responseBody: ApiSuccessResponse = response.body;
+ expect(responseBody.status).toEqual(SUCCESS_STATUS);
+ expect(responseBody.data).toBeDefined();
+ expect(responseBody.data.name).toEqual(clientUpdatedName);
+ });
+ });
+ it(`/DELETE/:id`, () => {
+ return request(app.getHttpServer()).delete(`/api/v2/oauth-clients/${client.clientId}`).expect(200);
+ });
+
+ afterAll(async () => {
+ await membershipFixtures.delete(membership.id);
+ });
+ });
+
+ afterAll(async () => {
+ teamFixtures.delete(org.id);
+ usersFixtures.delete(user.id);
+ await app.close();
+ });
+ });
+});
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.ts
new file mode 100644
index 00000000000000..da7a203ac3726f
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.ts
@@ -0,0 +1,121 @@
+import { getEnv } from "@/env";
+import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
+import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
+import { NextAuthGuard } from "@/modules/auth/guards/next-auth/next-auth.guard";
+import { OrganizationRolesGuard } from "@/modules/auth/guards/organization-roles/organization-roles.guard";
+import { CreateOAuthClientResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/CreateOAuthClientResponse.dto";
+import { GetOAuthClientResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientResponse.dto";
+import { GetOAuthClientsResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientsResponse.dto";
+import { UpdateOAuthClientInput } from "@/modules/oauth-clients/inputs/update-oauth-client.input";
+import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
+import {
+ Body,
+ Controller,
+ Get,
+ Post,
+ Patch,
+ Delete,
+ Param,
+ HttpCode,
+ HttpStatus,
+ Logger,
+ UseGuards,
+ NotFoundException,
+} from "@nestjs/common";
+import {
+ ApiTags as DocsTags,
+ ApiExcludeController as DocsExcludeController,
+ ApiOperation as DocsOperation,
+ ApiCreatedResponse as DocsCreatedResponse,
+} from "@nestjs/swagger";
+import { MembershipRole } from "@prisma/client";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { CreateOAuthClientInput } from "@calcom/platform-types";
+
+const AUTH_DOCUMENTATION = `â ïž First, this endpoint requires \`Cookie: next-auth.session-token=eyJhbGciOiJ\` header. Log into Cal web app using owner of organization that was created after visiting \`/settings/organizations/new\`, refresh swagger docs, and the cookie will be added to requests automatically to pass the NextAuthGuard.
+Second, make sure that the logged in user has organizationId set to pass the OrganizationRolesGuard guard.`;
+
+@Controller({
+ path: "oauth-clients",
+ version: "2",
+})
+@UseGuards(NextAuthGuard, OrganizationRolesGuard)
+@DocsExcludeController(getEnv("NODE_ENV") === "production")
+@DocsTags("Development only")
+export class OAuthClientsController {
+ private readonly logger = new Logger("OAuthClientController");
+
+ constructor(private readonly oauthClientRepository: OAuthClientRepository) {}
+
+ @Post("/")
+ @HttpCode(HttpStatus.CREATED)
+ @Roles([MembershipRole.ADMIN, MembershipRole.OWNER])
+ @DocsOperation({ description: AUTH_DOCUMENTATION })
+ @DocsCreatedResponse({
+ description: "Create an OAuth client",
+ type: CreateOAuthClientResponseDto,
+ })
+ async createOAuthClient(
+ @GetUser("organizationId") organizationId: number,
+ @Body() body: CreateOAuthClientInput
+ ): Promise {
+ this.logger.log(
+ `For organisation ${organizationId} creating OAuth Client with data: ${JSON.stringify(body)}`
+ );
+ const { id, secret } = await this.oauthClientRepository.createOAuthClient(organizationId, body);
+ return {
+ status: SUCCESS_STATUS,
+ data: {
+ clientId: id,
+ clientSecret: secret,
+ },
+ };
+ }
+
+ @Get("/")
+ @HttpCode(HttpStatus.OK)
+ @Roles([MembershipRole.ADMIN, MembershipRole.OWNER, MembershipRole.MEMBER])
+ @DocsOperation({ description: AUTH_DOCUMENTATION })
+ async getOAuthClients(
+ @GetUser("organizationId") organizationId: number
+ ): Promise {
+ const clients = await this.oauthClientRepository.getOrganizationOAuthClients(organizationId);
+ return { status: SUCCESS_STATUS, data: clients };
+ }
+
+ @Get("/:clientId")
+ @HttpCode(HttpStatus.OK)
+ @Roles([MembershipRole.ADMIN, MembershipRole.OWNER, MembershipRole.MEMBER])
+ @DocsOperation({ description: AUTH_DOCUMENTATION })
+ async getOAuthClientById(@Param("clientId") clientId: string): Promise {
+ const client = await this.oauthClientRepository.getOAuthClient(clientId);
+ if (!client) {
+ throw new NotFoundException(`OAuth client with ID ${clientId} not found`);
+ }
+ return { status: SUCCESS_STATUS, data: client };
+ }
+
+ @Patch("/:clientId")
+ @HttpCode(HttpStatus.OK)
+ @Roles([MembershipRole.ADMIN, MembershipRole.OWNER])
+ @DocsOperation({ description: AUTH_DOCUMENTATION })
+ async updateOAuthClient(
+ @Param("clientId") clientId: string,
+ @Body() body: UpdateOAuthClientInput
+ ): Promise {
+ this.logger.log(`For client ${clientId} updating OAuth Client with data: ${JSON.stringify(body)}`);
+ const client = await this.oauthClientRepository.updateOAuthClient(clientId, body);
+ return { status: SUCCESS_STATUS, data: client };
+ }
+
+ @Delete("/:clientId")
+ @HttpCode(HttpStatus.OK)
+ @Roles([MembershipRole.ADMIN, MembershipRole.OWNER])
+ @DocsOperation({ description: AUTH_DOCUMENTATION })
+ async deleteOAuthClient(@Param("clientId") clientId: string): Promise {
+ this.logger.log(`Deleting OAuth Client with ID: ${clientId}`);
+ const client = await this.oauthClientRepository.deleteOAuthClient(clientId);
+ return { status: SUCCESS_STATUS, data: client };
+ }
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/responses/CreateOAuthClientResponse.dto.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/responses/CreateOAuthClientResponse.dto.ts
new file mode 100644
index 00000000000000..fcee72c195ceea
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/responses/CreateOAuthClientResponse.dto.ts
@@ -0,0 +1,36 @@
+import { ApiProperty } from "@nestjs/swagger";
+import { Type } from "class-transformer";
+import { IsIn, ValidateNested, IsNotEmptyObject, IsString } from "class-validator";
+
+import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
+
+class DataDto {
+ @ApiProperty({
+ example: "clsx38nbl0001vkhlwin9fmt0",
+ })
+ @IsString()
+ clientId!: string;
+
+ @ApiProperty({
+ example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoib2F1dGgtY2xpZW50Iiwi",
+ })
+ @IsString()
+ clientSecret!: string;
+}
+
+export class CreateOAuthClientResponseDto {
+ @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
+ @IsIn([SUCCESS_STATUS, ERROR_STATUS])
+ status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
+
+ @ApiProperty({
+ example: {
+ clientId: "clsx38nbl0001vkhlwin9fmt0",
+ clientSecret: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoib2F1dGgtY2xpZW50Iiwi",
+ },
+ })
+ @IsNotEmptyObject()
+ @ValidateNested()
+ @Type(() => DataDto)
+ data!: DataDto;
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientResponse.dto.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientResponse.dto.ts
new file mode 100644
index 00000000000000..f019f43fcf04a0
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientResponse.dto.ts
@@ -0,0 +1,64 @@
+import { ApiProperty } from "@nestjs/swagger";
+import { Type } from "class-transformer";
+import {
+ IsArray,
+ ValidateNested,
+ IsEnum,
+ IsString,
+ IsNumber,
+ IsOptional,
+ IsDate,
+ IsNotEmptyObject,
+} from "class-validator";
+
+import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
+
+export class PlatformOAuthClientDto {
+ @ApiProperty({ example: "clsx38nbl0001vkhlwin9fmt0" })
+ @IsString()
+ id!: string;
+
+ @ApiProperty({ example: "MyClient" })
+ @IsString()
+ name!: string;
+
+ @ApiProperty({ example: "secretValue" })
+ @IsString()
+ secret!: string;
+
+ @ApiProperty({ example: 3 })
+ @IsNumber()
+ permissions!: number;
+
+ @ApiProperty({ example: "https://example.com/logo.png", required: false })
+ @IsOptional()
+ @IsString()
+ logo!: string | null;
+
+ @ApiProperty({ example: ["https://example.com/callback"] })
+ @IsArray()
+ @IsString({ each: true })
+ redirectUris!: string[];
+
+ @ApiProperty({ example: 1 })
+ @IsNumber()
+ organizationId!: number;
+
+ @ApiProperty({ example: new Date(), type: Date })
+ @IsDate()
+ createdAt!: Date;
+}
+
+export class GetOAuthClientResponseDto {
+ @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
+ @IsEnum([SUCCESS_STATUS, ERROR_STATUS])
+ status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
+
+ @ApiProperty({
+ type: PlatformOAuthClientDto,
+ })
+ @IsNotEmptyObject()
+ @ValidateNested()
+ @Type(() => PlatformOAuthClientDto)
+ data!: PlatformOAuthClientDto;
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientsResponse.dto.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientsResponse.dto.ts
new file mode 100644
index 00000000000000..584729b9f06d3b
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientsResponse.dto.ts
@@ -0,0 +1,21 @@
+import { PlatformOAuthClientDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientResponse.dto";
+import { ApiProperty } from "@nestjs/swagger";
+import { Type } from "class-transformer";
+import { IsArray, ValidateNested, IsEnum } from "class-validator";
+
+import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
+
+export class GetOAuthClientsResponseDto {
+ @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
+ @IsEnum([SUCCESS_STATUS, ERROR_STATUS])
+ status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
+
+ @ApiProperty({
+ type: PlatformOAuthClientDto,
+ isArray: true,
+ })
+ @ValidateNested({ each: true })
+ @Type(() => PlatformOAuthClientDto)
+ @IsArray()
+ data!: PlatformOAuthClientDto[];
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.e2e-spec.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.e2e-spec.ts
new file mode 100644
index 00000000000000..2081240bbe0c5e
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.e2e-spec.ts
@@ -0,0 +1,175 @@
+import { bootstrap } from "@/app";
+import { AppModule } from "@/app.module";
+import { HttpExceptionFilter } from "@/filters/http-exception.filter";
+import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
+import { ZodExceptionFilter } from "@/filters/zod-exception.filter";
+import { AuthModule } from "@/modules/auth/auth.module";
+import { OAuthAuthorizeInput } from "@/modules/oauth-clients/inputs/authorize.input";
+import { ExchangeAuthorizationCodeInput } from "@/modules/oauth-clients/inputs/exchange-code.input";
+import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { UsersModule } from "@/modules/users/users.module";
+import { INestApplication } from "@nestjs/common";
+import { NestExpressApplication } from "@nestjs/platform-express";
+import { Test, TestingModule } from "@nestjs/testing";
+import { PlatformOAuthClient, Team, User } from "@prisma/client";
+import * as request from "supertest";
+import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
+import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
+import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
+import { withNextAuth } from "test/utils/withNextAuth";
+
+import { X_CAL_SECRET_KEY } from "@calcom/platform-constants";
+
+describe("OAuthFlow Endpoints", () => {
+ describe("User Not Authenticated", () => {
+ let appWithoutAuth: INestApplication;
+
+ beforeAll(async () => {
+ const moduleRef = await Test.createTestingModule({
+ providers: [PrismaExceptionFilter, HttpExceptionFilter, ZodExceptionFilter],
+ imports: [AppModule, OAuthClientModule, UsersModule, AuthModule, PrismaModule],
+ }).compile();
+ appWithoutAuth = moduleRef.createNestApplication();
+ bootstrap(appWithoutAuth as NestExpressApplication);
+ await appWithoutAuth.init();
+ });
+
+ it(`POST /oauth/:clientId/authorize missing Cookie with user`, () => {
+ return request(appWithoutAuth.getHttpServer()).post("/api/v2/oauth/100/authorize").expect(401);
+ });
+
+ it(`POST /oauth/:clientId/exchange missing Authorization Bearer token`, () => {
+ return request(appWithoutAuth.getHttpServer()).post("/api/v2/oauth/100/exchange").expect(400);
+ });
+
+ it(`POST /oauth/:clientId/refresh missing ${X_CAL_SECRET_KEY} header with secret`, () => {
+ return request(appWithoutAuth.getHttpServer()).post("/api/v2/oauth/100/refresh").expect(401);
+ });
+
+ afterAll(async () => {
+ await appWithoutAuth.close();
+ });
+ });
+
+ describe("User Authenticated", () => {
+ let app: INestApplication;
+
+ let usersRepositoryFixtures: UserRepositoryFixture;
+ let organizationsRepositoryFixture: TeamRepositoryFixture;
+ let oAuthClientsRepositoryFixture: OAuthClientRepositoryFixture;
+
+ let user: User;
+ let organization: Team;
+ let oAuthClient: PlatformOAuthClient;
+
+ let authorizationCode: string | null;
+ let refreshToken: string;
+
+ beforeAll(async () => {
+ const userEmail = "developer@platform.com";
+
+ const moduleRef: TestingModule = await withNextAuth(
+ userEmail,
+ Test.createTestingModule({
+ providers: [PrismaExceptionFilter, HttpExceptionFilter, ZodExceptionFilter],
+ imports: [AppModule, OAuthClientModule, UsersModule, AuthModule, PrismaModule],
+ })
+ ).compile();
+
+ app = moduleRef.createNestApplication();
+ await app.init();
+
+ oAuthClientsRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
+ organizationsRepositoryFixture = new TeamRepositoryFixture(moduleRef);
+ usersRepositoryFixtures = new UserRepositoryFixture(moduleRef);
+
+ user = await usersRepositoryFixtures.create({
+ email: userEmail,
+ });
+ organization = await organizationsRepositoryFixture.create({ name: "organization" });
+ oAuthClient = await createOAuthClient(organization.id);
+ });
+
+ async function createOAuthClient(organizationId: number) {
+ const data = {
+ logo: "logo-url",
+ name: "name",
+ redirectUris: ["redirect-uri.com"],
+ permissions: 32,
+ };
+ const secret = "secret";
+
+ const client = await oAuthClientsRepositoryFixture.create(organizationId, data, secret);
+ return client;
+ }
+
+ describe("Authorize Endpoint", () => {
+ it("POST /oauth/:clientId/authorize", async () => {
+ const body: OAuthAuthorizeInput = {
+ redirectUri: oAuthClient.redirectUris[0],
+ };
+
+ const REDIRECT_STATUS = 302;
+
+ const response = await request(app.getHttpServer())
+ .post(`/oauth/${oAuthClient.id}/authorize`)
+ .send(body)
+ .expect(REDIRECT_STATUS);
+
+ const baseUrl = "http://www.localhost/";
+ const redirectUri = new URL(response.header.location, baseUrl);
+ authorizationCode = redirectUri.searchParams.get("code");
+
+ expect(authorizationCode).toBeDefined();
+ });
+ });
+
+ describe("Exchange Endpoint", () => {
+ it("POST /oauth/:clientId/exchange", async () => {
+ const authorizationToken = `Bearer ${authorizationCode}`;
+ const body: ExchangeAuthorizationCodeInput = {
+ clientSecret: oAuthClient.secret,
+ };
+
+ const response = await request(app.getHttpServer())
+ .post(`/oauth/${oAuthClient.id}/exchange`)
+ .set("Authorization", authorizationToken)
+ .send(body)
+ .expect(200);
+
+ expect(response.body?.data?.accessToken).toBeDefined();
+ expect(response.body?.data?.refreshToken).toBeDefined();
+
+ refreshToken = response.body.data.refreshToken;
+ });
+ });
+
+ describe("Refresh Token Endpoint", () => {
+ it("POST /oauth/:clientId/refresh", () => {
+ const secretKey = oAuthClient.secret;
+ const body = {
+ refreshToken,
+ };
+
+ return request(app.getHttpServer())
+ .post(`/oauth/${oAuthClient.id}/refresh`)
+ .set("x-cal-secret-key", secretKey)
+ .send(body)
+ .expect(200)
+ .then((response) => {
+ expect(response.body?.data?.accessToken).toBeDefined();
+ expect(response.body?.data?.refreshToken).toBeDefined();
+ });
+ });
+ });
+
+ afterAll(async () => {
+ await oAuthClientsRepositoryFixture.delete(oAuthClient.id);
+ await organizationsRepositoryFixture.delete(organization.id);
+ await usersRepositoryFixtures.delete(user.id);
+
+ await app.close();
+ });
+ });
+});
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts
new file mode 100644
index 00000000000000..12172dc0b1fb98
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts
@@ -0,0 +1,157 @@
+import { getEnv } from "@/env";
+import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
+import { NextAuthGuard } from "@/modules/auth/guards/next-auth/next-auth.guard";
+import { KeysResponseDto } from "@/modules/oauth-clients/controllers/oauth-flow/responses/KeysResponse.dto";
+import { OAuthClientCredentialsGuard } from "@/modules/oauth-clients/guards/oauth-client-credentials/oauth-client-credentials.guard";
+import { OAuthAuthorizeInput } from "@/modules/oauth-clients/inputs/authorize.input";
+import { ExchangeAuthorizationCodeInput } from "@/modules/oauth-clients/inputs/exchange-code.input";
+import { RefreshTokenInput } from "@/modules/oauth-clients/inputs/refresh-token.input";
+import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
+import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import {
+ BadRequestException,
+ Body,
+ Controller,
+ Headers,
+ HttpCode,
+ HttpStatus,
+ Param,
+ Post,
+ Response,
+ UseGuards,
+} from "@nestjs/common";
+import {
+ ApiTags as DocsTags,
+ ApiExcludeController as DocsExcludeController,
+ ApiOperation as DocsOperation,
+ ApiOkResponse as DocsOkResponse,
+ ApiBadRequestResponse as DocsBadRequestResponse,
+} from "@nestjs/swagger";
+import { Response as ExpressResponse } from "express";
+
+import { SUCCESS_STATUS, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
+
+@Controller({
+ path: "oauth/:clientId",
+ version: "2",
+})
+@DocsExcludeController(getEnv("NODE_ENV") === "production")
+@DocsTags("Development only")
+export class OAuthFlowController {
+ constructor(
+ private readonly oauthClientRepository: OAuthClientRepository,
+ private readonly tokensRepository: TokensRepository,
+ private readonly oAuthFlowService: OAuthFlowService
+ ) {}
+
+ @Post("/authorize")
+ @HttpCode(HttpStatus.OK)
+ @UseGuards(NextAuthGuard)
+ @DocsOperation({
+ summary: "Authorize an OAuth client",
+ description:
+ "Redirects the user to the specified 'redirect_uri' with an authorization code in query parameter if the client is authorized successfully. The code is then exchanged for access and refresh tokens via the `/exchange` endpoint.",
+ })
+ @DocsOkResponse({
+ description:
+ "The user is redirected to the 'redirect_uri' with an authorization code in query parameter e.g. `redirectUri?code=secretcode.`",
+ })
+ @DocsBadRequestResponse({
+ description:
+ "Bad request if the OAuth client is not found, if the redirect URI is invalid, or if the user has already authorized the client.",
+ })
+ async authorize(
+ @Param("clientId") clientId: string,
+ @Body() body: OAuthAuthorizeInput,
+ @GetUser("id") userId: number,
+ @Response() res: ExpressResponse
+ ): Promise {
+ const oauthClient = await this.oauthClientRepository.getOAuthClient(clientId);
+ if (!oauthClient) {
+ throw new BadRequestException(`OAuth client with ID '${clientId}' not found`);
+ }
+
+ if (!oauthClient?.redirectUris.includes(body.redirectUri)) {
+ throw new BadRequestException("Invalid 'redirect_uri' value.");
+ }
+
+ const alreadyAuthorized = await this.tokensRepository.getAuthorizationTokenByClientUserIds(
+ clientId,
+ userId
+ );
+
+ if (alreadyAuthorized) {
+ throw new BadRequestException(
+ `User with id=${userId} has already authorized client with id=${clientId}.`
+ );
+ }
+
+ const { id } = await this.tokensRepository.createAuthorizationToken(clientId, userId);
+
+ return res.redirect(`${body.redirectUri}?code=${id}`);
+ }
+
+ @Post("/exchange")
+ @HttpCode(HttpStatus.OK)
+ @DocsOperation({
+ summary: "Exchange authorization code for access tokens",
+ description:
+ "Exchanges the authorization code received from the `/authorize` endpoint for access and refresh tokens. The authorization code should be provided in the 'Authorization' header prefixed with 'Bearer '.",
+ })
+ @DocsOkResponse({
+ type: KeysResponseDto,
+ description: "Successfully exchanged authorization code for access and refresh tokens.",
+ })
+ @DocsBadRequestResponse({
+ description:
+ "Bad request if the authorization code is missing, invalid, or if the client ID and secret do not match.",
+ })
+ async exchange(
+ @Headers("Authorization") authorization: string,
+ @Param("clientId") clientId: string,
+ @Body() body: ExchangeAuthorizationCodeInput
+ ): Promise {
+ const authorizeEndpointCode = authorization.replace("Bearer ", "").trim();
+ if (!authorizeEndpointCode) {
+ throw new BadRequestException("Missing 'Bearer' Authorization header.");
+ }
+
+ const { accessToken, refreshToken } = await this.oAuthFlowService.exchangeAuthorizationToken(
+ authorizeEndpointCode,
+ clientId,
+ body.clientSecret
+ );
+
+ return {
+ status: SUCCESS_STATUS,
+ data: {
+ accessToken,
+ refreshToken,
+ },
+ };
+ }
+
+ @Post("/refresh")
+ @HttpCode(HttpStatus.OK)
+ @UseGuards(OAuthClientCredentialsGuard)
+ async refreshAccessToken(
+ @Param("clientId") clientId: string,
+ @Headers(X_CAL_SECRET_KEY) secretKey: string,
+ @Body() body: RefreshTokenInput
+ ): Promise {
+ const { accessToken, refreshToken } = await this.oAuthFlowService.refreshToken(
+ clientId,
+ secretKey,
+ body.refreshToken
+ );
+
+ return {
+ status: SUCCESS_STATUS,
+ data: {
+ accessToken: accessToken,
+ refreshToken: refreshToken,
+ },
+ };
+ }
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/responses/KeysResponse.dto.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/responses/KeysResponse.dto.ts
new file mode 100644
index 00000000000000..ba5c85b51e5a9b
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/responses/KeysResponse.dto.ts
@@ -0,0 +1,33 @@
+import { ApiProperty } from "@nestjs/swagger";
+import { Type } from "class-transformer";
+import { ValidateNested, IsEnum, IsString, IsNotEmptyObject } from "class-validator";
+
+import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
+
+class KeysDto {
+ @ApiProperty({
+ example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
+ })
+ @IsString()
+ accessToken!: string;
+
+ @ApiProperty({
+ example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
+ })
+ @IsString()
+ refreshToken!: string;
+}
+
+export class KeysResponseDto {
+ @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
+ @IsEnum([SUCCESS_STATUS, ERROR_STATUS])
+ status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
+
+ @ApiProperty({
+ type: KeysDto,
+ })
+ @IsNotEmptyObject()
+ @ValidateNested()
+ @Type(() => KeysDto)
+ data!: KeysDto;
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/guards/oauth-client-credentials/oauth-client-credentials.guard.spec.ts b/apps/api/v2/src/modules/oauth-clients/guards/oauth-client-credentials/oauth-client-credentials.guard.spec.ts
new file mode 100644
index 00000000000000..285f8661258050
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/guards/oauth-client-credentials/oauth-client-credentials.guard.spec.ts
@@ -0,0 +1,94 @@
+import { AppModule } from "@/app.module";
+import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
+import { createMock } from "@golevelup/ts-jest";
+import { ExecutionContext, UnauthorizedException } from "@nestjs/common";
+import { Test, TestingModule } from "@nestjs/testing";
+import { PlatformOAuthClient, Team } from "@prisma/client";
+import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
+import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
+
+import { X_CAL_SECRET_KEY } from "@calcom/platform-constants";
+
+import { OAuthClientCredentialsGuard } from "./oauth-client-credentials.guard";
+
+describe("OAuthClientCredentialsGuard", () => {
+ let guard: OAuthClientCredentialsGuard;
+ let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
+ let teamRepositoryFixture: TeamRepositoryFixture;
+ let oauthClient: PlatformOAuthClient;
+ let organization: Team;
+
+ beforeAll(async () => {
+ const module: TestingModule = await Test.createTestingModule({
+ imports: [AppModule, OAuthClientModule],
+ }).compile();
+
+ guard = module.get(OAuthClientCredentialsGuard);
+ teamRepositoryFixture = new TeamRepositoryFixture(module);
+ oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(module);
+
+ organization = await teamRepositoryFixture.create({ name: "organization" });
+
+ const data = {
+ logo: "logo-url",
+ name: "name",
+ redirectUris: ["redirect-uri"],
+ permissions: 32,
+ };
+ const secret = "secret";
+
+ oauthClient = await oauthClientRepositoryFixture.create(organization.id, data, secret);
+ });
+
+ it("should be defined", () => {
+ expect(guard).toBeDefined();
+ expect(oauthClient).toBeDefined();
+ });
+
+ it("should return true if client ID and secret are valid", async () => {
+ const mockContext = createMockExecutionContext(
+ { [X_CAL_SECRET_KEY]: oauthClient.secret },
+ { clientId: oauthClient.id }
+ );
+
+ await expect(guard.canActivate(mockContext)).resolves.toBe(true);
+ });
+
+ it("should return false if client ID is invalid", async () => {
+ const mockContext = createMockExecutionContext(
+ { [X_CAL_SECRET_KEY]: oauthClient.secret },
+ { clientId: "invalid id" }
+ );
+
+ await expect(guard.canActivate(mockContext)).rejects.toThrow(UnauthorizedException);
+ });
+
+ it("should return false if secret key is invalid", async () => {
+ const mockContext = createMockExecutionContext(
+ { [X_CAL_SECRET_KEY]: "invalid secret" },
+ { clientId: oauthClient.id }
+ );
+
+ await expect(guard.canActivate(mockContext)).rejects.toThrow(UnauthorizedException);
+ });
+
+ afterAll(async () => {
+ await oauthClientRepositoryFixture.delete(oauthClient.id);
+ await teamRepositoryFixture.delete(organization.id);
+ });
+
+ function createMockExecutionContext(
+ headers: Record,
+ params: Record
+ ): ExecutionContext {
+ return createMock({
+ switchToHttp: () => ({
+ getRequest: () => ({
+ headers,
+ params,
+ get: (headerName: string) => headers[headerName],
+ }),
+ }),
+ });
+ }
+});
diff --git a/apps/api/v2/src/modules/oauth-clients/guards/oauth-client-credentials/oauth-client-credentials.guard.ts b/apps/api/v2/src/modules/oauth-clients/guards/oauth-client-credentials/oauth-client-credentials.guard.ts
new file mode 100644
index 00000000000000..e58cdcba5c7fb5
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/guards/oauth-client-credentials/oauth-client-credentials.guard.ts
@@ -0,0 +1,33 @@
+import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
+import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common";
+import { Request } from "express";
+
+import { X_CAL_SECRET_KEY } from "@calcom/platform-constants";
+
+@Injectable()
+export class OAuthClientCredentialsGuard implements CanActivate {
+ constructor(private readonly oauthRepository: OAuthClientRepository) {}
+
+ async canActivate(context: ExecutionContext): Promise {
+ const request = context.switchToHttp().getRequest();
+ const { params } = request;
+
+ const oauthClientId = params.clientId;
+ const oauthClientSecret = request.get(X_CAL_SECRET_KEY);
+
+ if (!oauthClientId) {
+ throw new UnauthorizedException("Missing client ID");
+ }
+ if (!oauthClientSecret) {
+ throw new UnauthorizedException("Missing client secret");
+ }
+
+ const client = await this.oauthRepository.getOAuthClient(oauthClientId);
+
+ if (!client || client.secret !== oauthClientSecret) {
+ throw new UnauthorizedException("Invalid client credentials");
+ }
+
+ return true;
+ }
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/inputs/authorize.input.ts b/apps/api/v2/src/modules/oauth-clients/inputs/authorize.input.ts
new file mode 100644
index 00000000000000..62b7987310320a
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/inputs/authorize.input.ts
@@ -0,0 +1,6 @@
+import { IsString } from "class-validator";
+
+export class OAuthAuthorizeInput {
+ @IsString()
+ redirectUri!: string;
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/inputs/exchange-code.input.ts b/apps/api/v2/src/modules/oauth-clients/inputs/exchange-code.input.ts
new file mode 100644
index 00000000000000..938f8db08fe382
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/inputs/exchange-code.input.ts
@@ -0,0 +1,6 @@
+import { IsString } from "class-validator";
+
+export class ExchangeAuthorizationCodeInput {
+ @IsString()
+ clientSecret!: string;
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/inputs/refresh-token.input.ts b/apps/api/v2/src/modules/oauth-clients/inputs/refresh-token.input.ts
new file mode 100644
index 00000000000000..1eb25d3950a25c
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/inputs/refresh-token.input.ts
@@ -0,0 +1,6 @@
+import { IsString } from "class-validator";
+
+export class RefreshTokenInput {
+ @IsString()
+ refreshToken!: string;
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/inputs/update-oauth-client.input.ts b/apps/api/v2/src/modules/oauth-clients/inputs/update-oauth-client.input.ts
new file mode 100644
index 00000000000000..596651bdd46c0e
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/inputs/update-oauth-client.input.ts
@@ -0,0 +1,16 @@
+import { IsArray, IsOptional, IsString } from "class-validator";
+
+export class UpdateOAuthClientInput {
+ @IsOptional()
+ @IsString()
+ logo?: string;
+
+ @IsString()
+ @IsOptional()
+ name?: string;
+
+ @IsArray()
+ @IsOptional()
+ @IsString({ each: true })
+ redirectUris?: string[] = [];
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts b/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts
new file mode 100644
index 00000000000000..8debe05a7381e0
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/oauth-client.module.ts
@@ -0,0 +1,29 @@
+import { EventTypesModule } from "@/ee/event-types/event-types.module";
+import { AuthModule } from "@/modules/auth/auth.module";
+import { MembershipsModule } from "@/modules/memberships/memberships.module";
+import { OAuthClientUsersController } from "@/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller";
+import { OAuthClientsController } from "@/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller";
+import { OAuthFlowController } from "@/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller";
+import { OAuthClientCredentialsGuard } from "@/modules/oauth-clients/guards/oauth-client-credentials/oauth-client-credentials.guard";
+import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
+import { OAuthClientUsersService } from "@/modules/oauth-clients/services/oauth-clients-users.service";
+import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import { UsersModule } from "@/modules/users/users.module";
+import { Global, Module } from "@nestjs/common";
+
+@Global()
+@Module({
+ imports: [PrismaModule, AuthModule, UsersModule, MembershipsModule, EventTypesModule],
+ providers: [
+ OAuthClientRepository,
+ OAuthClientCredentialsGuard,
+ TokensRepository,
+ OAuthFlowService,
+ OAuthClientUsersService,
+ ],
+ controllers: [OAuthClientUsersController, OAuthClientsController, OAuthFlowController],
+ exports: [OAuthClientRepository, OAuthClientCredentialsGuard],
+})
+export class OAuthClientModule {}
diff --git a/apps/api/v2/src/modules/oauth-clients/oauth-client.repository.ts b/apps/api/v2/src/modules/oauth-clients/oauth-client.repository.ts
new file mode 100644
index 00000000000000..6dc399e2e4aead
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/oauth-client.repository.ts
@@ -0,0 +1,102 @@
+import { JwtService } from "@/modules/jwt/jwt.service";
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { Injectable } from "@nestjs/common";
+import type { PlatformOAuthClient } from "@prisma/client";
+
+import type { CreateOAuthClientInput } from "@calcom/platform-types";
+
+@Injectable()
+export class OAuthClientRepository {
+ constructor(
+ private readonly dbRead: PrismaReadService,
+ private readonly dbWrite: PrismaWriteService,
+ private jwtService: JwtService
+ ) {}
+
+ async createOAuthClient(organizationId: number, data: CreateOAuthClientInput) {
+ return this.dbWrite.prisma.platformOAuthClient.create({
+ data: {
+ ...data,
+ secret: this.jwtService.sign(data),
+ organizationId,
+ },
+ });
+ }
+
+ async getOAuthClient(clientId: string): Promise {
+ return this.dbRead.prisma.platformOAuthClient.findUnique({
+ where: { id: clientId },
+ });
+ }
+
+ async getOAuthClientWithAuthTokens(tokenId: string, clientId: string, clientSecret: string) {
+ return this.dbRead.prisma.platformOAuthClient.findUnique({
+ where: {
+ id: clientId,
+ secret: clientSecret,
+ authorizationTokens: {
+ some: {
+ id: tokenId,
+ },
+ },
+ },
+ include: {
+ authorizationTokens: {
+ where: {
+ id: tokenId,
+ },
+ include: {
+ owner: {
+ select: {
+ id: true,
+ },
+ },
+ },
+ },
+ },
+ });
+ }
+
+ async getOAuthClientWithRefreshSecret(clientId: string, clientSecret: string, refreshToken: string) {
+ return await this.dbRead.prisma.platformOAuthClient.findFirst({
+ where: {
+ id: clientId,
+ secret: clientSecret,
+ },
+ include: {
+ refreshToken: {
+ where: {
+ secret: refreshToken,
+ },
+ },
+ },
+ });
+ }
+
+ async getOrganizationOAuthClients(organizationId: number): Promise {
+ return this.dbRead.prisma.platformOAuthClient.findMany({
+ where: {
+ organization: {
+ id: organizationId,
+ },
+ },
+ });
+ }
+
+ async updateOAuthClient(
+ clientId: string,
+ updateData: Partial
+ ): Promise {
+ return this.dbWrite.prisma.platformOAuthClient.update({
+ where: { id: clientId },
+ data: updateData,
+ });
+ }
+
+ async deleteOAuthClient(clientId: string): Promise {
+ return this.dbWrite.prisma.platformOAuthClient.delete({
+ where: { id: clientId },
+ });
+ }
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts b/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts
new file mode 100644
index 00000000000000..7fc51ebf054489
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts
@@ -0,0 +1,83 @@
+import { EventTypesService } from "@/ee/event-types/services/event-types.service";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import { CreateUserInput } from "@/modules/users/inputs/create-user.input";
+import { UsersRepository } from "@/modules/users/users.repository";
+import { Injectable } from "@nestjs/common";
+import { User } from "@prisma/client";
+import * as crypto from "crypto";
+
+import { createNewUsersConnectToOrgIfExists } from "@calcom/platform-libraries";
+
+@Injectable()
+export class OAuthClientUsersService {
+ constructor(
+ private readonly userRepository: UsersRepository,
+ private readonly tokensRepository: TokensRepository,
+ private readonly eventTypesService: EventTypesService
+ ) {}
+
+ async createOauthClientUser(oAuthClientId: string, body: CreateUserInput, organizationId?: number) {
+ let user: User;
+ if (!organizationId) {
+ const username = generateShortHash(body.email, oAuthClientId);
+ user = await this.userRepository.create(body, username, oAuthClientId);
+ } else {
+ const [_, emailDomain] = body.email.split("@");
+ user = (
+ await createNewUsersConnectToOrgIfExists({
+ usernamesOrEmails: [body.email],
+ input: {
+ teamId: organizationId,
+ role: "MEMBER",
+ usernameOrEmail: [body.email],
+ isOrg: true,
+ language: "en",
+ },
+ parentId: null,
+ autoAcceptEmailDomain: emailDomain,
+ connectionInfoMap: {
+ [body.email]: {
+ orgId: organizationId,
+ autoAccept: true,
+ },
+ },
+ })
+ )[0];
+ await this.userRepository.addToOAuthClient(user.id, oAuthClientId);
+ await this.userRepository.update(user.id, { name: body.name ?? user.username ?? undefined });
+ }
+
+ const { accessToken, refreshToken } = await this.tokensRepository.createOAuthTokens(
+ oAuthClientId,
+ user.id
+ );
+ await this.eventTypesService.createUserDefaultEventTypes(user.id);
+
+ return {
+ user,
+ tokens: {
+ accessToken,
+ refreshToken,
+ },
+ };
+ }
+}
+
+function generateShortHash(email: string, clientId: string): string {
+ // Get the current timestamp
+ const timestamp = Date.now().toString();
+
+ // Concatenate the timestamp and email
+ const data = timestamp + email + clientId;
+
+ // Create a SHA256 hash
+ const hash = crypto
+ .createHash("sha256")
+ .update(data)
+ .digest("base64")
+ .replace("=", "")
+ .replace("/", "")
+ .replace("+", "");
+
+ return hash.toLowerCase();
+}
diff --git a/apps/api/v2/src/modules/oauth-clients/services/oauth-flow.service.ts b/apps/api/v2/src/modules/oauth-clients/services/oauth-flow.service.ts
new file mode 100644
index 00000000000000..a26e2bb7190cbf
--- /dev/null
+++ b/apps/api/v2/src/modules/oauth-clients/services/oauth-flow.service.ts
@@ -0,0 +1,117 @@
+import { TokenExpiredException } from "@/modules/auth/guards/access-token/token-expired.exception";
+import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import { BadRequestException, Injectable, Logger, UnauthorizedException } from "@nestjs/common";
+
+import { INVALID_ACCESS_TOKEN } from "@calcom/platform-constants";
+
+@Injectable()
+export class OAuthFlowService {
+ private logger = new Logger("OAuthFlowService");
+
+ constructor(
+ private readonly tokensRepository: TokensRepository,
+ private readonly oAuthClientRepository: OAuthClientRepository
+ ) //private readonly redisService: RedisIOService
+ {}
+
+ async propagateAccessToken(accessToken: string) {
+ // this.logger.log("Propagating access token to redis", accessToken);
+ // TODO propagate
+ //this.redisService.redis.hset("access_tokens", accessToken,)
+ return void 0;
+ }
+
+ async getOwnerId(accessToken: string) {
+ return this.tokensRepository.getAccessTokenOwnerId(accessToken);
+ }
+
+ async validateAccessToken(secret: string) {
+ // status can be "CACHE_HIT" or "CACHE_MISS", MISS will most likely mean the token has expired
+ // but we need to check the SQL db for it anyways.
+ const { status } = await this.readFromCache(secret);
+
+ if (status === "CACHE_HIT") {
+ return true;
+ }
+
+ const tokenExpiresAt = await this.tokensRepository.getAccessTokenExpiryDate(secret);
+
+ if (!tokenExpiresAt) {
+ throw new UnauthorizedException(INVALID_ACCESS_TOKEN);
+ }
+
+ if (new Date() > tokenExpiresAt) {
+ throw new TokenExpiredException();
+ }
+
+ return true;
+ }
+
+ private async readFromCache(secret: string) {
+ return { status: "CACHE_MISS" };
+ }
+
+ async exchangeAuthorizationToken(
+ tokenId: string,
+ clientId: string,
+ clientSecret: string
+ ): Promise<{ accessToken: string; refreshToken: string }> {
+ const oauthClient = await this.oAuthClientRepository.getOAuthClientWithAuthTokens(
+ tokenId,
+ clientId,
+ clientSecret
+ );
+
+ if (!oauthClient) {
+ throw new BadRequestException("Invalid OAuth Client.");
+ }
+
+ const authorizationToken = oauthClient.authorizationTokens[0];
+
+ if (!authorizationToken || !authorizationToken.owner.id) {
+ throw new BadRequestException("Invalid Authorization Token.");
+ }
+
+ const { accessToken, refreshToken } = await this.tokensRepository.createOAuthTokens(
+ clientId,
+ authorizationToken.owner.id
+ );
+ await this.tokensRepository.invalidateAuthorizationToken(authorizationToken.id);
+ void this.propagateAccessToken(accessToken); // voided as we don't need to await
+
+ return {
+ accessToken,
+ refreshToken,
+ };
+ }
+
+ async refreshToken(clientId: string, clientSecret: string, tokenSecret: string) {
+ const oauthClient = await this.oAuthClientRepository.getOAuthClientWithRefreshSecret(
+ clientId,
+ clientSecret,
+ tokenSecret
+ );
+
+ if (!oauthClient) {
+ throw new BadRequestException("Invalid OAuthClient credentials.");
+ }
+
+ const currentRefreshToken = oauthClient.refreshToken[0];
+
+ if (!currentRefreshToken) {
+ throw new BadRequestException("Invalid refresh token");
+ }
+
+ const { accessToken, refreshToken } = await this.tokensRepository.refreshOAuthTokens(
+ clientId,
+ currentRefreshToken.secret,
+ currentRefreshToken.userId
+ );
+
+ return {
+ accessToken: accessToken.secret,
+ refreshToken: refreshToken.secret,
+ };
+ }
+}
diff --git a/apps/api/v2/src/modules/prisma/prisma-read.service.ts b/apps/api/v2/src/modules/prisma/prisma-read.service.ts
new file mode 100644
index 00000000000000..2fbe34f7f44bdd
--- /dev/null
+++ b/apps/api/v2/src/modules/prisma/prisma-read.service.ts
@@ -0,0 +1,25 @@
+import type { OnModuleInit } from "@nestjs/common";
+import { Injectable } from "@nestjs/common";
+import { ConfigService } from "@nestjs/config";
+import { PrismaClient } from "@prisma/client";
+
+@Injectable()
+export class PrismaReadService implements OnModuleInit {
+ public prisma: PrismaClient;
+
+ constructor(private readonly configService: ConfigService) {
+ const dbUrl = configService.get("db.readUrl", { infer: true });
+
+ this.prisma = new PrismaClient({
+ datasources: {
+ db: {
+ url: dbUrl,
+ },
+ },
+ });
+ }
+
+ async onModuleInit() {
+ this.prisma.$connect();
+ }
+}
diff --git a/apps/api/v2/src/modules/prisma/prisma-write.service.ts b/apps/api/v2/src/modules/prisma/prisma-write.service.ts
new file mode 100644
index 00000000000000..abcc23273ce5c5
--- /dev/null
+++ b/apps/api/v2/src/modules/prisma/prisma-write.service.ts
@@ -0,0 +1,25 @@
+import { OnModuleInit } from "@nestjs/common";
+import { Injectable } from "@nestjs/common";
+import { ConfigService } from "@nestjs/config";
+import { PrismaClient } from "@prisma/client";
+
+@Injectable()
+export class PrismaWriteService implements OnModuleInit {
+ public prisma: PrismaClient;
+
+ constructor(private readonly configService: ConfigService) {
+ const dbUrl = configService.get("db.writeUrl", { infer: true });
+
+ this.prisma = new PrismaClient({
+ datasources: {
+ db: {
+ url: dbUrl,
+ },
+ },
+ });
+ }
+
+ async onModuleInit() {
+ this.prisma.$connect();
+ }
+}
diff --git a/apps/api/v2/src/modules/prisma/prisma.module.ts b/apps/api/v2/src/modules/prisma/prisma.module.ts
new file mode 100644
index 00000000000000..e9a66a5fc41da9
--- /dev/null
+++ b/apps/api/v2/src/modules/prisma/prisma.module.ts
@@ -0,0 +1,9 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { Module } from "@nestjs/common";
+
+@Module({
+ providers: [PrismaReadService, PrismaWriteService],
+ exports: [PrismaReadService, PrismaWriteService],
+})
+export class PrismaModule {}
diff --git a/apps/api/v2/src/modules/selected-calendars/selected-calendars.module.ts b/apps/api/v2/src/modules/selected-calendars/selected-calendars.module.ts
new file mode 100644
index 00000000000000..ef13910396d57f
--- /dev/null
+++ b/apps/api/v2/src/modules/selected-calendars/selected-calendars.module.ts
@@ -0,0 +1,9 @@
+import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [],
+ providers: [SelectedCalendarsRepository],
+ exports: [SelectedCalendarsRepository],
+})
+export class CredentialsModule {}
diff --git a/apps/api/v2/src/modules/selected-calendars/selected-calendars.repository.ts b/apps/api/v2/src/modules/selected-calendars/selected-calendars.repository.ts
new file mode 100644
index 00000000000000..a4a1bb5e7b44d2
--- /dev/null
+++ b/apps/api/v2/src/modules/selected-calendars/selected-calendars.repository.ts
@@ -0,0 +1,19 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { Injectable } from "@nestjs/common";
+
+@Injectable()
+export class SelectedCalendarsRepository {
+ constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
+
+ createSelectedCalendar(externalId: string, credentialId: number, userId: number, integration: string) {
+ return this.dbWrite.prisma.selectedCalendar.create({
+ data: {
+ userId,
+ externalId,
+ credentialId,
+ integration,
+ },
+ });
+ }
+}
diff --git a/apps/api/v2/src/modules/slots/controllers/slots.controller.ts b/apps/api/v2/src/modules/slots/controllers/slots.controller.ts
new file mode 100644
index 00000000000000..a75cf2d0722c52
--- /dev/null
+++ b/apps/api/v2/src/modules/slots/controllers/slots.controller.ts
@@ -0,0 +1,70 @@
+import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
+import { SlotsService } from "@/modules/slots/services/slots.service";
+import { Query, Body, Controller, Get, Delete, Post, Req, Res, UseGuards } from "@nestjs/common";
+import { Response as ExpressResponse, Request as ExpressRequest } from "express";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { getAvailableSlots } from "@calcom/platform-libraries";
+import type { AvailableSlotsType } from "@calcom/platform-libraries";
+import { RemoveSelectedSlotInput, ReserveSlotInput } from "@calcom/platform-types";
+import { ApiResponse, GetAvailableSlotsInput } from "@calcom/platform-types";
+
+@Controller({
+ path: "slots",
+ version: "2",
+})
+@UseGuards(AccessTokenGuard)
+export class SlotsController {
+ constructor(private readonly slotsService: SlotsService) {}
+
+ @Post("/reserve")
+ async reserveSlot(
+ @Body() body: ReserveSlotInput,
+ @Res({ passthrough: true }) res: ExpressResponse,
+ @Req() req: ExpressRequest
+ ): Promise> {
+ const uid = await this.slotsService.reserveSlot(body, req.cookies?.uid);
+
+ res.cookie("uid", uid);
+ return {
+ status: SUCCESS_STATUS,
+ data: uid,
+ };
+ }
+
+ @Delete("/selected-slot")
+ async deleteSelectedSlot(
+ @Query() params: RemoveSelectedSlotInput,
+ @Req() req: ExpressRequest
+ ): Promise {
+ const uid = req.cookies?.uid || params.uid;
+
+ await this.slotsService.deleteSelectedslot(uid);
+
+ return {
+ status: SUCCESS_STATUS,
+ };
+ }
+
+ @Get("/available")
+ async getAvailableSlots(
+ @Query() query: GetAvailableSlotsInput,
+ @Req() req: ExpressRequest
+ ): Promise> {
+ const isTeamEvent = await this.slotsService.checkIfIsTeamEvent(query.eventTypeId);
+ const availableSlots = await getAvailableSlots({
+ input: {
+ ...query,
+ isTeamEvent,
+ },
+ ctx: {
+ req,
+ },
+ });
+
+ return {
+ data: availableSlots,
+ status: "success",
+ };
+ }
+}
diff --git a/apps/api/v2/src/modules/slots/services/slots.service.ts b/apps/api/v2/src/modules/slots/services/slots.service.ts
new file mode 100644
index 00000000000000..1d17340ec8df52
--- /dev/null
+++ b/apps/api/v2/src/modules/slots/services/slots.service.ts
@@ -0,0 +1,57 @@
+import { EventTypesRepository } from "@/ee/event-types/event-types.repository";
+import { SlotsRepository } from "@/modules/slots/slots.repository";
+import { Injectable, NotFoundException } from "@nestjs/common";
+import { v4 as uuid } from "uuid";
+
+import { ReserveSlotInput } from "@calcom/platform-types";
+
+@Injectable()
+export class SlotsService {
+ constructor(
+ private readonly eventTypeRepo: EventTypesRepository,
+ private readonly slotsRepo: SlotsRepository
+ ) {}
+
+ async reserveSlot(input: ReserveSlotInput, headerUid?: string) {
+ const uid = headerUid || uuid();
+ const eventType = await this.eventTypeRepo.getEventTypeWithSeats(input.eventTypeId);
+ if (!eventType) {
+ throw new NotFoundException("Event Type not found");
+ }
+
+ let shouldReserveSlot = true;
+ if (eventType.seatsPerTimeSlot) {
+ const bookingWithAttendees = await this.slotsRepo.getBookingWithAttendees(input.bookingUid);
+ const bookingAttendeesLength = bookingWithAttendees?.attendees?.length;
+ if (bookingAttendeesLength) {
+ const seatsLeft = eventType.seatsPerTimeSlot - bookingAttendeesLength;
+ if (seatsLeft < 1) shouldReserveSlot = false;
+ } else {
+ shouldReserveSlot = false;
+ }
+ }
+
+ if (eventType && shouldReserveSlot) {
+ await Promise.all(
+ eventType.users.map((user) =>
+ this.slotsRepo.upsertSelectedSlot(user.id, input, uid, eventType.seatsPerTimeSlot !== null)
+ )
+ );
+ }
+
+ return uid;
+ }
+
+ async deleteSelectedslot(uid?: string) {
+ if (!uid) return;
+
+ return this.slotsRepo.deleteSelectedSlots(uid);
+ }
+
+ async checkIfIsTeamEvent(eventTypeId?: number) {
+ if (!eventTypeId) return false;
+
+ const event = await this.eventTypeRepo.getEventTypeById(eventTypeId);
+ return !!event?.teamId;
+ }
+}
diff --git a/apps/api/v2/src/modules/slots/slots.module.ts b/apps/api/v2/src/modules/slots/slots.module.ts
new file mode 100644
index 00000000000000..94e7c0d27fe2a9
--- /dev/null
+++ b/apps/api/v2/src/modules/slots/slots.module.ts
@@ -0,0 +1,14 @@
+import { EventTypesModule } from "@/ee/event-types/event-types.module";
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { SlotsController } from "@/modules/slots/controllers/slots.controller";
+import { SlotsService } from "@/modules/slots/services/slots.service";
+import { SlotsRepository } from "@/modules/slots/slots.repository";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule, EventTypesModule],
+ providers: [SlotsRepository, SlotsService],
+ controllers: [SlotsController],
+ exports: [SlotsService],
+})
+export class SlotsModule {}
diff --git a/apps/api/v2/src/modules/slots/slots.repository.ts b/apps/api/v2/src/modules/slots/slots.repository.ts
new file mode 100644
index 00000000000000..8ef589f9f87515
--- /dev/null
+++ b/apps/api/v2/src/modules/slots/slots.repository.ts
@@ -0,0 +1,53 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { Injectable } from "@nestjs/common";
+import { DateTime } from "luxon";
+
+import { MINUTES_TO_BOOK } from "@calcom/platform-libraries";
+import { ReserveSlotInput } from "@calcom/platform-types";
+
+@Injectable()
+export class SlotsRepository {
+ constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
+
+ async getBookingWithAttendees(bookingUid?: string) {
+ return this.dbRead.prisma.booking.findUnique({
+ where: { uid: bookingUid },
+ select: { attendees: true },
+ });
+ }
+
+ async upsertSelectedSlot(userId: number, input: ReserveSlotInput, uid: string, isSeat: boolean) {
+ const { slotUtcEndDate, slotUtcStartDate, eventTypeId } = input;
+
+ const releaseAt = DateTime.utc()
+ .plus({ minutes: parseInt(MINUTES_TO_BOOK) })
+ .toISO();
+ return this.dbWrite.prisma.selectedSlots.upsert({
+ where: {
+ selectedSlotUnique: { userId, slotUtcStartDate, slotUtcEndDate, uid },
+ },
+ update: {
+ slotUtcEndDate,
+ slotUtcStartDate,
+ releaseAt,
+ eventTypeId,
+ },
+ create: {
+ userId,
+ eventTypeId,
+ slotUtcStartDate,
+ slotUtcEndDate,
+ uid,
+ releaseAt,
+ isSeat,
+ },
+ });
+ }
+
+ async deleteSelectedSlots(uid: string) {
+ return this.dbWrite.prisma.selectedSlots.deleteMany({
+ where: { uid: { equals: uid } },
+ });
+ }
+}
diff --git a/apps/api/v2/src/modules/tokens/tokens.module.ts b/apps/api/v2/src/modules/tokens/tokens.module.ts
new file mode 100644
index 00000000000000..d700cc77541949
--- /dev/null
+++ b/apps/api/v2/src/modules/tokens/tokens.module.ts
@@ -0,0 +1,10 @@
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { TokensRepository } from "@/modules/tokens/tokens.repository";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule],
+ providers: [TokensRepository],
+ exports: [TokensRepository],
+})
+export class TokensModule {}
diff --git a/apps/api/v2/src/modules/tokens/tokens.repository.ts b/apps/api/v2/src/modules/tokens/tokens.repository.ts
new file mode 100644
index 00000000000000..f70fc570774d2f
--- /dev/null
+++ b/apps/api/v2/src/modules/tokens/tokens.repository.ts
@@ -0,0 +1,145 @@
+import { JwtService } from "@/modules/jwt/jwt.service";
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { Injectable } from "@nestjs/common";
+import { PlatformAuthorizationToken } from "@prisma/client";
+import { DateTime } from "luxon";
+
+@Injectable()
+export class TokensRepository {
+ constructor(
+ private readonly dbRead: PrismaReadService,
+ private readonly dbWrite: PrismaWriteService,
+ private readonly jwtService: JwtService
+ ) {}
+
+ async createAuthorizationToken(clientId: string, userId: number): Promise {
+ return this.dbWrite.prisma.platformAuthorizationToken.create({
+ data: {
+ client: {
+ connect: {
+ id: clientId,
+ },
+ },
+ owner: {
+ connect: {
+ id: userId,
+ },
+ },
+ },
+ });
+ }
+
+ async invalidateAuthorizationToken(tokenId: string) {
+ return this.dbWrite.prisma.platformAuthorizationToken.delete({
+ where: {
+ id: tokenId,
+ },
+ });
+ }
+
+ async getAuthorizationTokenByClientUserIds(clientId: string, userId: number) {
+ return this.dbRead.prisma.platformAuthorizationToken.findFirst({
+ where: {
+ platformOAuthClientId: clientId,
+ userId: userId,
+ },
+ });
+ }
+
+ async createOAuthTokens(clientId: string, ownerId: number) {
+ const accessExpiry = DateTime.now().plus({ minute: 1 }).startOf("minute").toJSDate();
+ const refreshExpiry = DateTime.now().plus({ year: 1 }).startOf("day").toJSDate();
+ const [accessToken, refreshToken] = await this.dbWrite.prisma.$transaction([
+ this.dbWrite.prisma.accessToken.create({
+ data: {
+ secret: this.jwtService.signAccessToken({ clientId, ownerId }),
+ expiresAt: accessExpiry,
+ client: { connect: { id: clientId } },
+ owner: { connect: { id: ownerId } },
+ },
+ }),
+ this.dbWrite.prisma.refreshToken.create({
+ data: {
+ secret: this.jwtService.signRefreshToken({ clientId, ownerId }),
+ expiresAt: refreshExpiry,
+ client: { connect: { id: clientId } },
+ owner: { connect: { id: ownerId } },
+ },
+ }),
+ ]);
+
+ return {
+ accessToken: accessToken.secret,
+ refreshToken: refreshToken.secret,
+ };
+ }
+
+ async getAccessTokenExpiryDate(accessTokenSecret: string) {
+ const accessToken = await this.dbRead.prisma.accessToken.findFirst({
+ where: {
+ secret: accessTokenSecret,
+ },
+ select: {
+ expiresAt: true,
+ },
+ });
+ return accessToken?.expiresAt;
+ }
+
+ async getAccessTokenOwnerId(accessTokenSecret: string) {
+ const accessToken = await this.dbRead.prisma.accessToken.findFirst({
+ where: {
+ secret: accessTokenSecret,
+ },
+ select: {
+ userId: true,
+ },
+ });
+
+ return accessToken?.userId;
+ }
+
+ async refreshOAuthTokens(clientId: string, refreshTokenSecret: string, tokenUserId: number) {
+ const accessExpiry = DateTime.now().plus({ minute: 1 }).startOf("minute").toJSDate();
+ const refreshExpiry = DateTime.now().plus({ year: 1 }).startOf("day").toJSDate();
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [_, _refresh, accessToken, refreshToken] = await this.dbWrite.prisma.$transaction([
+ this.dbWrite.prisma.accessToken.deleteMany({
+ where: { client: { id: clientId }, expiresAt: { lte: new Date() } },
+ }),
+ this.dbWrite.prisma.refreshToken.delete({ where: { secret: refreshTokenSecret } }),
+ this.dbWrite.prisma.accessToken.create({
+ data: {
+ secret: this.jwtService.signAccessToken({ clientId, userId: tokenUserId }),
+ expiresAt: accessExpiry,
+ client: { connect: { id: clientId } },
+ owner: { connect: { id: tokenUserId } },
+ },
+ }),
+ this.dbWrite.prisma.refreshToken.create({
+ data: {
+ secret: this.jwtService.signRefreshToken({ clientId, userId: tokenUserId }),
+ expiresAt: refreshExpiry,
+ client: { connect: { id: clientId } },
+ owner: { connect: { id: tokenUserId } },
+ },
+ }),
+ ]);
+ return { accessToken, refreshToken };
+ }
+
+ async getAccessTokenClient(accessToken: string) {
+ const token = await this.dbRead.prisma.accessToken.findFirst({
+ where: {
+ secret: accessToken,
+ },
+ select: {
+ client: true,
+ },
+ });
+
+ return token?.client;
+ }
+}
diff --git a/apps/api/v2/src/modules/users/inputs/create-user.input.ts b/apps/api/v2/src/modules/users/inputs/create-user.input.ts
new file mode 100644
index 00000000000000..72d8b91901dfa6
--- /dev/null
+++ b/apps/api/v2/src/modules/users/inputs/create-user.input.ts
@@ -0,0 +1,30 @@
+import { IsTimeFormat } from "@/modules/users/inputs/validators/is-time-format";
+import { IsWeekStart } from "@/modules/users/inputs/validators/is-week-start";
+import { IsNumber, IsOptional, IsTimeZone, IsString, Validate } from "class-validator";
+
+export class CreateUserInput {
+ @IsString()
+ email!: string;
+
+ @IsString()
+ @IsOptional()
+ name?: string;
+
+ @IsNumber()
+ @IsOptional()
+ @Validate(IsTimeFormat)
+ timeFormat?: number;
+
+ @IsNumber()
+ @IsOptional()
+ defaultScheduleId?: number;
+
+ @IsString()
+ @IsOptional()
+ @Validate(IsWeekStart)
+ weekStart?: string;
+
+ @IsTimeZone()
+ @IsOptional()
+ timeZone?: string;
+}
diff --git a/apps/api/v2/src/modules/users/inputs/update-user.input.ts b/apps/api/v2/src/modules/users/inputs/update-user.input.ts
new file mode 100644
index 00000000000000..db0caba2901632
--- /dev/null
+++ b/apps/api/v2/src/modules/users/inputs/update-user.input.ts
@@ -0,0 +1,31 @@
+import { IsTimeFormat } from "@/modules/users/inputs/validators/is-time-format";
+import { IsWeekStart } from "@/modules/users/inputs/validators/is-week-start";
+import { IsNumber, IsOptional, IsString, IsTimeZone, Validate } from "class-validator";
+
+export class UpdateUserInput {
+ @IsString()
+ @IsOptional()
+ email?: string;
+
+ @IsString()
+ @IsOptional()
+ name?: string;
+
+ @IsNumber()
+ @IsOptional()
+ @Validate(IsTimeFormat)
+ timeFormat?: number;
+
+ @IsNumber()
+ @IsOptional()
+ defaultScheduleId?: number;
+
+ @IsString()
+ @IsOptional()
+ @Validate(IsWeekStart)
+ weekStart?: string;
+
+ @IsTimeZone()
+ @IsOptional()
+ timeZone?: string;
+}
diff --git a/apps/api/v2/src/modules/users/inputs/validators/is-time-format.ts b/apps/api/v2/src/modules/users/inputs/validators/is-time-format.ts
new file mode 100644
index 00000000000000..2271a7e4f74420
--- /dev/null
+++ b/apps/api/v2/src/modules/users/inputs/validators/is-time-format.ts
@@ -0,0 +1,12 @@
+import { ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";
+
+@ValidatorConstraint({ name: "isTimeFormat", async: false })
+export class IsTimeFormat implements ValidatorConstraintInterface {
+ validate(timeFormat: number) {
+ return timeFormat === 12 || timeFormat === 24;
+ }
+
+ defaultMessage() {
+ return "timeFormat must be a number either 12 or 24";
+ }
+}
diff --git a/apps/api/v2/src/modules/users/inputs/validators/is-week-start.ts b/apps/api/v2/src/modules/users/inputs/validators/is-week-start.ts
new file mode 100644
index 00000000000000..d21cf1271910ec
--- /dev/null
+++ b/apps/api/v2/src/modules/users/inputs/validators/is-week-start.ts
@@ -0,0 +1,23 @@
+import { ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";
+
+@ValidatorConstraint({ name: "isWeekStart", async: false })
+export class IsWeekStart implements ValidatorConstraintInterface {
+ validate(weekStart: string) {
+ if (!weekStart) return false;
+
+ const lowerCaseWeekStart = weekStart.toLowerCase();
+ return (
+ lowerCaseWeekStart === "monday" ||
+ lowerCaseWeekStart === "tuesday" ||
+ lowerCaseWeekStart === "wednesday" ||
+ lowerCaseWeekStart === "thursday" ||
+ lowerCaseWeekStart === "friday" ||
+ lowerCaseWeekStart === "saturday" ||
+ lowerCaseWeekStart === "sunday"
+ );
+ }
+
+ defaultMessage() {
+ return "weekStart must be a string either Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, or Sunday";
+ }
+}
diff --git a/apps/api/v2/src/modules/users/users.module.ts b/apps/api/v2/src/modules/users/users.module.ts
new file mode 100644
index 00000000000000..932f4e0ea9c606
--- /dev/null
+++ b/apps/api/v2/src/modules/users/users.module.ts
@@ -0,0 +1,10 @@
+import { PrismaModule } from "@/modules/prisma/prisma.module";
+import { UsersRepository } from "@/modules/users/users.repository";
+import { Module } from "@nestjs/common";
+
+@Module({
+ imports: [PrismaModule],
+ providers: [UsersRepository],
+ exports: [UsersRepository],
+})
+export class UsersModule {}
diff --git a/apps/api/v2/src/modules/users/users.repository.ts b/apps/api/v2/src/modules/users/users.repository.ts
new file mode 100644
index 00000000000000..1557dfe00aab36
--- /dev/null
+++ b/apps/api/v2/src/modules/users/users.repository.ts
@@ -0,0 +1,127 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { CreateUserInput } from "@/modules/users/inputs/create-user.input";
+import { UpdateUserInput } from "@/modules/users/inputs/update-user.input";
+import { Injectable } from "@nestjs/common";
+import type { Profile, User } from "@prisma/client";
+
+export type UserWithProfile = User & {
+ movedToProfile?: Profile | null;
+};
+
+@Injectable()
+export class UsersRepository {
+ constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
+
+ async create(user: CreateUserInput, username: string, oAuthClientId: string) {
+ this.formatInput(user);
+
+ return this.dbRead.prisma.user.create({
+ data: {
+ ...user,
+ username,
+ platformOAuthClients: {
+ connect: { id: oAuthClientId },
+ },
+ },
+ });
+ }
+
+ async addToOAuthClient(userId: number, oAuthClientId: string) {
+ return this.dbRead.prisma.user.update({
+ data: {
+ platformOAuthClients: {
+ connect: { id: oAuthClientId },
+ },
+ },
+ where: { id: userId },
+ });
+ }
+
+ async findById(userId: number) {
+ return this.dbRead.prisma.user.findUnique({
+ where: {
+ id: userId,
+ },
+ });
+ }
+
+ async findByIdWithProfile(userId: number): Promise {
+ return this.dbRead.prisma.user.findUnique({
+ where: {
+ id: userId,
+ },
+ include: {
+ movedToProfile: true,
+ },
+ });
+ }
+
+ async findByIdWithCalendars(userId: number) {
+ return this.dbRead.prisma.user.findUnique({
+ where: {
+ id: userId,
+ },
+ include: {
+ selectedCalendars: true,
+ destinationCalendar: true,
+ },
+ });
+ }
+
+ async findByEmail(email: string) {
+ return this.dbRead.prisma.user.findUnique({
+ where: {
+ email,
+ },
+ });
+ }
+
+ async update(userId: number, updateData: UpdateUserInput) {
+ this.formatInput(updateData);
+
+ return this.dbWrite.prisma.user.update({
+ where: { id: userId },
+ data: updateData,
+ });
+ }
+
+ async delete(userId: number): Promise {
+ return this.dbWrite.prisma.user.delete({
+ where: { id: userId },
+ });
+ }
+
+ formatInput(userInput: CreateUserInput | UpdateUserInput) {
+ if (userInput.weekStart) {
+ userInput.weekStart = capitalize(userInput.weekStart);
+ }
+
+ if (userInput.timeZone) {
+ userInput.timeZone = capitalizeTimezone(userInput.timeZone);
+ }
+ }
+
+ setDefaultSchedule(userId: number, scheduleId: number) {
+ return this.dbWrite.prisma.user.update({
+ where: { id: userId },
+ data: {
+ defaultScheduleId: scheduleId,
+ },
+ });
+ }
+}
+
+function capitalizeTimezone(timezone: string) {
+ const segments = timezone.split("/");
+
+ const capitalizedSegments = segments.map((segment) => {
+ return capitalize(segment);
+ });
+
+ return capitalizedSegments.join("/");
+}
+
+function capitalize(str: string) {
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
+}
diff --git a/apps/api/v2/swagger/copy-swagger-module.ts b/apps/api/v2/swagger/copy-swagger-module.ts
new file mode 100644
index 00000000000000..8f6f23d3f382ff
--- /dev/null
+++ b/apps/api/v2/swagger/copy-swagger-module.ts
@@ -0,0 +1,28 @@
+import * as fs from "fs-extra";
+import * as path from "path";
+
+// First, copyNestSwagger is required to enable "@nestjs/swagger" in the "nest-cli.json",
+// because nest-cli.json is resolving "@nestjs/swagger" plugin from
+// project's node_modules, but due to dependency hoisting, the "@nestjs/swagger" is located in the root node_modules.
+// Second, we need to run this before starting the application using "nest start", because "nest start" is ran by
+// "nest-cli" with the "nest-cli.json" file, and for nest cli to be loaded with plugins correctly the "@nestjs/swagger"
+// should reside in the project's node_modules already before the "nest start" command is executed.
+async function copyNestSwagger() {
+ const monorepoRoot = path.resolve(__dirname, "../../../../");
+ const nodeModulesNestjs = path.resolve(__dirname, "../node_modules/@nestjs");
+ const swaggerModulePath = "@nestjs/swagger";
+
+ const sourceDir = path.join(monorepoRoot, "node_modules", swaggerModulePath);
+ const targetDir = path.join(nodeModulesNestjs, "swagger");
+
+ if (!(await fs.pathExists(targetDir))) {
+ try {
+ await fs.ensureDir(nodeModulesNestjs);
+ await fs.copy(sourceDir, targetDir);
+ } catch (error) {
+ console.error("Failed to copy @nestjs/swagger:", error);
+ }
+ }
+}
+
+copyNestSwagger();
diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json
new file mode 100644
index 00000000000000..7f3441eebe9329
--- /dev/null
+++ b/apps/api/v2/swagger/documentation.json
@@ -0,0 +1,1403 @@
+{
+ "openapi": "3.0.0",
+ "paths": {
+ "/health": {
+ "get": {
+ "operationId": "AppController_getHealth",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/events/public": {
+ "get": {
+ "operationId": "EventsController_getPublicEvent",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/oauth-clients/{clientId}/users": {
+ "post": {
+ "operationId": "OAuthClientUsersController_createUser",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateUserInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/oauth-clients/{clientId}/users/{userId}": {
+ "get": {
+ "operationId": "OAuthClientUsersController_getUserById",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "userId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "operationId": "OAuthClientUsersController_updateUser",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "userId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "number"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateUserInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "operationId": "OAuthClientUsersController_deleteUser",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "userId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/oauth-clients": {
+ "post": {
+ "operationId": "OAuthClientsController_createOAuthClient",
+ "summary": "",
+ "description": "â ïž First, this endpoint requires `Cookie: next-auth.session-token=eyJhbGciOiJ` header. Log into Cal web app using owner of organization that was created after visiting `/settings/organizations/new`, refresh swagger docs, and the cookie will be added to requests automatically to pass the NextAuthGuard.\nSecond, make sure that the logged in user has organizationId set to pass the OrganizationRolesGuard guard.",
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateOAuthClientInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Create an OAuth client",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateOAuthClientResponseDto"
+ }
+ }
+ }
+ }
+ },
+ "tags": ["Development only"]
+ },
+ "get": {
+ "operationId": "OAuthClientsController_getOAuthClients",
+ "summary": "",
+ "description": "â ïž First, this endpoint requires `Cookie: next-auth.session-token=eyJhbGciOiJ` header. Log into Cal web app using owner of organization that was created after visiting `/settings/organizations/new`, refresh swagger docs, and the cookie will be added to requests automatically to pass the NextAuthGuard.\nSecond, make sure that the logged in user has organizationId set to pass the OrganizationRolesGuard guard.",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/GetOAuthClientsResponseDto"
+ }
+ }
+ }
+ }
+ },
+ "tags": ["Development only"]
+ }
+ },
+ "/api/v2/oauth-clients/{clientId}": {
+ "get": {
+ "operationId": "OAuthClientsController_getOAuthClientById",
+ "summary": "",
+ "description": "â ïž First, this endpoint requires `Cookie: next-auth.session-token=eyJhbGciOiJ` header. Log into Cal web app using owner of organization that was created after visiting `/settings/organizations/new`, refresh swagger docs, and the cookie will be added to requests automatically to pass the NextAuthGuard.\nSecond, make sure that the logged in user has organizationId set to pass the OrganizationRolesGuard guard.",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/GetOAuthClientResponseDto"
+ }
+ }
+ }
+ }
+ },
+ "tags": ["Development only"]
+ },
+ "patch": {
+ "operationId": "OAuthClientsController_updateOAuthClient",
+ "summary": "",
+ "description": "â ïž First, this endpoint requires `Cookie: next-auth.session-token=eyJhbGciOiJ` header. Log into Cal web app using owner of organization that was created after visiting `/settings/organizations/new`, refresh swagger docs, and the cookie will be added to requests automatically to pass the NextAuthGuard.\nSecond, make sure that the logged in user has organizationId set to pass the OrganizationRolesGuard guard.",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateOAuthClientInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/GetOAuthClientResponseDto"
+ }
+ }
+ }
+ }
+ },
+ "tags": ["Development only"]
+ },
+ "delete": {
+ "operationId": "OAuthClientsController_deleteOAuthClient",
+ "summary": "",
+ "description": "â ïž First, this endpoint requires `Cookie: next-auth.session-token=eyJhbGciOiJ` header. Log into Cal web app using owner of organization that was created after visiting `/settings/organizations/new`, refresh swagger docs, and the cookie will be added to requests automatically to pass the NextAuthGuard.\nSecond, make sure that the logged in user has organizationId set to pass the OrganizationRolesGuard guard.",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/GetOAuthClientResponseDto"
+ }
+ }
+ }
+ }
+ },
+ "tags": ["Development only"]
+ }
+ },
+ "/api/v2/oauth/{clientId}/authorize": {
+ "post": {
+ "operationId": "OAuthFlowController_authorize",
+ "summary": "Authorize an OAuth client",
+ "description": "Redirects the user to the specified 'redirect_uri' with an authorization code in query parameter if the client is authorized successfully. The code is then exchanged for access and refresh tokens via the `/exchange` endpoint.",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OAuthAuthorizeInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "The user is redirected to the 'redirect_uri' with an authorization code in query parameter e.g. `redirectUri?code=secretcode.`"
+ },
+ "400": {
+ "description": "Bad request if the OAuth client is not found, if the redirect URI is invalid, or if the user has already authorized the client."
+ }
+ },
+ "tags": ["Development only"]
+ }
+ },
+ "/api/v2/oauth/{clientId}/exchange": {
+ "post": {
+ "operationId": "OAuthFlowController_exchange",
+ "summary": "Exchange authorization code for access tokens",
+ "description": "Exchanges the authorization code received from the `/authorize` endpoint for access and refresh tokens. The authorization code should be provided in the 'Authorization' header prefixed with 'Bearer '.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "required": true,
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ExchangeAuthorizationCodeInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successfully exchanged authorization code for access and refresh tokens.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/KeysResponseDto"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad request if the authorization code is missing, invalid, or if the client ID and secret do not match."
+ }
+ },
+ "tags": ["Development only"]
+ }
+ },
+ "/api/v2/oauth/{clientId}/refresh": {
+ "post": {
+ "operationId": "OAuthFlowController_refreshAccessToken",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "x-cal-secret-key",
+ "required": true,
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RefreshTokenInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/KeysResponseDto"
+ }
+ }
+ }
+ }
+ },
+ "tags": ["Development only"]
+ }
+ },
+ "/api/v2/event-types": {
+ "post": {
+ "operationId": "EventTypesController_createEventType",
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateEventTypeInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/event-types/{eventTypeId}": {
+ "get": {
+ "operationId": "EventTypesController_getEventType",
+ "parameters": [
+ {
+ "name": "eventTypeId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/platform/gcal/oauth/auth-url": {
+ "get": {
+ "operationId": "GcalController_redirect",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "required": true,
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/platform/gcal/oauth/save": {
+ "get": {
+ "operationId": "GcalController_save",
+ "parameters": [
+ {
+ "name": "state",
+ "required": true,
+ "in": "query",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "code",
+ "required": true,
+ "in": "query",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/platform/gcal/check": {
+ "get": {
+ "operationId": "GcalController_check",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/platform/provider/{clientId}": {
+ "get": {
+ "operationId": "CalProviderController_verifyClientId",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/platform/provider/{clientId}/access-token": {
+ "get": {
+ "operationId": "CalProviderController_verifyAccessToken",
+ "parameters": [
+ {
+ "name": "clientId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/schedules": {
+ "post": {
+ "operationId": "SchedulesController_createSchedule",
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateScheduleInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "operationId": "SchedulesController_getSchedules",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/schedules/default": {
+ "get": {
+ "operationId": "SchedulesController_getDefaultSchedule",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/schedules/time-zones": {
+ "get": {
+ "operationId": "SchedulesController_getTimeZones",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/schedules/{scheduleId}": {
+ "get": {
+ "operationId": "SchedulesController_getSchedule",
+ "parameters": [
+ {
+ "name": "scheduleId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "operationId": "SchedulesController_updateSchedule",
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateScheduleInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "operationId": "SchedulesController_deleteSchedule",
+ "parameters": [
+ {
+ "name": "scheduleId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/me": {
+ "get": {
+ "operationId": "MeController_getMe",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "operationId": "MeController_updateMe",
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateUserInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/ee/calendars/busy-times": {
+ "get": {
+ "operationId": "CalendarsController_getBusyTimes",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/ee/calendars": {
+ "get": {
+ "operationId": "CalendarsController_getCalendars",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/ee/bookings": {
+ "post": {
+ "operationId": "BookingsController_createBooking",
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateBookingInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/ee/bookings/reccuring": {
+ "post": {
+ "operationId": "BookingsController_createReccuringBooking",
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/ee/bookings/instant": {
+ "post": {
+ "operationId": "BookingsController_createInstantBooking",
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateBookingInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/slots/reserve": {
+ "post": {
+ "operationId": "SlotsController_reserveSlot",
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReserveSlotInput"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/slots/selected-slot": {
+ "delete": {
+ "operationId": "SlotsController_deleteSelectedSlot",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/slots/available": {
+ "get": {
+ "operationId": "SlotsController_getAvailableSlots",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "info": {
+ "title": "Cal.com v2 API",
+ "description": "",
+ "version": "1.0.0",
+ "contact": {}
+ },
+ "tags": [],
+ "servers": [],
+ "components": {
+ "schemas": {
+ "CreateUserInput": {
+ "type": "object",
+ "properties": {
+ "email": {
+ "type": "string"
+ },
+ "timeFormat": {
+ "type": "number"
+ },
+ "defaultScheduleId": {
+ "type": "number"
+ },
+ "weekStart": {
+ "type": "string"
+ },
+ "timeZone": {
+ "type": "string"
+ }
+ },
+ "required": ["email"]
+ },
+ "UpdateUserInput": {
+ "type": "object",
+ "properties": {
+ "email": {
+ "type": "string"
+ },
+ "timeFormat": {
+ "type": "number"
+ },
+ "defaultScheduleId": {
+ "type": "number"
+ },
+ "weekStart": {
+ "type": "string"
+ },
+ "timeZone": {
+ "type": "string"
+ }
+ }
+ },
+ "CreateOAuthClientInput": {
+ "type": "object",
+ "properties": {}
+ },
+ "DataDto": {
+ "type": "object",
+ "properties": {
+ "clientId": {
+ "type": "string",
+ "example": "clsx38nbl0001vkhlwin9fmt0"
+ },
+ "clientSecret": {
+ "type": "string",
+ "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoib2F1dGgtY2xpZW50Iiwi"
+ }
+ },
+ "required": ["clientId", "clientSecret"]
+ },
+ "CreateOAuthClientResponseDto": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string",
+ "example": "success",
+ "enum": ["success", "error"]
+ },
+ "data": {
+ "example": {
+ "clientId": "clsx38nbl0001vkhlwin9fmt0",
+ "clientSecret": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoib2F1dGgtY2xpZW50Iiwi"
+ },
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/DataDto"
+ }
+ ]
+ }
+ },
+ "required": ["status", "data"]
+ },
+ "PlatformOAuthClientDto": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "example": "clsx38nbl0001vkhlwin9fmt0"
+ },
+ "name": {
+ "type": "string",
+ "example": "MyClient"
+ },
+ "secret": {
+ "type": "string",
+ "example": "secretValue"
+ },
+ "permissions": {
+ "type": "number",
+ "example": 3
+ },
+ "logo": {
+ "type": "object",
+ "example": "https://example.com/logo.png"
+ },
+ "redirectUris": {
+ "example": ["https://example.com/callback"],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "organizationId": {
+ "type": "number",
+ "example": 1
+ },
+ "createdAt": {
+ "format": "date-time",
+ "type": "string",
+ "example": "2024-03-08T14:10:38.418Z"
+ }
+ },
+ "required": ["id", "name", "secret", "permissions", "redirectUris", "organizationId", "createdAt"]
+ },
+ "GetOAuthClientsResponseDto": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string",
+ "example": "success",
+ "enum": ["success", "error"]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/PlatformOAuthClientDto"
+ }
+ }
+ },
+ "required": ["status", "data"]
+ },
+ "GetOAuthClientResponseDto": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string",
+ "example": "success",
+ "enum": ["success", "error"]
+ },
+ "data": {
+ "$ref": "#/components/schemas/PlatformOAuthClientDto"
+ }
+ },
+ "required": ["status", "data"]
+ },
+ "UpdateOAuthClientInput": {
+ "type": "object",
+ "properties": {
+ "logo": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "redirectUris": {
+ "default": [],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "OAuthAuthorizeInput": {
+ "type": "object",
+ "properties": {
+ "redirectUri": {
+ "type": "string"
+ }
+ },
+ "required": ["redirectUri"]
+ },
+ "ExchangeAuthorizationCodeInput": {
+ "type": "object",
+ "properties": {
+ "clientSecret": {
+ "type": "string"
+ }
+ },
+ "required": ["clientSecret"]
+ },
+ "KeysDto": {
+ "type": "object",
+ "properties": {
+ "accessToken": {
+ "type": "string",
+ "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
+ },
+ "refreshToken": {
+ "type": "string",
+ "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
+ }
+ },
+ "required": ["accessToken", "refreshToken"]
+ },
+ "KeysResponseDto": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string",
+ "example": "success",
+ "enum": ["success", "error"]
+ },
+ "data": {
+ "$ref": "#/components/schemas/KeysDto"
+ }
+ },
+ "required": ["status", "data"]
+ },
+ "RefreshTokenInput": {
+ "type": "object",
+ "properties": {
+ "refreshToken": {
+ "type": "string"
+ }
+ },
+ "required": ["refreshToken"]
+ },
+ "CreateEventTypeInput": {
+ "type": "object",
+ "properties": {
+ "length": {
+ "type": "number",
+ "minimum": 1
+ },
+ "slug": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ }
+ },
+ "required": ["length", "slug", "title"]
+ },
+ "CreateAvailabilityInput": {
+ "type": "object",
+ "properties": {
+ "days": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ },
+ "startTime": {
+ "format": "date-time",
+ "type": "string"
+ },
+ "endTime": {
+ "format": "date-time",
+ "type": "string"
+ }
+ },
+ "required": ["days", "startTime", "endTime"]
+ },
+ "CreateScheduleInput": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "timeZone": {
+ "type": "string"
+ },
+ "availabilities": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CreateAvailabilityInput"
+ }
+ },
+ "isDefault": {
+ "type": "object",
+ "default": true
+ }
+ },
+ "required": ["name", "timeZone", "isDefault"]
+ },
+ "UpdateScheduleInput": {
+ "type": "object",
+ "properties": {}
+ },
+ "CreateBookingInput": {
+ "type": "object",
+ "properties": {
+ "end": {
+ "type": "string"
+ },
+ "start": {
+ "type": "string"
+ },
+ "eventTypeId": {
+ "type": "number"
+ },
+ "eventTypeSlug": {
+ "type": "string"
+ },
+ "rescheduleUid": {
+ "type": "string"
+ },
+ "recurringEventId": {
+ "type": "string"
+ },
+ "timeZone": {
+ "type": "string"
+ },
+ "user": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "language": {
+ "type": "string"
+ },
+ "bookingUid": {
+ "type": "string"
+ },
+ "metadata": {
+ "type": "object"
+ },
+ "hasHashedBookingLink": {
+ "type": "boolean"
+ },
+ "hashedLink": {
+ "type": "string",
+ "nullable": true
+ },
+ "seatReferenceUid": {
+ "type": "string"
+ }
+ },
+ "required": ["start", "eventTypeId", "timeZone", "language", "metadata", "hashedLink"]
+ },
+ "ReserveSlotInput": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ }
+}
diff --git a/apps/api/v2/test/fixtures/repository/credentials.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/credentials.repository.fixture.ts
new file mode 100644
index 00000000000000..449d1124ec2bd6
--- /dev/null
+++ b/apps/api/v2/test/fixtures/repository/credentials.repository.fixture.ts
@@ -0,0 +1,33 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { TestingModule } from "@nestjs/testing";
+import { Prisma } from "@prisma/client";
+
+export class CredentialsRepositoryFixture {
+ private primaReadClient: PrismaReadService["prisma"];
+ private prismaWriteClient: PrismaWriteService["prisma"];
+
+ constructor(private readonly module: TestingModule) {
+ this.primaReadClient = module.get(PrismaReadService).prisma;
+ this.prismaWriteClient = module.get(PrismaWriteService).prisma;
+ }
+
+ create(type: string, key: Prisma.InputJsonValue, userId: number, appId: string) {
+ return this.prismaWriteClient.credential.create({
+ data: {
+ type,
+ key,
+ userId,
+ appId,
+ },
+ });
+ }
+
+ delete(id: number) {
+ return this.prismaWriteClient.credential.delete({
+ where: {
+ id,
+ },
+ });
+ }
+}
diff --git a/apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts
new file mode 100644
index 00000000000000..03e98a4dd4e3ca
--- /dev/null
+++ b/apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts
@@ -0,0 +1,36 @@
+import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input";
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { TestingModule } from "@nestjs/testing";
+import { EventType } from "@prisma/client";
+
+export class EventTypesRepositoryFixture {
+ private primaReadClient: PrismaReadService["prisma"];
+ private prismaWriteClient: PrismaWriteService["prisma"];
+
+ constructor(private readonly module: TestingModule) {
+ this.primaReadClient = module.get(PrismaReadService).prisma;
+ this.prismaWriteClient = module.get(PrismaWriteService).prisma;
+ }
+
+ async getAllUserEventTypes(userId: number) {
+ return this.prismaWriteClient.eventType.findMany({
+ where: {
+ userId,
+ },
+ });
+ }
+
+ async create(data: CreateEventTypeInput, userId: number) {
+ return this.prismaWriteClient.eventType.create({
+ data: {
+ ...data,
+ userId,
+ },
+ });
+ }
+
+ async delete(eventTypeId: EventType["id"]) {
+ return this.prismaWriteClient.eventType.delete({ where: { id: eventTypeId } });
+ }
+}
diff --git a/apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts
new file mode 100644
index 00000000000000..21ee461df0f66e
--- /dev/null
+++ b/apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts
@@ -0,0 +1,34 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { TestingModule } from "@nestjs/testing";
+import { Membership, MembershipRole, Prisma, Team, User } from "@prisma/client";
+
+export class MembershipRepositoryFixture {
+ private primaReadClient: PrismaReadService["prisma"];
+ private prismaWriteClient: PrismaWriteService["prisma"];
+
+ constructor(private readonly module: TestingModule) {
+ this.primaReadClient = module.get(PrismaReadService).prisma;
+ this.prismaWriteClient = module.get(PrismaWriteService).prisma;
+ }
+
+ async create(data: Prisma.MembershipCreateInput) {
+ return this.prismaWriteClient.membership.create({ data });
+ }
+
+ async delete(membershipId: Membership["id"]) {
+ return this.prismaWriteClient.membership.delete({ where: { id: membershipId } });
+ }
+
+ async get(membershipId: Membership["id"]) {
+ return this.primaReadClient.membership.findFirst({ where: { id: membershipId } });
+ }
+
+ async addUserToOrg(user: User, org: Team, role: MembershipRole, accepted: boolean) {
+ const membership = await this.prismaWriteClient.membership.create({
+ data: { teamId: org.id, userId: user.id, role, accepted },
+ });
+ await this.prismaWriteClient.user.update({ where: { id: user.id }, data: { organizationId: org.id } });
+ return membership;
+ }
+}
diff --git a/apps/api/v2/test/fixtures/repository/oauth-client.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/oauth-client.repository.fixture.ts
new file mode 100644
index 00000000000000..4aeb2868b041dc
--- /dev/null
+++ b/apps/api/v2/test/fixtures/repository/oauth-client.repository.fixture.ts
@@ -0,0 +1,49 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { TestingModule } from "@nestjs/testing";
+import { PlatformOAuthClient } from "@prisma/client";
+
+import { CreateOAuthClientInput } from "@calcom/platform-types";
+
+export class OAuthClientRepositoryFixture {
+ private prismaReadClient: PrismaReadService["prisma"];
+ private prismaWriteClient: PrismaWriteService["prisma"];
+
+ constructor(private readonly module: TestingModule) {
+ this.prismaReadClient = module.get(PrismaReadService).prisma;
+ this.prismaWriteClient = module.get(PrismaWriteService).prisma;
+ }
+
+ async get(clientId: PlatformOAuthClient["id"]) {
+ return this.prismaReadClient.platformOAuthClient.findFirst({ where: { id: clientId } });
+ }
+
+ async getUsers(clientId: PlatformOAuthClient["id"]) {
+ const response = await this.prismaReadClient.platformOAuthClient.findFirst({
+ where: { id: clientId },
+ include: {
+ users: true,
+ },
+ });
+
+ return response?.users;
+ }
+
+ async create(organizationId: number, data: CreateOAuthClientInput, secret: string) {
+ return this.prismaWriteClient.platformOAuthClient.create({
+ data: {
+ ...data,
+ secret,
+ organizationId,
+ },
+ });
+ }
+
+ async delete(clientId: PlatformOAuthClient["id"]) {
+ return this.prismaWriteClient.platformOAuthClient.delete({ where: { id: clientId } });
+ }
+
+ async deleteByClientId(clientId: PlatformOAuthClient["id"]) {
+ return this.prismaWriteClient.platformOAuthClient.delete({ where: { id: clientId } });
+ }
+}
diff --git a/apps/api/v2/test/fixtures/repository/schedules.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/schedules.repository.fixture.ts
new file mode 100644
index 00000000000000..f34be13c9f236b
--- /dev/null
+++ b/apps/api/v2/test/fixtures/repository/schedules.repository.fixture.ts
@@ -0,0 +1,26 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { TestingModule } from "@nestjs/testing";
+import { Schedule } from "@prisma/client";
+
+export class SchedulesRepositoryFixture {
+ private primaReadClient: PrismaReadService["prisma"];
+ private prismaWriteClient: PrismaWriteService["prisma"];
+
+ constructor(private readonly module: TestingModule) {
+ this.primaReadClient = module.get(PrismaReadService).prisma;
+ this.prismaWriteClient = module.get(PrismaWriteService).prisma;
+ }
+
+ async getById(scheduleId: Schedule["id"]) {
+ return this.primaReadClient.schedule.findFirst({ where: { id: scheduleId } });
+ }
+
+ async deleteById(scheduleId: Schedule["id"]) {
+ return this.prismaWriteClient.schedule.delete({ where: { id: scheduleId } });
+ }
+
+ async deleteAvailabilities(scheduleId: Schedule["id"]) {
+ return this.prismaWriteClient.availability.deleteMany({ where: { scheduleId } });
+ }
+}
diff --git a/apps/api/v2/test/fixtures/repository/team.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/team.repository.fixture.ts
new file mode 100644
index 00000000000000..1d059087318298
--- /dev/null
+++ b/apps/api/v2/test/fixtures/repository/team.repository.fixture.ts
@@ -0,0 +1,26 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { TestingModule } from "@nestjs/testing";
+import { Prisma, Team } from "@prisma/client";
+
+export class TeamRepositoryFixture {
+ private primaReadClient: PrismaReadService["prisma"];
+ private prismaWriteClient: PrismaWriteService["prisma"];
+
+ constructor(private readonly module: TestingModule) {
+ this.primaReadClient = module.get(PrismaReadService).prisma;
+ this.prismaWriteClient = module.get(PrismaWriteService).prisma;
+ }
+
+ async get(teamId: Team["id"]) {
+ return this.primaReadClient.team.findFirst({ where: { id: teamId } });
+ }
+
+ async create(data: Prisma.TeamCreateInput) {
+ return this.prismaWriteClient.team.create({ data });
+ }
+
+ async delete(teamId: Team["id"]) {
+ return this.prismaWriteClient.team.delete({ where: { id: teamId } });
+ }
+}
diff --git a/apps/api/v2/test/fixtures/repository/tokens.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/tokens.repository.fixture.ts
new file mode 100644
index 00000000000000..6716f17948190a
--- /dev/null
+++ b/apps/api/v2/test/fixtures/repository/tokens.repository.fixture.ts
@@ -0,0 +1,47 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { TestingModule } from "@nestjs/testing";
+import * as crypto from "crypto";
+import { DateTime } from "luxon";
+
+export class TokensRepositoryFixture {
+ private primaReadClient: PrismaReadService["prisma"];
+ private prismaWriteClient: PrismaWriteService["prisma"];
+
+ constructor(private readonly module: TestingModule) {
+ this.primaReadClient = module.get(PrismaReadService).prisma;
+ this.prismaWriteClient = module.get(PrismaWriteService).prisma;
+ }
+
+ async createTokens(userId: number, clientId: string) {
+ const accessExpiry = DateTime.now().plus({ days: 1 }).startOf("day").toJSDate();
+ const refreshExpiry = DateTime.now().plus({ year: 1 }).startOf("day").toJSDate();
+ const accessTokenBuffer = crypto.randomBytes(48);
+ const accessTokenSecret = accessTokenBuffer.toString("hex");
+ const refreshTokenBuffer = crypto.randomBytes(48);
+ const refreshTokenSecret = refreshTokenBuffer.toString("hex");
+ const [accessToken, refreshToken] = await this.prismaWriteClient.$transaction([
+ this.prismaWriteClient.accessToken.create({
+ data: {
+ secret: accessTokenSecret,
+ expiresAt: accessExpiry,
+ client: { connect: { id: clientId } },
+ owner: { connect: { id: userId } },
+ },
+ }),
+ this.prismaWriteClient.refreshToken.create({
+ data: {
+ secret: refreshTokenSecret,
+ expiresAt: refreshExpiry,
+ client: { connect: { id: clientId } },
+ owner: { connect: { id: userId } },
+ },
+ }),
+ ]);
+
+ return {
+ accessToken: accessToken.secret,
+ refreshToken: refreshToken.secret,
+ };
+ }
+}
diff --git a/apps/api/v2/test/fixtures/repository/users.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/users.repository.fixture.ts
new file mode 100644
index 00000000000000..ce0035ab4cc30f
--- /dev/null
+++ b/apps/api/v2/test/fixtures/repository/users.repository.fixture.ts
@@ -0,0 +1,51 @@
+import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
+import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
+import { TestingModule } from "@nestjs/testing";
+import { Prisma, User } from "@prisma/client";
+
+export class UserRepositoryFixture {
+ private primaReadClient: PrismaReadService["prisma"];
+ private prismaWriteClient: PrismaWriteService["prisma"];
+
+ constructor(private readonly module: TestingModule) {
+ this.primaReadClient = module.get(PrismaReadService).prisma;
+ this.prismaWriteClient = module.get(PrismaWriteService).prisma;
+ }
+
+ async get(userId: User["id"]) {
+ return this.primaReadClient.user.findFirst({ where: { id: userId } });
+ }
+
+ async create(data: Prisma.UserCreateInput) {
+ try {
+ // avoid uniq constraint in tests
+ await this.deleteByEmail(data.email);
+ } catch {}
+
+ return this.prismaWriteClient.user.create({ data });
+ }
+
+ async createOAuthManagedUser(email: Prisma.UserCreateInput["email"], oAuthClientId: string) {
+ try {
+ // avoid uniq constraint in tests
+ await this.deleteByEmail(email);
+ } catch {}
+
+ return this.prismaWriteClient.user.create({
+ data: {
+ email,
+ platformOAuthClients: {
+ connect: { id: oAuthClientId },
+ },
+ },
+ });
+ }
+
+ async delete(userId: User["id"]) {
+ return this.prismaWriteClient.user.delete({ where: { id: userId } });
+ }
+
+ async deleteByEmail(email: User["email"]) {
+ return this.prismaWriteClient.user.delete({ where: { email } });
+ }
+}
diff --git a/apps/api/v2/test/mocks/access-token-mock.strategy.ts b/apps/api/v2/test/mocks/access-token-mock.strategy.ts
new file mode 100644
index 00000000000000..f7c89a2beb8ee0
--- /dev/null
+++ b/apps/api/v2/test/mocks/access-token-mock.strategy.ts
@@ -0,0 +1,25 @@
+import { BaseStrategy } from "@/lib/passport/strategies/types";
+import { UsersRepository } from "@/modules/users/users.repository";
+import { Injectable } from "@nestjs/common";
+import { PassportStrategy } from "@nestjs/passport";
+
+@Injectable()
+export class AccessTokenMockStrategy extends PassportStrategy(BaseStrategy, "access-token") {
+ constructor(private readonly email: string, private readonly usersRepository: UsersRepository) {
+ super();
+ }
+
+ async authenticate() {
+ try {
+ const user = await this.usersRepository.findByEmail(this.email);
+ if (!user) {
+ throw new Error("User with the provided ID not found");
+ }
+
+ return this.success(user);
+ } catch (error) {
+ console.error(error);
+ if (error instanceof Error) return this.error(error);
+ }
+ }
+}
diff --git a/apps/api/v2/test/mocks/next-auth-mock.strategy.ts b/apps/api/v2/test/mocks/next-auth-mock.strategy.ts
new file mode 100644
index 00000000000000..96e92b00a8a42f
--- /dev/null
+++ b/apps/api/v2/test/mocks/next-auth-mock.strategy.ts
@@ -0,0 +1,24 @@
+import { BaseStrategy } from "@/lib/passport/strategies/types";
+import { UsersRepository } from "@/modules/users/users.repository";
+import { Injectable } from "@nestjs/common";
+import { PassportStrategy } from "@nestjs/passport";
+
+@Injectable()
+export class NextAuthMockStrategy extends PassportStrategy(BaseStrategy, "next-auth") {
+ constructor(private readonly email: string, private readonly userRepository: UsersRepository) {
+ super();
+ }
+ async authenticate() {
+ try {
+ const user = await this.userRepository.findByEmail(this.email);
+ if (!user) {
+ throw new Error("User with the provided email not found");
+ }
+
+ return this.success(user);
+ } catch (error) {
+ console.error(error);
+ if (error instanceof Error) return this.error(error);
+ }
+ }
+}
diff --git a/apps/api/v2/test/setEnvVars.ts b/apps/api/v2/test/setEnvVars.ts
new file mode 100644
index 00000000000000..d353423dfa1238
--- /dev/null
+++ b/apps/api/v2/test/setEnvVars.ts
@@ -0,0 +1,16 @@
+import type { Environment } from "@/env";
+
+const env: Partial> = {
+ API_PORT: "5555",
+ DATABASE_READ_URL: "postgresql://postgres:@localhost:5450/calendso",
+ DATABASE_WRITE_URL: "postgresql://postgres:@localhost:5450/calendso",
+ NEXTAUTH_SECRET: "XF+Hws3A5g2eyWA5uGYYVJ74X+wrCWJ8oWo6kAfU6O8=",
+ JWT_SECRET: "XF+Hws3A5g2eyWA5uGYYVJ74X+wrCWJ8oWo6kAfU6O8=",
+ LOG_LEVEL: "trace",
+ REDIS_URL: "redis://localhost:9199",
+};
+
+process.env = {
+ ...env,
+ ...process.env,
+};
diff --git a/apps/api/v2/test/utils/withAccessTokenAuth.ts b/apps/api/v2/test/utils/withAccessTokenAuth.ts
new file mode 100644
index 00000000000000..809de3c683b873
--- /dev/null
+++ b/apps/api/v2/test/utils/withAccessTokenAuth.ts
@@ -0,0 +1,10 @@
+import { AccessTokenStrategy } from "@/modules/auth/strategies/access-token/access-token.strategy";
+import { UsersRepository } from "@/modules/users/users.repository";
+import { TestingModuleBuilder } from "@nestjs/testing";
+import { AccessTokenMockStrategy } from "test/mocks/access-token-mock.strategy";
+
+export const withAccessTokenAuth = (email: string, module: TestingModuleBuilder) =>
+ module.overrideProvider(AccessTokenStrategy).useFactory({
+ factory: (usersRepository: UsersRepository) => new AccessTokenMockStrategy(email, usersRepository),
+ inject: [UsersRepository],
+ });
diff --git a/apps/api/v2/test/utils/withNextAuth.ts b/apps/api/v2/test/utils/withNextAuth.ts
new file mode 100644
index 00000000000000..4a96bf7edfd93a
--- /dev/null
+++ b/apps/api/v2/test/utils/withNextAuth.ts
@@ -0,0 +1,10 @@
+import { NextAuthStrategy } from "@/modules/auth/strategies/next-auth/next-auth.strategy";
+import { UsersRepository } from "@/modules/users/users.repository";
+import { TestingModuleBuilder } from "@nestjs/testing";
+import { NextAuthMockStrategy } from "test/mocks/next-auth-mock.strategy";
+
+export const withNextAuth = (email: string, module: TestingModuleBuilder) =>
+ module.overrideProvider(NextAuthStrategy).useFactory({
+ factory: (userRepository: UsersRepository) => new NextAuthMockStrategy(email, userRepository),
+ inject: [UsersRepository],
+ });
diff --git a/apps/api/v2/tsconfig.build.json b/apps/api/v2/tsconfig.build.json
new file mode 100644
index 00000000000000..64f86c6bd2bb30
--- /dev/null
+++ b/apps/api/v2/tsconfig.build.json
@@ -0,0 +1,4 @@
+{
+ "extends": "./tsconfig.json",
+ "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
+}
diff --git a/apps/api/v2/tsconfig.json b/apps/api/v2/tsconfig.json
new file mode 100644
index 00000000000000..3e738f626136f8
--- /dev/null
+++ b/apps/api/v2/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "declaration": true,
+ "resolveJsonModule": true,
+ "removeComments": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "allowSyntheticDefaultImports": true,
+ "target": "ES2021",
+ "sourceMap": true,
+ "outDir": "./dist",
+ "baseUrl": ".",
+ "jsx": "react-jsx",
+ "paths": {
+ "@/*": ["./src/*"],
+ "@prisma/client/*": ["@calcom/prisma/client/*"]
+ },
+ "incremental": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noImplicitAny": true,
+ "forceConsistentCasingInFileNames": false,
+ "noFallthroughCasesInSwitch": false
+ },
+ "exclude": ["./dist", "next-i18next.config.js"],
+ "include": ["./**/*.ts", "../../../packages/types/*.d.ts"]
+}
diff --git a/apps/web/components/settings/organizations/platform/oauth-clients/OAuthClientCard.tsx b/apps/web/components/settings/organizations/platform/oauth-clients/OAuthClientCard.tsx
new file mode 100644
index 00000000000000..d5afcfd42992bf
--- /dev/null
+++ b/apps/web/components/settings/organizations/platform/oauth-clients/OAuthClientCard.tsx
@@ -0,0 +1,124 @@
+import { Asterisk, Clipboard } from "lucide-react";
+import React from "react";
+
+import { classNames } from "@calcom/lib";
+import { PERMISSIONS_GROUPED_MAP } from "@calcom/platform-constants";
+import type { Avatar } from "@calcom/prisma/client";
+import { Button, showToast } from "@calcom/ui";
+
+import { hasPermission } from "../../../../../../../packages/platform/utils/permissions";
+
+type OAuthClientCardProps = {
+ name: string;
+ logo?: Avatar;
+ redirectUris: string[];
+ permissions: number;
+ lastItem: boolean;
+ id: string;
+ secret: string;
+ onDelete: (id: string) => Promise;
+ isLoading: boolean;
+};
+
+export const OAuthClientCard = ({
+ name,
+ logo,
+ redirectUris,
+ permissions,
+ id,
+ secret,
+ lastItem,
+ onDelete,
+ isLoading,
+}: OAuthClientCardProps) => {
+ const clientPermissions = Object.values(PERMISSIONS_GROUPED_MAP).map((value, index) => {
+ let permissionsMessage = "";
+ const hasReadPermission = hasPermission(permissions, value.read);
+ const hasWritePermission = hasPermission(permissions, value.write);
+
+ if (hasReadPermission || hasWritePermission) {
+ permissionsMessage = hasReadPermission ? "read" : "write";
+ }
+
+ if (hasReadPermission && hasWritePermission) {
+ permissionsMessage = "read/write";
+ }
+
+ return (
+ !!permissionsMessage && (
+
+ {permissionsMessage} {`${value.label}s`.toLocaleLowerCase()}
+ {Object.values(PERMISSIONS_GROUPED_MAP).length === index + 1 ? " " : ", "}
+
+ )
+ );
+ });
+
+ return (
+
+
+
+
+ Client name: {name}
+
+
+ {!!logo && (
+
+ <>{logo}>
+
+ )}
+
+
+
Client Id:
+
{id}
+
{
+ navigator.clipboard.writeText(id);
+ showToast("Client id copied to clipboard.", "success");
+ }}
+ />
+
+
+
+
Client Secret:
+
+ {[...new Array(20)].map((_, index) => (
+
+ ))}
+
{
+ navigator.clipboard.writeText(secret);
+ showToast("Client secret copied to clipboard.", "success");
+ }}
+ />
+
+
+
+
Permissions:
+
{clientPermissions}
+
+
+ Redirect uris:
+ {redirectUris.map((item, index) => (redirectUris.length === index + 1 ? `${item}` : `${item}, `))}
+
+
+
+ onDelete(id)}>
+ Delete
+
+
+
+ );
+};
diff --git a/apps/web/components/settings/organizations/platform/oauth-clients/OAuthClientForm.tsx b/apps/web/components/settings/organizations/platform/oauth-clients/OAuthClientForm.tsx
new file mode 100644
index 00000000000000..11f341c1c40735
--- /dev/null
+++ b/apps/web/components/settings/organizations/platform/oauth-clients/OAuthClientForm.tsx
@@ -0,0 +1,228 @@
+import { useRouter } from "next/router";
+import type { FC } from "react";
+import React, { useState, useCallback } from "react";
+import { useForm, useFieldArray } from "react-hook-form";
+
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { PERMISSIONS_GROUPED_MAP } from "@calcom/platform-constants/permissions";
+import { showToast } from "@calcom/ui";
+import { Meta, Button, TextField, Label } from "@calcom/ui";
+import { Plus, Trash } from "@calcom/ui/components/icon";
+
+import { useCreateOAuthClient } from "@lib/hooks/settings/organizations/platform/oauth-clients/usePersistOAuthClient";
+
+type FormValues = {
+ name: string;
+ logo?: string;
+ permissions: number;
+ eventTypeRead: boolean;
+ eventTypeWrite: boolean;
+ bookingRead: boolean;
+ bookingWrite: boolean;
+ scheduleRead: boolean;
+ scheduleWrite: boolean;
+ appsRead: boolean;
+ appsWrite: boolean;
+ profileRead: boolean;
+ profileWrite: boolean;
+ redirectUris: {
+ uri: string;
+ }[];
+};
+
+export const OAuthClientForm: FC = () => {
+ const { t } = useLocale();
+ const router = useRouter();
+ const { register, control, handleSubmit, setValue } = useForm({
+ defaultValues: {
+ redirectUris: [{ uri: "" }],
+ },
+ });
+ const { fields, append, remove } = useFieldArray({
+ control,
+ name: "redirectUris",
+ });
+ const [isSelectAllPermissionsChecked, setIsSelectAllPermissionsChecked] = useState(false);
+
+ const selectAllPermissions = useCallback(() => {
+ Object.keys(PERMISSIONS_GROUPED_MAP).forEach((key) => {
+ const entity = key as keyof typeof PERMISSIONS_GROUPED_MAP;
+ const permissionKey = PERMISSIONS_GROUPED_MAP[entity].key;
+
+ setValue(`${permissionKey}Read`, !isSelectAllPermissionsChecked);
+ setValue(`${permissionKey}Write`, !isSelectAllPermissionsChecked);
+ });
+
+ setIsSelectAllPermissionsChecked((preValue) => !preValue);
+ }, [isSelectAllPermissionsChecked, setValue]);
+
+ const { mutateAsync, isPending } = useCreateOAuthClient({
+ onSuccess: () => {
+ showToast("OAuth client created successfully", "success");
+ router.push("/settings/organizations/platform/oauth-clients");
+ },
+ onError: () => {
+ showToast("Internal server error, please try again later", "error");
+ },
+ });
+
+ const onSubmit = (data: FormValues) => {
+ let userPermissions = 0;
+ const userRedirectUris = data.redirectUris.map((uri) => uri.uri).filter((uri) => !!uri);
+
+ Object.keys(PERMISSIONS_GROUPED_MAP).forEach((key) => {
+ const entity = key as keyof typeof PERMISSIONS_GROUPED_MAP;
+ const entityKey = PERMISSIONS_GROUPED_MAP[entity].key;
+ const read = PERMISSIONS_GROUPED_MAP[entity].read;
+ const write = PERMISSIONS_GROUPED_MAP[entity].write;
+ if (data[`${entityKey}Read`]) userPermissions |= read;
+ if (data[`${entityKey}Write`]) userPermissions |= write;
+ });
+
+ mutateAsync({
+ name: data.name,
+ permissions: userPermissions,
+ // logo: data.logo,
+ redirectUris: userRedirectUris,
+ });
+ };
+
+ const permissionsCheckboxes = Object.keys(PERMISSIONS_GROUPED_MAP).map((key) => {
+ const entity = key as keyof typeof PERMISSIONS_GROUPED_MAP;
+ const permissionKey = PERMISSIONS_GROUPED_MAP[entity].key;
+ const permissionLabel = PERMISSIONS_GROUPED_MAP[entity].label;
+
+ return (
+
+ );
+ });
+
+ return (
+
+ );
+};
diff --git a/apps/web/lib/hooks/settings/organizations/platform/oauth-clients/useOAuthClients.ts b/apps/web/lib/hooks/settings/organizations/platform/oauth-clients/useOAuthClients.ts
new file mode 100644
index 00000000000000..6295ca7579601d
--- /dev/null
+++ b/apps/web/lib/hooks/settings/organizations/platform/oauth-clients/useOAuthClients.ts
@@ -0,0 +1,36 @@
+import { useQuery } from "@tanstack/react-query";
+
+import type { ApiSuccessResponse } from "@calcom/platform-types";
+import type { PlatformOAuthClient } from "@calcom/prisma/client";
+
+export const useOAuthClients = () => {
+ const query = useQuery>({
+ queryKey: ["oauth-clients"],
+ queryFn: () => {
+ return fetch("/api/v2/oauth-clients", {
+ method: "get",
+ headers: { "Content-type": "application/json" },
+ }).then((res) => res.json());
+ },
+ });
+
+ return { ...query, data: query.data?.data ?? [] };
+};
+
+export const useOAuthClient = (clientId: string) => {
+ const {
+ isLoading,
+ error,
+ data: response,
+ } = useQuery>({
+ queryKey: ["oauth-client"],
+ queryFn: () => {
+ return fetch(`/api/v2/oauth-clients/${clientId}`, {
+ method: "get",
+ headers: { "Content-type": "application/json" },
+ }).then((res) => res.json());
+ },
+ });
+
+ return { isLoading, error, data: response?.data };
+};
diff --git a/apps/web/lib/hooks/settings/organizations/platform/oauth-clients/usePersistOAuthClient.ts b/apps/web/lib/hooks/settings/organizations/platform/oauth-clients/usePersistOAuthClient.ts
new file mode 100644
index 00000000000000..975cacc55ef990
--- /dev/null
+++ b/apps/web/lib/hooks/settings/organizations/platform/oauth-clients/usePersistOAuthClient.ts
@@ -0,0 +1,80 @@
+import { useMutation } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { ApiResponse, CreateOAuthClientInput, DeleteOAuthClientInput } from "@calcom/platform-types";
+import type { OAuthClient } from "@calcom/prisma/client";
+
+interface IPersistOAuthClient {
+ onSuccess?: () => void;
+ onError?: () => void;
+}
+
+export const useCreateOAuthClient = (
+ { onSuccess, onError }: IPersistOAuthClient = {
+ onSuccess: () => {
+ return;
+ },
+ onError: () => {
+ return;
+ },
+ }
+) => {
+ const mutation = useMutation<
+ ApiResponse<{ clientId: string; clientSecret: string }>,
+ unknown,
+ CreateOAuthClientInput
+ >({
+ mutationFn: (data) => {
+ return fetch("/api/v2/oauth-clients", {
+ method: "post",
+ headers: { "Content-type": "application/json" },
+ body: JSON.stringify(data),
+ }).then((res) => res.json());
+ },
+ onSuccess: (data) => {
+ if (data.status === SUCCESS_STATUS) {
+ onSuccess?.();
+ } else {
+ onError?.();
+ }
+ },
+ onError: () => {
+ onError?.();
+ },
+ });
+
+ return mutation;
+};
+
+export const useDeleteOAuthClient = (
+ { onSuccess, onError }: IPersistOAuthClient = {
+ onSuccess: () => {
+ return;
+ },
+ onError: () => {
+ return;
+ },
+ }
+) => {
+ const mutation = useMutation, unknown, DeleteOAuthClientInput>({
+ mutationFn: (data) => {
+ const { id } = data;
+ return fetch(`/api/v2/oauth-clients/${id}`, {
+ method: "delete",
+ headers: { "Content-type": "application/json" },
+ }).then((res) => res.json());
+ },
+ onSuccess: (data) => {
+ if (data.status === SUCCESS_STATUS) {
+ onSuccess?.();
+ } else {
+ onError?.();
+ }
+ },
+ onError: () => {
+ onError?.();
+ },
+ });
+
+ return mutation;
+};
diff --git a/apps/web/modules/users/views/users-type-public-view.tsx b/apps/web/modules/users/views/users-type-public-view.tsx
index 050c0b13140632..ec1ac12b708c79 100644
--- a/apps/web/modules/users/views/users-type-public-view.tsx
+++ b/apps/web/modules/users/views/users-type-public-view.tsx
@@ -2,9 +2,9 @@
import { useSearchParams } from "next/navigation";
-import { Booker } from "@calcom/atoms";
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
+import { Booker } from "@calcom/platform-atoms";
import { type PageProps } from "./users-type-public-view.getServerSideProps";
diff --git a/apps/web/next.config.js b/apps/web/next.config.js
index 32b7ec4cea4d60..c4b169d64d61e1 100644
--- a/apps/web/next.config.js
+++ b/apps/web/next.config.js
@@ -303,6 +303,10 @@ const nextConfig = {
];
let afterFiles = [
+ {
+ source: "/api/v2/:path*",
+ destination: `${process.env.NEXT_PUBLIC_API_V2_URL}/:path*`,
+ },
{
source: "/org/:slug",
destination: "/team/:slug",
diff --git a/apps/web/package.json b/apps/web/package.json
index 5a4d61a2b4e742..c5725ef2969e4a 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -116,6 +116,7 @@
"react-multi-email": "^0.5.3",
"react-phone-input-2": "^2.15.1",
"react-phone-number-input": "^3.2.7",
+ "react-query": "^3.39.3",
"react-schemaorg": "^2.0.0",
"react-select": "^5.7.0",
"react-timezone-select": "^1.4.0",
diff --git a/apps/web/pages/api/trpc/timezones/[trpc].ts b/apps/web/pages/api/trpc/timezones/[trpc].ts
new file mode 100644
index 00000000000000..661af7e50fe29a
--- /dev/null
+++ b/apps/web/pages/api/trpc/timezones/[trpc].ts
@@ -0,0 +1,4 @@
+import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
+import { timezonesRouter } from "@calcom/trpc/server/routers/publicViewer/timezones/_router";
+
+export default createNextApiHandler(timezonesRouter, true);
diff --git a/apps/web/pages/auth/platform/authorize.tsx b/apps/web/pages/auth/platform/authorize.tsx
new file mode 100644
index 00000000000000..81163cdca0d1c9
--- /dev/null
+++ b/apps/web/pages/auth/platform/authorize.tsx
@@ -0,0 +1,130 @@
+import { useRouter } from "next/navigation";
+
+import { APP_NAME } from "@calcom/lib/constants";
+import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { Avatar, Button } from "@calcom/ui";
+import { Info } from "@calcom/ui/components/icon";
+import { Plus } from "@calcom/ui/components/icon";
+
+import PageWrapper from "@components/PageWrapper";
+
+import { PERMISSIONS_GROUPED_MAP } from "../../../../../packages/platform/constants/permissions";
+import { hasPermission } from "../../../../../packages/platform/utils/permissions";
+
+export default function Authorize() {
+ const { t } = useLocale();
+ const router = useRouter();
+
+ const searchParams = useCompatSearchParams();
+ const queryString = searchParams?.toString();
+
+ // const { isLoading, error, data: client } = useOAuthClient(queryString);
+
+ const client: {
+ name: string;
+ logo?: string;
+ redirect_uris: string[];
+ permissions: number;
+ } = {
+ name: "Acme.com",
+ redirect_uris: ["", ""],
+ permissions: 7,
+ };
+
+ console.log("These are the search params:", queryString);
+
+ const permissions = Object.values(PERMISSIONS_GROUPED_MAP).map((value) => {
+ let permissionsMessage = "";
+ const hasReadPermission = hasPermission(client.permissions, value.read);
+ const hasWritePermission = hasPermission(client.permissions, value.write);
+
+ if (hasReadPermission || hasWritePermission) {
+ permissionsMessage = hasReadPermission ? "Read" : "Write";
+ }
+
+ if (hasReadPermission && hasWritePermission) {
+ permissionsMessage = "Read, write";
+ }
+
+ return (
+ !!permissionsMessage && (
+
+ ✓
+ {permissionsMessage} your {`${value.label}s`.toLocaleLowerCase()}
+
+ )
+ );
+ });
+
+ return (
+
+
+
+ {/*
+ below is where the client logo will be displayed
+ first we check if the client has a logo property and display logo if present
+ else we take logo from user profile pic
+ */}
+ {client.logo ? (
+
}
+ className="items-center"
+ imageSrc={client.logo}
+ size="lg"
+ />
+ ) : (
+
}
+ className="items-center"
+ imageSrc="/cal-com-icon.svg"
+ size="lg"
+ />
+ )}
+
+
+
+
+
+
+
+
+
+ {t("access_cal_account", { clientName: client.name, appName: APP_NAME })}
+
+
+ {t("allow_client_to", { clientName: client.name })}
+
+
+
+
+
+
+
+
+ {t("allow_client_to_do", { clientName: client.name })}
+
+
{t("oauth_access_information", { appName: APP_NAME })}
{" "}
+
+
+
+
+ {
+ router.back();
+ }}>
+ {t("go_back")}
+
+
+ {t("allow")}
+
+
+
+
+ );
+}
+
+Authorize.PageWrapper = PageWrapper;
diff --git a/apps/web/pages/availability/[schedule].tsx b/apps/web/pages/availability/[schedule].tsx
index 79993835a76124..b57f887a39fa6f 100644
--- a/apps/web/pages/availability/[schedule].tsx
+++ b/apps/web/pages/availability/[schedule].tsx
@@ -1,42 +1,19 @@
import { useRouter } from "next/navigation";
-import { useMemo, useState } from "react";
-import { Controller, useFieldArray, useForm, useWatch } from "react-hook-form";
+import { useForm } from "react-hook-form";
-import dayjs from "@calcom/dayjs";
-import { DateOverrideInputDialog, DateOverrideList } from "@calcom/features/schedules";
-import Schedule from "@calcom/features/schedules/components/Schedule";
-import Shell from "@calcom/features/shell/Shell";
-import { classNames } from "@calcom/lib";
-import { availabilityAsString } from "@calcom/lib/availability";
import { withErrorFromUnknown } from "@calcom/lib/getClientErrorFromUnknown";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HttpError } from "@calcom/lib/http-error";
+import { AvailabilitySettings } from "@calcom/platform-atoms";
import { trpc } from "@calcom/trpc/react";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
-import type { Schedule as ScheduleType, TimeRange, WorkingHours } from "@calcom/types/schedule";
-import {
- Button,
- ConfirmationDialogContent,
- Dialog,
- DialogTrigger,
- Form,
- Label,
- showToast,
- Skeleton,
- SkeletonText,
- Switch,
- TimezoneSelect,
- Tooltip,
- VerticalDivider,
-} from "@calcom/ui";
-import { ArrowLeft, Info, MoreVertical, Plus, Trash } from "@calcom/ui/components/icon";
+import type { Schedule as ScheduleType, TimeRange } from "@calcom/types/schedule";
+import { showToast } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
-import { SelectSkeletonLoader } from "@components/availability/SkeletonLoader";
-import EditableHeading from "@components/ui/EditableHeading";
-export type AvailabilityFormValues = {
+type AvailabilityFormValues = {
name: string;
schedule: ScheduleType;
dateOverrides: { ranges: TimeRange[] }[];
@@ -44,148 +21,15 @@ export type AvailabilityFormValues = {
isDefault: boolean;
};
-const useExcludedDates = () => {
- const watchValues = useWatch({ name: "dateOverrides" }) as {
- ranges: TimeRange[];
- }[];
- return useMemo(() => {
- return watchValues?.map((field) => dayjs(field.ranges[0].start).utc().format("YYYY-MM-DD"));
- }, [watchValues]);
-};
-
-const useSettings = () => {
- const { data } = useMeQuery();
- return {
- hour12: data?.timeFormat === 12,
- timeZone: data?.timeZone,
- };
-};
-
-const DateOverride = ({ workingHours }: { workingHours: WorkingHours[] }) => {
- const { hour12 } = useSettings();
- const { append, replace, fields } = useFieldArray({
- name: "dateOverrides",
- });
- const excludedDates = useExcludedDates();
- const { t } = useLocale();
- return (
-
-
- {t("date_overrides")}{" "}
-
-
-
-
-
-
-
{t("date_overrides_subtitle")}
-
-
- ranges.forEach((range) => append({ ranges: [range] }))}
- Trigger={
-
- {t("add_an_override")}
-
- }
- />
-
-
- );
-};
-
-const DeleteDialogButton = ({
- disabled,
- scheduleId,
- buttonClassName,
- onDeleteConfirmed,
-}: {
- disabled?: boolean;
- onDeleteConfirmed?: () => void;
- buttonClassName: string;
- scheduleId: number;
-}) => {
- const { t } = useLocale();
- const router = useRouter();
- const utils = trpc.useUtils();
- const { isPending, mutate } = trpc.viewer.availability.schedule.delete.useMutation({
- onError: withErrorFromUnknown((err) => {
- showToast(err.message, "error");
- }),
- onSettled: () => {
- utils.viewer.availability.list.invalidate();
- },
- onSuccess: () => {
- showToast(t("schedule_deleted_successfully"), "success");
- router.push("/availability");
- },
- });
-
- return (
-
-
-
-
- {
- scheduleId && mutate({ scheduleId });
- onDeleteConfirmed?.();
- }}>
- {t("delete_schedule_description")}
-
-
- );
-};
-
-// Simplify logic by assuming this will never be opened on a large screen
-const SmallScreenSideBar = ({ open, children }: { open: boolean; children: JSX.Element }) => {
- return (
-
-
- {open ? children : null}
-
-
- );
-};
export default function Availability() {
const searchParams = useCompatSearchParams();
- const { t, i18n } = useLocale();
- const utils = trpc.useUtils();
+ const { t } = useLocale();
+ const router = useRouter();
+ const utils = trpc.useContext();
const me = useMeQuery();
const scheduleId = searchParams?.get("schedule") ? Number(searchParams.get("schedule")) : -1;
const fromEventType = searchParams?.get("fromEventType");
const { timeFormat } = me.data || { timeFormat: null };
- const [openSidebar, setOpenSidebar] = useState(false);
const { data: schedule, isPending } = trpc.viewer.availability.schedule.get.useQuery(
{ scheduleId },
{
@@ -226,257 +70,54 @@ export default function Availability() {
},
});
+ const deleteMutation = trpc.viewer.availability.schedule.delete.useMutation({
+ onError: withErrorFromUnknown((err) => {
+ showToast(err.message, "error");
+ }),
+ onSettled: () => {
+ utils.viewer.availability.list.invalidate();
+ },
+ onSuccess: () => {
+ showToast(t("schedule_deleted_successfully"), "success");
+ router.push("/availability");
+ },
+ });
+
return (
- (
-
- )}
- />
- }
- subtitle={
- schedule ? (
- schedule.schedule
- .filter((availability) => !!availability.days.length)
- .map((availability) => (
-
- {availabilityAsString(availability, { locale: i18n.language, hour12: timeFormat === 12 })}
-
-
- ))
- ) : (
-
- )
+
-
- {!openSidebar ? (
- <>
-
- {t("set_to_default")}
-
- (
-
- )}
- />
- >
- ) : null}
-
-
-
-
-
-
-
- <>
-
-
{
- setOpenSidebar(false);
- }}
- />
- {t("availability_settings")}
- {
- setOpenSidebar(false);
- }}
- />
-
-
- {t("name")}
- (
-
- )}
- />
-
-
-
-
- {t("set_to_default")}
-
- (
-
- )}
- />
-
-
-
-
-
-
- {t("timezone")}
-
-
- value ? (
- onChange(timezone.value)}
- />
- ) : (
-
- )
- }
- />
-
-
-
-
- {t("something_doesnt_look_right")}
-
-
-
- {t("launch_troubleshooter")}
-
-
-
-
-
- >
-
-
-
-
- {t("save")}
-
- setOpenSidebar(true)}
- />
-
- }>
-
-
+ isDeleting={deleteMutation.isPending}
+ isLoading={isPending}
+ isSaving={updateMutation.isPending}
+ timeFormat={timeFormat}
+ weekStart={me.data?.weekStart || "Sunday"}
+ backPath={fromEventType ? true : "/availability"}
+ handleDelete={() => {
+ scheduleId && deleteMutation.mutate({ scheduleId });
+ }}
+ handleSubmit={async ({ dateOverrides, ...values }) => {
+ scheduleId &&
+ updateMutation.mutate({
+ scheduleId,
+ dateOverrides: dateOverrides.flatMap((override) => override.ranges),
+ ...values,
+ });
+ }}
+ />
);
}
diff --git a/apps/web/pages/availability/index.tsx b/apps/web/pages/availability/index.tsx
index 4fda487a04119a..30cd99df98afa1 100644
--- a/apps/web/pages/availability/index.tsx
+++ b/apps/web/pages/availability/index.tsx
@@ -12,6 +12,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HttpError } from "@calcom/lib/http-error";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
+import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
import { EmptyScreen, showToast, ToggleGroup } from "@calcom/ui";
import { Clock } from "@calcom/ui/components/icon";
@@ -172,6 +173,7 @@ export default function AvailabilityPage() {
const searchParams = useCompatSearchParams();
const router = useRouter();
const pathname = usePathname();
+ const me = useMeQuery();
// Get a new searchParams string by merging the current
// searchParams with a provided key/value pair
@@ -210,7 +212,11 @@ export default function AvailabilityPage() {
}>
- {searchParams?.get("type") === "team" ? : }
+ {searchParams?.get("type") === "team" ? (
+
+ ) : (
+
+ )}
);
diff --git a/apps/web/pages/d/[link]/[slug].tsx b/apps/web/pages/d/[link]/[slug].tsx
index 8a52eff77d3a0d..1c7a52c5a619ba 100644
--- a/apps/web/pages/d/[link]/[slug].tsx
+++ b/apps/web/pages/d/[link]/[slug].tsx
@@ -1,8 +1,8 @@
"use client";
-import { Booker } from "@calcom/atoms";
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
+import { Booker } from "@calcom/platform-atoms";
import { getServerSideProps, type PageProps } from "@lib/d/[link]/[slug]/getServerSideProps";
diff --git a/apps/web/pages/org/[orgSlug]/instant-meeting/team/[slug]/[type].tsx b/apps/web/pages/org/[orgSlug]/instant-meeting/team/[slug]/[type].tsx
index b0b46e89cec909..1d391d7eb27afe 100644
--- a/apps/web/pages/org/[orgSlug]/instant-meeting/team/[slug]/[type].tsx
+++ b/apps/web/pages/org/[orgSlug]/instant-meeting/team/[slug]/[type].tsx
@@ -1,13 +1,13 @@
import type { GetServerSidePropsContext } from "next";
import { z } from "zod";
-import { Booker } from "@calcom/atoms";
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
import { getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking";
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import slugify from "@calcom/lib/slugify";
+import { Booker } from "@calcom/platform-atoms";
import prisma from "@calcom/prisma";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
diff --git a/apps/web/pages/settings/organizations/platform/oauth-clients/create.tsx b/apps/web/pages/settings/organizations/platform/oauth-clients/create.tsx
new file mode 100644
index 00000000000000..654cbf0a19ffbf
--- /dev/null
+++ b/apps/web/pages/settings/organizations/platform/oauth-clients/create.tsx
@@ -0,0 +1,19 @@
+import React from "react";
+
+import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
+
+import PageWrapper from "@components/PageWrapper";
+import { OAuthClientForm } from "@components/settings/organizations/platform/oauth-clients/OAuthClientForm";
+
+export const CreateOAuthClient = () => {
+ return (
+
+
+
+ );
+};
+
+CreateOAuthClient.getLayout = getLayout;
+CreateOAuthClient.PageWrapper = PageWrapper;
+
+export default CreateOAuthClient;
diff --git a/apps/web/pages/settings/organizations/platform/oauth-clients/index.tsx b/apps/web/pages/settings/organizations/platform/oauth-clients/index.tsx
new file mode 100644
index 00000000000000..6b0c90f0220755
--- /dev/null
+++ b/apps/web/pages/settings/organizations/platform/oauth-clients/index.tsx
@@ -0,0 +1,100 @@
+import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
+import { Plus } from "lucide-react";
+import { useRouter } from "next/router";
+import React from "react";
+
+import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
+import { EmptyScreen, showToast } from "@calcom/ui";
+import { Meta, Button } from "@calcom/ui";
+import { Spinner } from "@calcom/ui/components/icon/Spinner";
+
+import { useOAuthClients } from "@lib/hooks/settings/organizations/platform/oauth-clients/useOAuthClients";
+import { useDeleteOAuthClient } from "@lib/hooks/settings/organizations/platform/oauth-clients/usePersistOAuthClient";
+
+import PageWrapper from "@components/PageWrapper";
+import { OAuthClientCard } from "@components/settings/organizations/platform/oauth-clients/OAuthClientCard";
+
+const queryClient = new QueryClient();
+
+export const OAuthClients = () => {
+ const { data, isLoading, refetch: refetchClients } = useOAuthClients();
+ const { mutateAsync, isPending: isDeleting } = useDeleteOAuthClient({
+ onSuccess: () => {
+ showToast("OAuth client deleted successfully", "success");
+ refetchClients();
+ },
+ });
+
+ const handleDelete = async (id: string) => {
+ await mutateAsync({ id: id });
+ };
+
+ const NewOAuthClientButton = () => {
+ const router = useRouter();
+
+ return (
+ {
+ e.preventDefault();
+ router.push("/settings/organizations/platform/oauth-clients/create");
+ }}
+ color="secondary"
+ StartIcon={Plus}>
+ Add
+
+ );
+ };
+
+ if (isLoading) {
+ return ;
+ }
+
+ return (
+
+
+
}
+ borderInShellHeader={true}
+ />
+
+ {Array.isArray(data) && data.length ? (
+ <>
+
+ {data.map((client, index) => {
+ return (
+
+ );
+ })}
+
+ >
+ ) : (
+
}
+ />
+ )}
+
+
+
+ );
+};
+
+OAuthClients.getLayout = getLayout;
+OAuthClients.PageWrapper = PageWrapper;
+
+export default OAuthClients;
diff --git a/apps/web/pages/team/[slug]/[type].tsx b/apps/web/pages/team/[slug]/[type].tsx
index 8b2ee43ad10607..fea7fe49347984 100644
--- a/apps/web/pages/team/[slug]/[type].tsx
+++ b/apps/web/pages/team/[slug]/[type].tsx
@@ -2,9 +2,9 @@
import { useSearchParams } from "next/navigation";
-import { Booker } from "@calcom/atoms";
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
+import { Booker } from "@calcom/platform-atoms";
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index 3acc201ff62096..92c944f4337820 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -2190,6 +2190,8 @@
"access_bookings": "Read, edit, delete your bookings",
"allow_client_to_do": "Allow {{clientName}} to do this?",
"oauth_access_information": "By clicking allow, you allow this app to use your information in accordance with their terms of service and privacy policy. You can remove access in the {{appName}} App Store.",
+ "oauth_form_title":"OAuth client creation form",
+ "oauth_form_description":"This is the form to create a new OAuth client",
"allow": "Allow",
"view_only_edit_availability_not_onboarded": "This user has not completed onboarding. You will not be able to set their availability until they have completed onboarding.",
"view_only_edit_availability": "You are viewing this user's availability. You can only edit your own availability.",
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index 831bb9eb69036a..8a173ecdc7d1fd 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -15,7 +15,8 @@
"name": "next"
}
],
- "strictNullChecks": true
+ "strictNullChecks": true,
+ "experimentalDecorators": true
},
"include": [
/* Find a way to not require this - App files don't belong here. */
diff --git a/package.json b/package.json
index 2f7c43d2d67dde..46b04c292eadca 100644
--- a/package.json
+++ b/package.json
@@ -4,11 +4,14 @@
"private": true,
"workspaces": [
"apps/*",
+ "apps/api/*",
"packages/*",
"packages/embeds/*",
"packages/features/*",
"packages/app-store/*",
- "packages/app-store/ee/*"
+ "packages/app-store/ee/*",
+ "packages/platform/*",
+ "packages/platform/examples/base"
],
"scripts": {
"app-store-cli": "yarn workspace @calcom/app-store-cli",
@@ -31,11 +34,11 @@
"db-studio": "yarn prisma studio",
"deploy": "turbo run deploy",
"dev:all": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/website\" --scope=\"@calcom/console\"",
- "dev:ai": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/api\" --scope=\"@calcom/ai\"",
- "dev:api": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/api\"",
- "dev:api:console": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/api\" --scope=\"@calcom/console\"",
+ "dev:ai": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/api-proxy\" --scope=\"@calcom/api\" --scope=\"@calcom/ai\"",
+ "dev:api": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/api-proxy\" --scope=\"@calcom/api\"",
+ "dev:api:console": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/api-proxy\" --scope=\"@calcom/api\" --scope=\"@calcom/console\"",
"dev:console": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/console\"",
- "dev:swagger": "turbo run dev --scope=\"@calcom/api\" --scope=\"@calcom/swagger\"",
+ "dev:swagger": "turbo run dev --scope=\"@calcom/api-proxy\" --scope=\"@calcom/api\" --scope=\"@calcom/swagger\"",
"dev:website": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/website\"",
"dev": "turbo run dev --scope=\"@calcom/web\"",
"build-storybook": "turbo run build --scope=\"@calcom/storybook\"",
@@ -105,7 +108,6 @@
"vitest-mock-extended": "^1.1.3"
},
"dependencies": {
- "city-timezones": "^1.2.1",
"eslint": "^8.34.0",
"lucide-react": "^0.171.0",
"turbo": "^1.10.1"
diff --git a/packages/app-store/exchange2013calendar/lib/CalendarService.ts b/packages/app-store/exchange2013calendar/lib/CalendarService.ts
index 1b89229499b1f3..4f4224a5ff18ec 100644
--- a/packages/app-store/exchange2013calendar/lib/CalendarService.ts
+++ b/packages/app-store/exchange2013calendar/lib/CalendarService.ts
@@ -144,7 +144,7 @@ export default class ExchangeCalendarService implements Calendar {
async getAvailability(dateFrom: string, dateTo: string, selectedCalendars: IntegrationCalendar[]) {
try {
- const externalCalendars = await this.listCalendars();
+ const externalCalendars: IntegrationCalendar[] = await this.listCalendars();
const calendarsToGetAppointmentsFrom = [];
for (let i = 0; i < selectedCalendars.length; i++) {
//Only select vaild calendars! (We get all all active calendars on the instance! even from different users!)
@@ -220,7 +220,7 @@ export default class ExchangeCalendarService implements Calendar {
}
}
return allFolders;
- });
+ }) as Promise;
} catch (reason) {
this.log.error(reason);
throw reason;
diff --git a/packages/app-store/exchange2016calendar/lib/CalendarService.ts b/packages/app-store/exchange2016calendar/lib/CalendarService.ts
index 5b4c57fca3b02a..1c33286d14d2cc 100644
--- a/packages/app-store/exchange2016calendar/lib/CalendarService.ts
+++ b/packages/app-store/exchange2016calendar/lib/CalendarService.ts
@@ -225,7 +225,7 @@ export default class ExchangeCalendarService implements Calendar {
}
}
return allFolders;
- });
+ }) as Promise;
} catch (reason) {
this.log.error(reason);
throw reason;
diff --git a/packages/app-store/test-setup.ts b/packages/app-store/test-setup.ts
index eb345413c0a381..039f7e3a318922 100644
--- a/packages/app-store/test-setup.ts
+++ b/packages/app-store/test-setup.ts
@@ -10,10 +10,23 @@ vi.mock("@calcom/lib/hooks/useLocale", () => ({
useLocale: () => {
return {
t: (str: string) => str,
+ isLocaleReady: true,
+ i18n: {
+ language: "en",
+ defaultLocale: "en",
+ locales: ["en"],
+ exists: () => false,
+ },
};
},
}));
+vi.mock("@calcom/platform-atoms", () => ({
+ useIsPlatform: () => {
+ return false;
+ },
+}));
+
global.ResizeObserver = vi.fn().mockImplementation(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
diff --git a/packages/app-store/tsconfig.json b/packages/app-store/tsconfig.json
index b08597974dbe6d..8e9faeb56eb3a5 100644
--- a/packages/app-store/tsconfig.json
+++ b/packages/app-store/tsconfig.json
@@ -10,7 +10,8 @@
"@lib/*": ["../../apps/web/lib/*"],
"@prisma/client/*": ["@calcom/prisma/client/*"]
},
- "resolveJsonModule": true
+ "resolveJsonModule": true,
+ "experimentalDecorators": true
},
"include": [
"next-env.d.ts",
diff --git a/packages/atoms/booker/index.ts b/packages/atoms/booker/index.ts
deleted file mode 100644
index 8f1e66dfa0966a..00000000000000
--- a/packages/atoms/booker/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { BookerWebWrapper as Booker } from "./wrappers/BookerWebWrapper";
diff --git a/packages/atoms/cal-provider/export.ts b/packages/atoms/cal-provider/export.ts
deleted file mode 100644
index 4a4e04887cf6db..00000000000000
--- a/packages/atoms/cal-provider/export.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { CalProvider } from "./index";
-export * from "../types";
diff --git a/packages/atoms/cal-provider/index.tsx b/packages/atoms/cal-provider/index.tsx
deleted file mode 100644
index ded4497ad274cc..00000000000000
--- a/packages/atoms/cal-provider/index.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import type { ReactNode } from "react";
-import { createContext, useContext } from "react";
-
-type CalProviderProps = {
- apiKey: string;
- children: ReactNode;
-};
-
-const ApiKeyContext = createContext("");
-
-export const useApiKey = () => useContext(ApiKeyContext);
-
-export function CalProvider({ apiKey, children }: CalProviderProps) {
- return {children} ;
-}
diff --git a/packages/atoms/globals.css b/packages/atoms/globals.css
deleted file mode 100644
index 12686157f6b945..00000000000000
--- a/packages/atoms/globals.css
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * @NOTE: This file is only imported when building the component's CSS file
- * When using this component in any Cal project, the globals are automatically imported
- * in that project.
- */
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-@import "../ui/styles/shared-globals.css";
diff --git a/packages/atoms/index.ts b/packages/atoms/index.ts
deleted file mode 100644
index 07163614f2dd99..00000000000000
--- a/packages/atoms/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { Booker } from "./booker";
-export { CalProvider } from "./cal-provider/index";
diff --git a/packages/atoms/package.json b/packages/atoms/package.json
deleted file mode 100644
index 11de78770e584f..00000000000000
--- a/packages/atoms/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "@calcom/atoms",
- "private": true,
- "sideEffects": false,
- "type": "module",
- "description": "Cal.com Atoms",
- "authors": "Cal.com, Inc.",
- "version": "1.0.0",
- "scripts": {
- "build": "node build.mjs"
- },
- "devDependencies": {
- "@rollup/plugin-node-resolve": "^15.0.1",
- "@types/react": "18.0.26",
- "@types/react-dom": "^18.0.9",
- "@vitejs/plugin-react": "^2.2.0",
- "rollup-plugin-node-builtins": "^2.1.2",
- "typescript": "^4.9.4",
- "vite": "^4.1.2"
- },
- "main": "./index"
-}
diff --git a/packages/atoms/tailwind.config.cjs b/packages/atoms/tailwind.config.cjs
deleted file mode 100644
index 55b6825ae45e2d..00000000000000
--- a/packages/atoms/tailwind.config.cjs
+++ /dev/null
@@ -1,7 +0,0 @@
-const base = require("@calcom/config/tailwind-preset");
-
-/** @type {import('tailwindcss').Config} */
-module.exports = {
- ...base,
- content: ["./bookings/**/*.tsx"],
-};
diff --git a/packages/atoms/tsconfig.json b/packages/atoms/tsconfig.json
deleted file mode 100644
index b8fb2a6430a461..00000000000000
--- a/packages/atoms/tsconfig.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "extends": "@calcom/tsconfig/react-library.json",
- "compilerOptions": {
- "baseUrl": ".",
- "paths": {
- "~/*": ["/*"]
- },
- "resolveJsonModule": true
- },
- "include": [".", "../types/next-auth.d.ts"],
- "exclude": ["dist", "build", "node_modules"]
-}
diff --git a/packages/atoms/vite.config.ts b/packages/atoms/vite.config.ts
deleted file mode 100644
index 0950e3ce06b105..00000000000000
--- a/packages/atoms/vite.config.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { resolve } from "path";
-import { defineConfig } from "vite";
-
-export default defineConfig({
- build: {
- lib: {
- entry: [resolve(__dirname, "booker/export.ts")],
- name: "CalAtoms",
- fileName: "cal-atoms",
- },
- rollupOptions: {
- external: ["react", "fs", "path", "os", "react-dom"],
- output: {
- globals: {
- react: "React",
- "react-dom": "ReactDOM",
- },
- },
- },
- },
- resolve: {
- alias: {
- fs: resolve("../../node_modules/rollup-plugin-node-builtins"),
- path: resolve("../../node_modules/rollup-plugin-node-builtins"),
- os: resolve("../../node_modules/rollup-plugin-node-builtins"),
- },
- },
-});
diff --git a/packages/config/tailwind-preset.js b/packages/config/tailwind-preset.js
index 61aea1585ee3c3..64391df41f4229 100644
--- a/packages/config/tailwind-preset.js
+++ b/packages/config/tailwind-preset.js
@@ -11,7 +11,7 @@ module.exports = {
"../../packages/app-store/**/*{components,pages}/**/*.{js,ts,jsx,tsx}",
"../../packages/features/**/*.{js,ts,jsx,tsx}",
"../../packages/ui/**/*.{js,ts,jsx,tsx}",
- "../../packages/atoms/**/*.{js,ts,jsx,tsx}",
+ "../../packages/platform/atoms/**/*.{js,ts,jsx,tsx}",
],
darkMode: "class",
theme: {
diff --git a/packages/dayjs/index.ts b/packages/dayjs/index.ts
index c2c9a000f55e30..c1dd1cbeb3f978 100644
--- a/packages/dayjs/index.ts
+++ b/packages/dayjs/index.ts
@@ -1,6 +1,5 @@
/* eslint-disable @calcom/eslint/deprecated-imports */
import dayjs from "dayjs";
-import dayjsBusinessTime from "dayjs-business-days2";
import customParseFormat from "dayjs/plugin/customParseFormat";
import duration from "dayjs/plugin/duration";
import isBetween from "dayjs/plugin/isBetween";
@@ -12,8 +11,10 @@ import timeZone from "dayjs/plugin/timezone";
import toArray from "dayjs/plugin/toArray";
import utc from "dayjs/plugin/utc";
+import BusinessDaysPlugin from "./plugins/business-days-plugin";
+
dayjs.extend(customParseFormat);
-dayjs.extend(dayjsBusinessTime);
+dayjs.extend(BusinessDaysPlugin);
dayjs.extend(isBetween);
dayjs.extend(isToday);
dayjs.extend(localizedFormat);
diff --git a/packages/dayjs/package.json b/packages/dayjs/package.json
index 8aa9fa660282ea..785a9ce265a4a9 100644
--- a/packages/dayjs/package.json
+++ b/packages/dayjs/package.json
@@ -5,7 +5,6 @@
"version": "1.0.0",
"main": "./index.ts",
"dependencies": {
- "dayjs": "1.11.2",
- "dayjs-business-days2": "1.1.0"
+ "dayjs": "1.11.2"
}
}
diff --git a/packages/dayjs/plugins/business-days-plugin.ts b/packages/dayjs/plugins/business-days-plugin.ts
new file mode 100644
index 00000000000000..f20a53ac835c34
--- /dev/null
+++ b/packages/dayjs/plugins/business-days-plugin.ts
@@ -0,0 +1,242 @@
+import type { Dayjs, PluginFunc } from "dayjs";
+
+interface BusinessDaysPluginOptions {
+ holidays?: string[];
+ holidayFormat?: string;
+ additionalWorkingDays?: string[];
+ additionalWorkingDayFormat?: string;
+ workingWeekdays?: number[];
+}
+
+const BusinessDaysPlugin: PluginFunc = (
+ options = {},
+ dayjsClass,
+ dayjsFactory
+) => {
+ const defaultWorkingWeekdays = [1, 2, 3, 4, 5];
+
+ dayjsFactory.getWorkingWeekdays = function (): number[] {
+ return options.workingWeekdays || defaultWorkingWeekdays;
+ };
+
+ dayjsFactory.setWorkingWeekdays = function (workingWeekdays: number[]): void {
+ options.workingWeekdays = workingWeekdays;
+ };
+
+ dayjsFactory.getHolidays = function (): string[] {
+ return options.holidays || [];
+ };
+
+ dayjsFactory.setHolidays = function (holidays: string[]): void {
+ options.holidays = holidays;
+ };
+
+ dayjsFactory.getHolidayFormat = function (): string | undefined {
+ return options.holidayFormat;
+ };
+
+ dayjsFactory.setHolidayFormat = function (holidayFormat: string): void {
+ options.holidayFormat = holidayFormat;
+ };
+
+ dayjsFactory.getAdditionalWorkingDays = function (): string[] {
+ return options.additionalWorkingDays || [];
+ };
+
+ dayjsFactory.setAdditionalWorkingDays = function (additionalWorkingDays: string[]): void {
+ options.additionalWorkingDays = additionalWorkingDays;
+ };
+
+ dayjsFactory.getAdditionalWorkingDayFormat = function (): string | undefined {
+ return options.additionalWorkingDayFormat;
+ };
+
+ dayjsFactory.setAdditionalWorkingDayFormat = function (additionalWorkingDayFormat: string): void {
+ options.additionalWorkingDayFormat = additionalWorkingDayFormat;
+ };
+
+ dayjsClass.prototype.isHoliday = function (this: Dayjs): boolean {
+ if (!options.holidays) {
+ return false;
+ }
+ if (options.holidays.includes(this.format(options.holidayFormat))) {
+ return true;
+ }
+
+ return false;
+ };
+
+ dayjsClass.prototype.isBusinessDay = function (this: Dayjs): boolean {
+ const workingWeekdays = options.workingWeekdays || defaultWorkingWeekdays;
+
+ if (this.isHoliday()) {
+ return false;
+ }
+ if (this.isAdditionalWorkingDay()) {
+ return true;
+ }
+ if (workingWeekdays.includes(this.day())) {
+ return true;
+ }
+
+ return false;
+ };
+
+ dayjsClass.prototype.isAdditionalWorkingDay = function (this: Dayjs): boolean {
+ if (!options.additionalWorkingDays) {
+ return false;
+ }
+ if (options.additionalWorkingDays.includes(this.format(options.additionalWorkingDayFormat))) {
+ return true;
+ }
+
+ return false;
+ };
+
+ dayjsClass.prototype.businessDaysAdd = function (this: Dayjs, days: number): Dayjs {
+ const numericDirection = days < 0 ? -1 : 1;
+ let currentDay = this.clone();
+ let daysRemaining = Math.abs(days);
+
+ while (daysRemaining > 0) {
+ currentDay = currentDay.add(numericDirection, `d`);
+
+ if (currentDay.isBusinessDay()) {
+ daysRemaining -= 1;
+ }
+ }
+
+ return currentDay;
+ };
+
+ dayjsClass.prototype.businessDaysSubtract = function (this: Dayjs, days: number): Dayjs {
+ let currentDay = this.clone();
+
+ currentDay = currentDay.businessDaysAdd(days * -1);
+
+ return currentDay;
+ };
+
+ dayjsClass.prototype.businessDiff = function (this: Dayjs, date: Dayjs): number {
+ const day1 = this.clone();
+ const day2 = date.clone();
+
+ const isPositiveDiff = day1 >= day2;
+ let start = isPositiveDiff ? day2 : day1;
+ const end = isPositiveDiff ? day1 : day2;
+
+ let daysBetween = 0;
+
+ if (start.isSame(end)) {
+ return daysBetween;
+ }
+
+ while (start < end) {
+ if (start.isBusinessDay()) {
+ daysBetween += 1;
+ }
+
+ start = start.add(1, `d`);
+ }
+
+ return isPositiveDiff ? daysBetween : -daysBetween;
+ };
+
+ dayjsClass.prototype.nextBusinessDay = function (this: Dayjs): Dayjs {
+ const searchLimit = 7;
+ let currentDay = this.clone();
+
+ let loopIndex = 1;
+ while (loopIndex < searchLimit) {
+ currentDay = currentDay.add(1, `day`);
+
+ if (currentDay.isBusinessDay()) {
+ break;
+ }
+ loopIndex += 1;
+ }
+
+ return currentDay;
+ };
+
+ dayjsClass.prototype.prevBusinessDay = function (this: Dayjs): Dayjs {
+ const searchLimit = 7;
+ let currentDay = this.clone();
+
+ let loopIndex = 1;
+ while (loopIndex < searchLimit) {
+ currentDay = currentDay.subtract(1, `day`);
+
+ if (currentDay.isBusinessDay()) {
+ break;
+ }
+ loopIndex += 1;
+ }
+
+ return currentDay;
+ };
+
+ dayjsClass.prototype.businessDaysInMonth = function (this: Dayjs): Dayjs[] {
+ if (!this.isValid()) {
+ return [];
+ }
+
+ let currentDay = this.clone().startOf(`month`);
+ const monthEnd = this.clone().endOf(`month`);
+ const businessDays: Dayjs[] = [];
+ let monthComplete = false;
+
+ while (!monthComplete) {
+ if (currentDay.isBusinessDay()) {
+ businessDays.push(currentDay.clone());
+ }
+
+ currentDay = currentDay.add(1, `day`);
+
+ if (currentDay.isAfter(monthEnd)) {
+ monthComplete = true;
+ }
+ }
+
+ return businessDays;
+ };
+
+ dayjsClass.prototype.lastBusinessDayOfMonth = function (this: Dayjs): Dayjs {
+ const businessDays = this.businessDaysInMonth();
+ const lastBusinessDay = businessDays[businessDays.length - 1];
+ return lastBusinessDay;
+ };
+
+ dayjsClass.prototype.businessWeeksInMonth = function (this: Dayjs): Dayjs[][] {
+ if (!this.isValid()) {
+ return [];
+ }
+
+ let currentDay = this.clone().startOf(`month`);
+ const monthEnd = this.clone().endOf(`month`);
+ const businessWeeks: Dayjs[][] = [];
+ let businessDays: Dayjs[] = [];
+ let monthComplete = false;
+
+ while (!monthComplete) {
+ if (currentDay.isBusinessDay()) {
+ businessDays.push(currentDay.clone());
+ }
+
+ if (currentDay.day() === 5 || currentDay.isSame(monthEnd, `day`)) {
+ businessWeeks.push(businessDays);
+ businessDays = [];
+ }
+
+ currentDay = currentDay.add(1, `day`);
+
+ if (currentDay.isAfter(monthEnd)) {
+ monthComplete = true;
+ }
+ }
+
+ return businessWeeks;
+ };
+};
+
+export default BusinessDaysPlugin;
diff --git a/packages/dayjs/tsconfig.json b/packages/dayjs/tsconfig.json
new file mode 100644
index 00000000000000..9380508994ae0c
--- /dev/null
+++ b/packages/dayjs/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "@calcom/tsconfig/base.json",
+ "include": [".", "../types/business-days-plugin.d.ts"]
+}
diff --git a/packages/embeds/embed-core/package.json b/packages/embeds/embed-core/package.json
index 835b5230c9c33b..1508ce97d1469c 100644
--- a/packages/embeds/embed-core/package.json
+++ b/packages/embeds/embed-core/package.json
@@ -50,6 +50,7 @@
"postcss": "^8.4.18",
"tailwindcss": "^3.3.3",
"typescript": "^4.9.4",
- "vite": "^4.1.2"
+ "vite": "^4.1.2",
+ "vite-plugin-environment": "^1.1.3"
}
}
diff --git a/packages/embeds/embed-core/src/embed.ts b/packages/embeds/embed-core/src/embed.ts
index 0cb67b3a05a8f9..a7a0a9604998e2 100644
--- a/packages/embeds/embed-core/src/embed.ts
+++ b/packages/embeds/embed-core/src/embed.ts
@@ -19,8 +19,7 @@ export type Message = {
};
// HACK: Redefine and don't import WEBAPP_URL as it causes import statement to be present in built file.
// This is happening because we are not able to generate an App and a lib using single Vite Config.
-const WEBAPP_URL =
- import.meta.env.EMBED_PUBLIC_WEBAPP_URL || `https://${import.meta.env.EMBED_PUBLIC_VERCEL_URL}`;
+const WEBAPP_URL = process.env.EMBED_PUBLIC_WEBAPP_URL || `https://${process.env.EMBED_PUBLIC_VERCEL_URL}`;
customElements.define("cal-modal-box", ModalBox);
customElements.define("cal-floating-button", FloatingButton);
@@ -52,7 +51,7 @@ if (!globalCal || !globalCal.q) {
// Store Commit Hash to know exactly what version of the code is running
// TODO: Ideally it should be the version as per package.json and then it can be renamed to version.
// But because it is built on local machine right now, it is much more reliable to have the commit hash.
-globalCal.fingerprint = import.meta.env.EMBED_PUBLIC_EMBED_FINGER_PRINT as string;
+globalCal.fingerprint = process.env.EMBED_PUBLIC_EMBED_FINGER_PRINT as string;
globalCal.__css = allCss;
document.head.appendChild(document.createElement("style")).innerHTML = css;
diff --git a/packages/embeds/embed-core/src/preview.ts b/packages/embeds/embed-core/src/preview.ts
index 8838d76d129ed3..e8a9096e7806fe 100644
--- a/packages/embeds/embed-core/src/preview.ts
+++ b/packages/embeds/embed-core/src/preview.ts
@@ -43,7 +43,7 @@ if (!bookerUrl || !embedLibUrl) {
})(window, embedLibUrl, "init");
const previewWindow = window;
-previewWindow.Cal.fingerprint = import.meta.env.EMBED_PUBLIC_EMBED_FINGER_PRINT as string;
+previewWindow.Cal.fingerprint = process.env.EMBED_PUBLIC_EMBED_FINGER_PRINT as string;
previewWindow.Cal("init", {
origin: bookerUrl,
diff --git a/packages/embeds/embed-core/vite.config.js b/packages/embeds/embed-core/vite.config.js
index 9da4a02fcdcc7b..4c63cf230a7c97 100644
--- a/packages/embeds/embed-core/vite.config.js
+++ b/packages/embeds/embed-core/vite.config.js
@@ -1,3 +1,5 @@
+import EnvironmentPlugin from "vite-plugin-environment";
+
import viteBaseConfig from "../vite.config";
const path = require("path");
@@ -15,6 +17,11 @@ module.exports = defineConfig((configEnv) => {
embed: path.resolve(__dirname, "src/embed.ts"),
},
plugins: [
+ EnvironmentPlugin([
+ "EMBED_PUBLIC_EMBED_FINGER_PRINT",
+ "EMBED_PUBLIC_VERCEL_URL",
+ "EMBED_PUBLIC_WEBAPP_URL",
+ ]),
{
generateBundle: (code, bundle) => {
// Note: banner/footer doesn't work because it doesn't enclose the entire library code, some variables are still left out.
diff --git a/packages/features/bookings/Booker/Booker.tsx b/packages/features/bookings/Booker/Booker.tsx
index 1e5e02de21681a..f1119c0caf4df6 100644
--- a/packages/features/bookings/Booker/Booker.tsx
+++ b/packages/features/bookings/Booker/Booker.tsx
@@ -9,7 +9,6 @@ import dayjs from "@calcom/dayjs";
import { getQueryParam } from "@calcom/features/bookings/Booker/utils/query-param";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
import classNames from "@calcom/lib/classNames";
-import { DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { BookerLayouts } from "@calcom/prisma/zod-utils";
@@ -29,7 +28,6 @@ import { Away, NotFound } from "./components/Unavailable";
import { fadeInLeft, getBookerSizeClassNames, useBookerResizeAnimation } from "./config";
import { useBookerStore } from "./store";
import type { BookerProps, WrappedBookerProps } from "./types";
-import { useBrandColors } from "./utils/use-brand-colors";
const loadFramerFeatures = () => import("./framer-features").then((res) => res.default);
const PoweredBy = dynamic(() => import("@calcom/ee/components/PoweredBy"));
@@ -66,8 +64,10 @@ const BookerComponent = ({
bookerLayout,
schedule,
verifyCode,
+ isPlatform,
orgBannerUrl,
}: BookerProps & WrappedBookerProps) => {
+ const { t } = useLocale();
const [bookerState, setBookerState] = useBookerStore((state) => [state.state, state.setState], shallow);
const selectedDate = useBookerStore((state) => state.selectedDate);
const {
@@ -114,14 +114,6 @@ const BookerComponent = ({
const timeslotsRef = useRef(null);
const StickyOnDesktop = isMobile ? "div" : StickyBox;
- const { t } = useLocale();
-
- useBrandColors({
- brandColor: event.data?.profile.brandColor ?? DEFAULT_LIGHT_BRAND_COLOR,
- darkBrandColor: event.data?.profile.darkBrandColor ?? DEFAULT_DARK_BRAND_COLOR,
- theme: event.data?.profile.theme,
- });
-
const { bookerFormErrorRef, key, formEmail, bookingForm, errors: formErrors } = bookerForm;
const { handleBookEvent, errors, loadingStates, expiryTime } = bookings;
@@ -166,27 +158,34 @@ const BookerComponent = ({
bookingForm={bookingForm}
eventQuery={event}
extraOptions={extraOptions}
- rescheduleUid={rescheduleUid}>
+ rescheduleUid={rescheduleUid}
+ isPlatform={isPlatform}>
<>
-
- {
- onGoBackInstantMeeting();
- }}
- />
+ {verifyCode ? (
+
+ ) : (
+ <>>
+ )}
+ {!isPlatform && (
+ {
+ onGoBackInstantMeeting();
+ }}
+ />
+ )}
>
) : (
@@ -214,12 +213,13 @@ const BookerComponent = ({
setEmailVerificationModalVisible,
setSeatedEventData,
setSelectedTimeslot,
- verifyCode.error,
- verifyCode.isPending,
- verifyCode.resetErrors,
- verifyCode.setIsPending,
- verifyCode.verifyCodeWithSessionNotRequired,
- verifyCode.verifyCodeWithSessionRequired,
+ verifyCode?.error,
+ verifyCode?.isPending,
+ verifyCode?.resetErrors,
+ verifyCode?.setIsPending,
+ verifyCode?.verifyCodeWithSessionNotRequired,
+ verifyCode?.verifyCodeWithSessionRequired,
+ isPlatform,
]);
if (entity.isUnpublished) {
@@ -236,7 +236,7 @@ const BookerComponent = ({
return (
<>
- {event.data ? : null}
+ {event.data && !isPlatform ? : <>>}
{bookerState !== "booking" && event.data?.isInstantEvent && (
-
- isEmbed ? (
- <>>
- ) : (
- <>
- {
- onOverlayClickNoCalendar();
- }}
- />
- >
- )
- }
- />
+ {!isPlatform ? (
+
+ isEmbed ? (
+ <>>
+ ) : (
+ <>
+ {
+ onOverlayClickNoCalendar();
+ }}
+ />
+ >
+ )
+ }
+ />
+ ) : (
+ <>>
+ )}
)}
- {orgBannerUrl && (
+ {orgBannerUrl && !isPlatform && (
)}
-
+
{layout !== BookerLayouts.MONTH_VIEW &&
!(layout === "mobile" && bookerState === "booking") && (
@@ -413,6 +417,7 @@ const BookerComponent = ({
isLoading={schedule.isPending}
seatsPerTimeSlot={event.data?.seatsPerTimeSlot}
showAvailableSeatsCount={event.data?.seatsShowAvailabilityCount}
+ event={event}
/>
@@ -427,7 +432,7 @@ const BookerComponent = ({
}}
/>
- {!hideBranding && (
+ {!hideBranding && !isPlatform && (
- setSelectedTimeslot(null)}
- visible={bookerState === "booking" && shouldShowFormInDialog}>
- {EventBooker}
-
+ {!isPlatform ? (
+ setSelectedTimeslot(null)}
+ visible={bookerState === "booking" && shouldShowFormInDialog}>
+ {EventBooker}
+
+ ) : (
+ <>>
+ )}
>
);
};
diff --git a/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx b/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
index f308ce22f8906d..4d152e4811c357 100644
--- a/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
+++ b/packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
@@ -2,6 +2,7 @@ import { useRef } from "react";
import dayjs from "@calcom/dayjs";
import { AvailableTimes, AvailableTimesSkeleton } from "@calcom/features/bookings";
+import type { useEventReturnType } from "@calcom/features/bookings/Booker/utils/event";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
import { useSlotsForAvailableDates } from "@calcom/features/schedules/lib/use-schedule/useSlotsForDate";
import { classNames } from "@calcom/lib";
@@ -18,6 +19,7 @@ type AvailableTimeSlotsProps = {
isLoading: boolean;
seatsPerTimeSlot?: number | null;
showAvailableSeatsCount?: boolean | null;
+ event: useEventReturnType;
};
/**
@@ -34,6 +36,7 @@ export const AvailableTimeSlots = ({
showAvailableSeatsCount,
schedule,
isLoading,
+ event,
}: AvailableTimeSlotsProps) => {
const selectedDate = useBookerStore((state) => state.selectedDate);
const setSelectedTimeslot = useBookerStore((state) => state.setSelectedTimeslot);
@@ -116,6 +119,7 @@ export const AvailableTimeSlots = ({
slots={slots.slots}
seatsPerTimeSlot={seatsPerTimeSlot}
showAvailableSeatsCount={showAvailableSeatsCount}
+ event={event}
/>
))}
diff --git a/packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx b/packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx
index 317bffa221937f..30e6352f194522 100644
--- a/packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx
+++ b/packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx
@@ -27,6 +27,7 @@ type BookEventFormProps = {
bookingForm: UseBookingFormReturnType["bookingForm"];
renderConfirmNotVerifyEmailButtonCond: boolean;
extraOptions: Record;
+ isPlatform?: boolean;
};
export const BookEventForm = ({
@@ -41,6 +42,7 @@ export const BookEventForm = ({
bookingForm,
children,
extraOptions,
+ isPlatform = false,
}: Omit & {
eventQuery: useEventReturnType;
rescheduleUid: string | null;
@@ -111,7 +113,7 @@ export const BookEventForm = ({
/>
)}
- {IS_CALCOM && (
+ {!isPlatform && IS_CALCOM && (
By proceeding, you agree to our{" "}
@@ -172,11 +174,11 @@ const getError = (
t: TFunction,
responseVercelIdHeader: string | null
) => {
- if (globalError) return globalError.message;
+ if (globalError) return globalError?.message;
const error = dataError;
- return error.message ? (
+ return error?.message ? (
<>
{responseVercelIdHeader ?? ""} {t(error.message)}
>
diff --git a/packages/features/bookings/Booker/components/EventMeta.tsx b/packages/features/bookings/Booker/components/EventMeta.tsx
index 7c8d4c63314735..9d07759ca41707 100644
--- a/packages/features/bookings/Booker/components/EventMeta.tsx
+++ b/packages/features/bookings/Booker/components/EventMeta.tsx
@@ -1,6 +1,6 @@
import { m } from "framer-motion";
import dynamic from "next/dynamic";
-import { useEffect } from "react";
+import { useMemo, useEffect } from "react";
import { shallow } from "zustand/shallow";
import { useEmbedUiConfig, useIsEmbed } from "@calcom/embed-core/embed-iframe";
@@ -9,6 +9,7 @@ import { SeatsAvailabilityText } from "@calcom/features/bookings/components/Seat
import { EventMetaBlock } from "@calcom/features/bookings/components/event-meta/Details";
import { useTimePreferences } from "@calcom/features/bookings/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { Timezone as PlatformTimezoneSelect } from "@calcom/platform-atoms";
import { Calendar, Globe, User } from "@calcom/ui/components/icon";
import { fadeInUp } from "../config";
@@ -16,7 +17,7 @@ import { useBookerStore } from "../store";
import { FromToTime } from "../utils/dates";
import type { useEventReturnType } from "../utils/event";
-const TimezoneSelect = dynamic(
+const WebTimezoneSelect = dynamic(
() => import("@calcom/ui/components/form/timezone-select/TimezoneSelect").then((mod) => mod.TimezoneSelect),
{
ssr: false,
@@ -26,9 +27,11 @@ const TimezoneSelect = dynamic(
export const EventMeta = ({
event,
isPending,
+ isPlatform = true,
}: {
event: useEventReturnType["data"];
isPending: useEventReturnType["isPending"];
+ isPlatform?: boolean;
}) => {
const { setTimezone, timeFormat, timezone } = useTimePreferences();
const selectedDuration = useBookerStore((state) => state.selectedDuration);
@@ -44,6 +47,10 @@ export const EventMeta = ({
const embedUiConfig = useEmbedUiConfig();
const isEmbed = useIsEmbed();
const hideEventTypeDetails = isEmbed ? embedUiConfig.hideEventTypeDetails : false;
+ const [TimezoneSelect] = useMemo(
+ () => (isPlatform ? [PlatformTimezoneSelect] : [WebTimezoneSelect]),
+ [isPlatform]
+ );
useEffect(() => {
//In case the event has lockTimeZone enabled ,set the timezone to event's attached availability timezone
diff --git a/packages/features/bookings/Booker/components/hooks/useBookingForm.ts b/packages/features/bookings/Booker/components/hooks/useBookingForm.ts
index 0b05983038644c..ea6e6449539891 100644
--- a/packages/features/bookings/Booker/components/hooks/useBookingForm.ts
+++ b/packages/features/bookings/Booker/components/hooks/useBookingForm.ts
@@ -12,7 +12,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useInitialFormValues } from "./useInitialFormValues";
export interface IUseBookingForm {
- event: useEventReturnType;
+ event: useEventReturnType["data"];
sessionEmail?: string | null;
sessionName?: string | null;
sessionUsername?: string | null;
@@ -42,9 +42,9 @@ export const useBookingForm = ({
const bookingFormSchema = z
.object({
- responses: event?.data
+ responses: event
? getBookingResponsesSchema({
- bookingFields: event.data.bookingFields,
+ bookingFields: event.bookingFields,
view: rescheduleUid ? "reschedule" : "booking",
})
: // Fallback until event is loaded.
@@ -62,7 +62,7 @@ export const useBookingForm = ({
const isRescheduling = !!rescheduleUid && !!bookingData;
const { initialValues, key } = useInitialFormValues({
- eventType: event.data,
+ eventType: event,
rescheduleUid,
isRescheduling,
email: sessionEmail,
@@ -101,7 +101,7 @@ export const useBookingForm = ({
// It shouldn't be possible that this method is fired without having event data,
// but since in theory (looking at the types) it is possible, we still handle that case.
- if (!event?.data) {
+ if (!event) {
bookingForm.setError("globalError", { message: t("error_booking_event") });
return;
}
diff --git a/packages/features/bookings/Booker/components/hooks/useBookings.ts b/packages/features/bookings/Booker/components/hooks/useBookings.ts
index 15a2a518e2bc62..e36f646ae0b973 100644
--- a/packages/features/bookings/Booker/components/hooks/useBookings.ts
+++ b/packages/features/bookings/Booker/components/hooks/useBookings.ts
@@ -8,17 +8,11 @@ import { sdkActionManager } from "@calcom/embed-core/embed-iframe";
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import type { useEventReturnType } from "@calcom/features/bookings/Booker/utils/event";
import { updateQueryParam, getQueryParam } from "@calcom/features/bookings/Booker/utils/query-param";
-import {
- createBooking,
- createRecurringBooking,
- mapBookingToMutationInput,
- mapRecurringBookingToMutationInput,
- createInstantBooking,
- useTimePreferences,
-} from "@calcom/features/bookings/lib";
+import { createBooking, createRecurringBooking, createInstantBooking } from "@calcom/features/bookings/lib";
import { getFullName } from "@calcom/features/form-builder/utils";
import { useBookingSuccessRedirect } from "@calcom/lib/bookingSuccessRedirect";
import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { useHandleBookEvent } from "@calcom/platform-atoms";
import { BookingStatus } from "@calcom/prisma/enums";
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc";
@@ -48,20 +42,14 @@ export type UseBookingsReturnType = ReturnType;
export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBookings) => {
const router = useRouter();
const eventSlug = useBookerStore((state) => state.eventSlug);
- const setFormValues = useBookerStore((state) => state.setFormValues);
const rescheduleUid = useBookerStore((state) => state.rescheduleUid);
const bookingData = useBookerStore((state) => state.bookingData);
const timeslot = useBookerStore((state) => state.selectedTimeslot);
- const seatedEventData = useBookerStore((state) => state.seatedEventData);
- const { t, i18n } = useLocale();
+ const { t } = useLocale();
const bookingSuccessRedirect = useBookingSuccessRedirect();
const bookerFormErrorRef = useRef(null);
const [instantMeetingTokenExpiryTime, setExpiryTime] = useState();
- const recurringEventCount = useBookerStore((state) => state.recurringEventCount);
- const isInstantMeeting = useBookerStore((state) => state.isInstantMeeting);
const duration = useBookerStore((state) => state.selectedDuration);
- const { timezone } = useTimePreferences();
- const username = useBookerStore((state) => state.username);
const isRescheduling = !!rescheduleUid && !!bookingData;
@@ -229,56 +217,15 @@ export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBo
},
});
- const handleBookEvent = () => {
- const values = bookingForm.getValues();
- if (timeslot) {
- // Clears form values stored in store, so old values won't stick around.
- setFormValues({});
- bookingForm.clearErrors();
-
- // It shouldn't be possible that this method is fired without having event data,
- // but since in theory (looking at the types) it is possible, we still handle that case.
- if (!event?.data) {
- bookingForm.setError("globalError", { message: t("error_booking_event") });
- return;
- }
-
- // Ensures that duration is an allowed value, if not it defaults to the
- // default event duration.
- const validDuration = event.data.isDynamic
- ? duration || event.data.length
- : duration && event.data.metadata?.multipleDuration?.includes(duration)
- ? duration
- : event.data.length;
-
- const bookingInput = {
- values,
- duration: validDuration,
- event: event.data,
- date: timeslot,
- timeZone: timezone,
- language: i18n.language,
- rescheduleUid: rescheduleUid || undefined,
- bookingUid: (bookingData && bookingData.uid) || seatedEventData?.bookingUid || undefined,
- username: username || "",
- metadata: metadata,
- hashedLink,
- };
-
- if (isInstantMeeting) {
- createInstantBookingMutation.mutate(mapBookingToMutationInput(bookingInput));
- } else if (event.data?.recurringEvent?.freq && recurringEventCount && !rescheduleUid) {
- createRecurringBookingMutation.mutate(
- mapRecurringBookingToMutationInput(bookingInput, recurringEventCount)
- );
- } else {
- createBookingMutation.mutate(mapBookingToMutationInput(bookingInput));
- }
- // Clears form values stored in store, so old values won't stick around.
- setFormValues({});
- bookingForm.clearErrors();
- }
- };
+ const handleBookEvent = useHandleBookEvent({
+ event,
+ bookingForm,
+ hashedLink,
+ metadata,
+ handleInstantBooking: createInstantBookingMutation.mutate,
+ handleRecBooking: createRecurringBookingMutation.mutate,
+ handleBooking: createBookingMutation.mutate,
+ });
const errors = {
hasDataErrors: Boolean(
diff --git a/packages/features/bookings/Booker/types.ts b/packages/features/bookings/Booker/types.ts
index 0c851753c6cab2..6586847a4c5131 100644
--- a/packages/features/bookings/Booker/types.ts
+++ b/packages/features/bookings/Booker/types.ts
@@ -87,7 +87,7 @@ export interface BookerProps {
isInstantMeeting?: boolean;
}
-export type WrappedBookerProps = {
+export type WrappedBookerPropsMain = {
sessionUsername?: string | null;
rescheduleUid: string | null;
bookingUid: string | null;
@@ -103,14 +103,24 @@ export type WrappedBookerProps = {
bookings: UseBookingsReturnType;
slots: UseSlotsReturnType;
calendars: UseCalendarsReturnType;
- verifyEmail: UseVerifyEmailReturnType;
bookerForm: UseBookingFormReturnType;
event: useEventReturnType;
schedule: useScheduleForEventReturnType;
bookerLayout: UseBookerLayoutType;
+ verifyEmail: UseVerifyEmailReturnType;
+};
+
+export type WrappedBookerPropsForPlatform = WrappedBookerPropsMain & {
+ isPlatform: true;
+ verifyCode: undefined;
+};
+export type WrappedBookerPropsForWeb = WrappedBookerPropsMain & {
+ isPlatform: false;
verifyCode: UseVerifyCodeReturnType;
};
+export type WrappedBookerProps = WrappedBookerPropsForPlatform | WrappedBookerPropsForWeb;
+
export type BookerState = "loading" | "selecting_date" | "selecting_time" | "booking";
export type BookerLayout = BookerLayouts | "mobile";
export type BookerAreas = "calendar" | "timeslots" | "main" | "meta" | "header";
diff --git a/packages/features/bookings/Booker/utils/event.ts b/packages/features/bookings/Booker/utils/event.ts
index 763b85941f75b4..150166d4072979 100644
--- a/packages/features/bookings/Booker/utils/event.ts
+++ b/packages/features/bookings/Booker/utils/event.ts
@@ -24,10 +24,17 @@ export const useEvent = () => {
const isTeamEvent = useBookerStore((state) => state.isTeamEvent);
const org = useBookerStore((state) => state.org);
- return trpc.viewer.public.event.useQuery(
+ const event = trpc.viewer.public.event.useQuery(
{ username: username ?? "", eventSlug: eventSlug ?? "", isTeamEvent, org: org ?? null },
{ refetchOnWindowFocus: false, enabled: Boolean(username) && Boolean(eventSlug) }
);
+
+ return {
+ data: event?.data,
+ isSuccess: event?.isSuccess,
+ isError: event?.isError,
+ isPending: event?.isPending,
+ };
};
/**
@@ -79,7 +86,7 @@ export const useScheduleForEvent = ({
const isTeam = !!event.data?.team?.parentId;
- return useSchedule({
+ const schedule = useSchedule({
username: usernameFromStore ?? username,
eventSlug: eventSlugFromStore ?? eventSlug,
eventId: event.data?.id ?? eventId,
@@ -94,4 +101,12 @@ export const useScheduleForEvent = ({
isTeamEvent: pathname?.indexOf("/team/") !== -1 || isTeam,
orgSlug,
});
+
+ return {
+ data: schedule?.data,
+ isPending: schedule?.isPending,
+ isError: schedule?.isError,
+ isSuccess: schedule?.isSuccess,
+ isLoading: schedule?.isLoading,
+ };
};
diff --git a/packages/features/bookings/components/AvailableTimes.tsx b/packages/features/bookings/components/AvailableTimes.tsx
index c91b6157c0a00f..0d9ff9f00da274 100644
--- a/packages/features/bookings/components/AvailableTimes.tsx
+++ b/packages/features/bookings/components/AvailableTimes.tsx
@@ -12,7 +12,7 @@ import { localStorage } from "@calcom/lib/webstorage";
import { Button, SkeletonText } from "@calcom/ui";
import { useBookerStore } from "../Booker/store";
-import { useEvent } from "../Booker/utils/event";
+import type { useEventReturnType } from "../Booker/utils/event";
import { getQueryParam } from "../Booker/utils/query-param";
import { useTimePreferences } from "../lib";
import { useCheckOverlapWithOverlay } from "../lib/useCheckOverlapWithOverlay";
@@ -33,6 +33,7 @@ type AvailableTimesProps = {
showTimeFormatToggle?: boolean;
className?: string;
selectedSlots?: string[];
+ event: useEventReturnType;
};
const SlotItem = ({
@@ -41,12 +42,14 @@ const SlotItem = ({
selectedSlots,
onTimeSelect,
showAvailableSeatsCount,
+ event,
}: {
slot: Slots[string][number];
seatsPerTimeSlot?: number | null;
selectedSlots?: string[];
onTimeSelect: TOnTimeSelect;
showAvailableSeatsCount?: boolean | null;
+ event: useEventReturnType;
}) => {
const { t } = useLocale();
@@ -55,7 +58,7 @@ const SlotItem = ({
const [timeFormat, timezone] = useTimePreferences((state) => [state.timeFormat, state.timezone]);
const bookingData = useBookerStore((state) => state.bookingData);
const layout = useBookerStore((state) => state.layout);
- const { data: event } = useEvent();
+ const { data: eventData } = event;
const hasTimeSlots = !!seatsPerTimeSlot;
const computedDateWithUsersTimezone = dayjs.utc(slot.time).tz(timezone);
@@ -71,7 +74,7 @@ const SlotItem = ({
const { isOverlapping, overlappingTimeEnd, overlappingTimeStart } = useCheckOverlapWithOverlay({
start: computedDateWithUsersTimezone,
- selectedDuration: event?.length ?? 0,
+ selectedDuration: eventData?.length ?? 0,
offset,
});
@@ -187,6 +190,7 @@ export const AvailableTimes = ({
showTimeFormatToggle = true,
className,
selectedSlots,
+ event,
}: AvailableTimesProps) => {
const { t } = useLocale();
@@ -211,6 +215,7 @@ export const AvailableTimes = ({
selectedSlots={selectedSlots}
seatsPerTimeSlot={seatsPerTimeSlot}
showAvailableSeatsCount={showAvailableSeatsCount}
+ event={event}
/>
))}
diff --git a/packages/features/bookings/components/event-meta/AvailableEventLocations.tsx b/packages/features/bookings/components/event-meta/AvailableEventLocations.tsx
index 0eca36957cf6cd..bd3264b5dfd289 100644
--- a/packages/features/bookings/components/event-meta/AvailableEventLocations.tsx
+++ b/packages/features/bookings/components/event-meta/AvailableEventLocations.tsx
@@ -7,8 +7,10 @@ import { getEventLocationType, getTranslatedLocation } from "@calcom/app-store/l
import { classNames } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import invertLogoOnDark from "@calcom/lib/invertLogoOnDark";
+import { useIsPlatform } from "@calcom/platform-atoms";
import { Tooltip } from "@calcom/ui";
import { Link } from "@calcom/ui/components/icon";
+import { Video } from "@calcom/ui/components/icon";
const excludeNullValues = (value: unknown) => !!value;
@@ -19,6 +21,12 @@ function RenderIcon({
eventLocationType: DefaultEventLocationType | EventLocationTypeFromApp;
isTooltip: boolean;
}) {
+ const isPlatform = useIsPlatform();
+
+ if (isPlatform) {
+ return ;
+ }
+
return (
{
+ const isPlatform = useIsPlatform();
const showMembers = schedulingType !== SchedulingType.ROUND_ROBIN;
- const shownUsers = showMembers ? users : [];
+ const shownUsers = showMembers && !isPlatform ? users : [];
// In some cases we don't show the user's names, but only show the profile name.
const showOnlyProfileName =
diff --git a/packages/features/bookings/types.ts b/packages/features/bookings/types.ts
index 204188b65099f9..c327a5b5543ba9 100644
--- a/packages/features/bookings/types.ts
+++ b/packages/features/bookings/types.ts
@@ -1,11 +1,10 @@
+import type { SchedulingType } from "@prisma/client";
import type { ErrorOption, FieldPath } from "react-hook-form";
import type { BookingCreateBody } from "@calcom/prisma/zod-utils";
import type { RouterOutputs } from "@calcom/trpc/react";
import type { AppsStatus } from "@calcom/types/Calendar";
-import type { SchedulingType } from ".prisma/client";
-
export type PublicEvent = NonNullable;
export type ValidationErrors = { key: FieldPath; error: ErrorOption }[];
diff --git a/packages/features/ee/teams/components/TeamList.tsx b/packages/features/ee/teams/components/TeamList.tsx
index 393b2d9553d9ba..b97da81d4fe860 100644
--- a/packages/features/ee/teams/components/TeamList.tsx
+++ b/packages/features/ee/teams/components/TeamList.tsx
@@ -63,7 +63,7 @@ export default function TeamList(props: Props) {
(team, i) =>
team.role !== "MEMBER" &&
i === 0 && (
-
+
{t("recommended_next_steps")}
) : null}
diff --git a/packages/features/form-builder/index.ts b/packages/features/form-builder/index.ts
index 8f5cae07e0bb89..88af5facf14caa 100644
--- a/packages/features/form-builder/index.ts
+++ b/packages/features/form-builder/index.ts
@@ -2,4 +2,4 @@
// It has zod schema associated with it and I currently can't import zod in there.
// Move it later there maybe? @sean
export { FormBuilder } from "./FormBuilder";
-export { FormBuilderField } from "./FormBuilder";
+export { FormBuilderField } from "./FormBuilderField";
diff --git a/packages/features/insights/server/trpc-router.ts b/packages/features/insights/server/trpc-router.ts
index 43fb97bf393ad7..b02ecfcc8de64b 100644
--- a/packages/features/insights/server/trpc-router.ts
+++ b/packages/features/insights/server/trpc-router.ts
@@ -118,7 +118,7 @@ const emptyResponseEventsByStatus = {
},
};
-interface IResultTeamList {
+export interface IResultTeamList {
id: number;
slug: string | null;
name: string | null;
diff --git a/packages/features/schedules/components/DateOverrideInputDialog.tsx b/packages/features/schedules/components/DateOverrideInputDialog.tsx
index a628e81b0cba6f..f1d7c72e6341a4 100644
--- a/packages/features/schedules/components/DateOverrideInputDialog.tsx
+++ b/packages/features/schedules/components/DateOverrideInputDialog.tsx
@@ -27,12 +27,14 @@ const DateOverrideForm = ({
workingHours,
excludedDates,
onChange,
+ userTimeFormat,
}: {
workingHours?: WorkingHours[];
onChange: (newValue: TimeRange[]) => void;
excludedDates: string[];
value?: TimeRange[];
onClose?: () => void;
+ userTimeFormat: number | null;
}) => {
const [browsingDate, setBrowsingDate] = useState
();
const { t, i18n, isLocaleReady } = useLocale();
@@ -163,7 +165,7 @@ const DateOverrideForm = ({
{t("date_overrides_unavailable")}
) : (
-
+
)}
void;
value?: TimeRange[];
+ userTimeFormat: number | null;
}) => {
const [open, setOpen] = useState(false);
return (
@@ -219,6 +223,7 @@ const DateOverrideInputDialog = ({
excludedDates={excludedDates}
{...passThroughProps}
onClose={() => setOpen(false)}
+ userTimeFormat={userTimeFormat}
/>
diff --git a/packages/features/schedules/components/DateOverrideList.tsx b/packages/features/schedules/components/DateOverrideList.tsx
index 5083f5f49b2d40..06c6dd6985b266 100644
--- a/packages/features/schedules/components/DateOverrideList.tsx
+++ b/packages/features/schedules/components/DateOverrideList.tsx
@@ -13,6 +13,7 @@ const sortByDate = (a: { ranges: TimeRange[]; id: string }, b: { ranges: TimeRan
const DateOverrideList = ({
workingHours,
excludedDates = [],
+ userTimeFormat,
hour12,
replace,
fields,
@@ -22,6 +23,7 @@ const DateOverrideList = ({
fields: { ranges: TimeRange[]; id: string }[];
workingHours: WorkingHours[];
excludedDates?: string[];
+ userTimeFormat: number | null;
hour12: boolean;
}) => {
const { t, i18n } = useLocale();
@@ -68,6 +70,7 @@ const DateOverrideList = ({
= {
[Key in FieldPath]: FieldPathValue extends TValue ? Key : never;
}[FieldPath];
-const ScheduleDay = ({
+export const ScheduleDay = ({
name,
weekday,
control,
CopyButton,
disabled,
+ labels,
+ userTimeFormat,
+ className,
}: {
name: ArrayPath;
weekday: string;
control: Control;
CopyButton: JSX.Element;
disabled?: boolean;
+ labels?: ScheduleLabelsType;
+ userTimeFormat: number | null;
+ className?: {
+ scheduleDay?: string;
+ dayRanges?: string;
+ timeRangeField?: string;
+ labelAndSwitchContainer?: string;
+ };
}) => {
const { watch, setValue } = useFormContext();
const watchDayRange = watch(name);
return (
-
+
{/* Label & switch container */}
-
+
@@ -77,7 +103,17 @@ const ScheduleDay =
({
<>
{watchDayRange ? (
-
+
{!!watchDayRange.length && !disabled &&
{CopyButton}
}
) : (
@@ -91,9 +127,11 @@ const ScheduleDay = ({
const CopyButton = ({
getValuesFromDayRange,
weekStart,
+ labels,
}: {
getValuesFromDayRange: string;
weekStart: number;
+ labels?: ScheduleLabelsType;
}) => {
const { t } = useLocale();
const [open, setOpen] = useState(false);
@@ -109,7 +147,7 @@ const CopyButton = ({
)}
data-testid="copy-button"
type="button"
- tooltip={t("copy_times_to_tooltip")}
+ tooltip={labels?.copyTime ?? t("copy_times_to_tooltip")}
color="minimal"
variant="icon"
StartIcon={Copy}
@@ -133,33 +171,72 @@ const CopyButton = ({
const Schedule = <
TFieldValues extends FieldValues,
TPath extends FieldPathByValue
+>(props: {
+ name: TPath;
+ control: Control;
+ weekStart?: number;
+ disabled?: boolean;
+ labels?: ScheduleLabelsType;
+ userTimeFormat?: number | null;
+}) => {
+ const query = useMeQuery();
+ const { timeFormat } = query.data || { timeFormat: null };
+
+ return ;
+};
+
+export const ScheduleComponent = <
+ TFieldValues extends FieldValues,
+ TPath extends FieldPathByValue
>({
name,
control,
disabled,
weekStart = 0,
+ labels,
+ userTimeFormat,
+ className,
}: {
name: TPath;
control: Control;
weekStart?: number;
disabled?: boolean;
+ labels?: ScheduleLabelsType;
+ userTimeFormat: number | null;
+ className?: {
+ schedule?: string;
+ scheduleDay?: string;
+ dayRanges?: string;
+ timeRanges?: string;
+ labelAndSwitchContainer?: string;
+ };
}) => {
const { i18n } = useLocale();
return (
-
+
{/* First iterate for each day */}
{weekdayNames(i18n.language, weekStart, "long").map((weekday, num) => {
const weekdayIndex = (num + weekStart) % 7;
const dayRangeName = `${name}.${weekdayIndex}` as ArrayPath
;
return (
}
+ CopyButton={
+
+ }
/>
);
})}
@@ -171,10 +248,19 @@ export const DayRanges = ({
name,
disabled,
control,
+ labels,
+ userTimeFormat,
+ className,
}: {
name: ArrayPath;
control?: Control;
disabled?: boolean;
+ labels?: ScheduleLabelsType;
+ userTimeFormat: number | null;
+ className?: {
+ dayRanges?: string;
+ timeRangeField?: string;
+ };
}) => {
const { t } = useLocale();
const { getValues } = useFormContext();
@@ -185,16 +271,25 @@ export const DayRanges = ({
});
return (
-
+
{fields.map((field, index: number) => (
-
} />
+ (
+
+ )}
+ />
{index === 0 && (
{
const { t } = useLocale();
return (
@@ -248,7 +345,7 @@ const RemoveTimeButton = ({
StartIcon={Trash}
onClick={() => remove(index)}
className={className}
- tooltip={t("delete")}
+ tooltip={labels?.deleteTime ?? t("delete")}
/>
);
};
@@ -258,11 +355,13 @@ const TimeRangeField = ({
value,
onChange,
disabled,
-}: { className?: string; disabled?: boolean } & ControllerRenderProps) => {
+ userTimeFormat,
+}: { className?: string; disabled?: boolean; userTimeFormat: number | null } & ControllerRenderProps) => {
// this is a controlled component anyway given it uses LazySelect, so keep it RHF agnostic.
return (
-
>, "value"> & {
value: ConfigType;
min?: ConfigType;
max?: ConfigType;
+ userTimeFormat: number | null;
}) => {
// Lazy-loaded options, otherwise adding a field has a noticeable redraw delay.
- const { options, filter } = useOptions();
+ const { options, filter } = useOptions(userTimeFormat);
useEffect(() => {
filter({ current: value });
@@ -329,11 +431,7 @@ interface IOption {
*/
/** Begin Time Increments For Select */
const INCREMENT = Number(process.env.NEXT_PUBLIC_AVAILABILITY_SCHEDULE_INTERVAL) || 15;
-const useOptions = () => {
- // Get user so we can determine 12/24 hour format preferences
- const query = useMeQuery();
- const { timeFormat } = query.data || { timeFormat: null };
-
+const useOptions = (timeFormat: number | null) => {
const [filteredOptions, setFilteredOptions] = useState([]);
const options = useMemo(() => {
diff --git a/packages/features/schedules/components/index.ts b/packages/features/schedules/components/index.ts
index 7d8653ecad1cf4..0da678d3ba91ad 100644
--- a/packages/features/schedules/components/index.ts
+++ b/packages/features/schedules/components/index.ts
@@ -3,3 +3,4 @@ export { ScheduleListItem } from "./ScheduleListItem";
export { default as DateOverrideInputDialog } from "./DateOverrideInputDialog";
export { default as Schedule } from "./Schedule";
export { default as DateOverrideList } from "./DateOverrideList";
+export { ScheduleComponent } from "./Schedule";
diff --git a/packages/features/schedules/lib/use-schedule/useSchedule.ts b/packages/features/schedules/lib/use-schedule/useSchedule.ts
index d84cb227aafbe0..329278f3e1a877 100644
--- a/packages/features/schedules/lib/use-schedule/useSchedule.ts
+++ b/packages/features/schedules/lib/use-schedule/useSchedule.ts
@@ -1,8 +1,8 @@
-import dayjs from "@calcom/dayjs";
+import { useTimesForSchedule } from "@calcom/features/schedules/lib/use-schedule/useTimesForSchedule";
import { getUsernameList } from "@calcom/lib/defaultEvents";
import { trpc } from "@calcom/trpc/react";
-type UseScheduleWithCacheArgs = {
+export type UseScheduleWithCacheArgs = {
username?: string | null;
eventSlug?: string | null;
eventId?: number | null;
@@ -33,30 +33,13 @@ export const useSchedule = ({
isTeamEvent,
orgSlug,
}: UseScheduleWithCacheArgs) => {
- const now = dayjs();
- const monthDayjs = month ? dayjs(month) : now;
- const nextMonthDayjs = monthDayjs.add(monthCount ? monthCount : 1, "month");
- // Why the non-null assertions? All of these arguments are checked in the enabled condition,
- // and the query will not run if they are null. However, the check in `enabled` does
- // no satisfy typescript.
- let startTime;
- let endTime;
-
- if (!!dayCount && dayCount > 0) {
- if (selectedDate) {
- startTime = dayjs(selectedDate).toISOString();
- endTime = dayjs(selectedDate).add(dayCount, "day").toISOString();
- } else if (monthDayjs.month() === now.month()) {
- startTime = now.startOf("day").toISOString();
- endTime = now.startOf("day").add(dayCount, "day").toISOString();
- } else {
- startTime = monthDayjs.startOf("month").toISOString();
- endTime = monthDayjs.startOf("month").add(dayCount, "day").toISOString();
- }
- } else {
- startTime = monthDayjs.startOf("month").toISOString();
- endTime = (prefetchNextMonth ? nextMonthDayjs : monthDayjs).endOf("month").toISOString();
- }
+ const [startTime, endTime] = useTimesForSchedule({
+ month,
+ monthCount,
+ dayCount,
+ prefetchNextMonth,
+ selectedDate,
+ });
return trpc.viewer.public.slots.getSchedule.useQuery(
{
diff --git a/packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts b/packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts
new file mode 100644
index 00000000000000..eeb62d6d8ed6ee
--- /dev/null
+++ b/packages/features/schedules/lib/use-schedule/useTimesForSchedule.ts
@@ -0,0 +1,41 @@
+import dayjs from "@calcom/dayjs";
+
+import type { UseScheduleWithCacheArgs } from "./useSchedule";
+
+type UseTimesForScheduleProps = Pick<
+ UseScheduleWithCacheArgs,
+ "month" | "monthCount" | "dayCount" | "selectedDate" | "prefetchNextMonth"
+>;
+export const useTimesForSchedule = ({
+ month,
+ monthCount,
+ selectedDate,
+ dayCount,
+ prefetchNextMonth,
+}: UseTimesForScheduleProps): [string, string] => {
+ const now = dayjs();
+ const monthDayjs = month ? dayjs(month) : now;
+ const nextMonthDayjs = monthDayjs.add(monthCount ? monthCount : 1, "month");
+ // Why the non-null assertions? All of these arguments are checked in the enabled condition,
+ // and the query will not run if they are null. However, the check in `enabled` does
+ // no satisfy typescript.
+ let startTime;
+ let endTime;
+
+ if (!!dayCount && dayCount > 0) {
+ if (selectedDate) {
+ startTime = dayjs(selectedDate).toISOString();
+ endTime = dayjs(selectedDate).add(dayCount, "day").toISOString();
+ } else if (monthDayjs.month() === now.month()) {
+ startTime = now.startOf("day").toISOString();
+ endTime = now.startOf("day").add(dayCount, "day").toISOString();
+ } else {
+ startTime = monthDayjs.startOf("month").toISOString();
+ endTime = monthDayjs.startOf("month").add(dayCount, "day").toISOString();
+ }
+ } else {
+ startTime = monthDayjs.startOf("month").toISOString();
+ endTime = (prefetchNextMonth ? nextMonthDayjs : monthDayjs).endOf("month").toISOString();
+ }
+ return [startTime, endTime];
+};
diff --git a/packages/features/settings/layouts/SettingsLayout.tsx b/packages/features/settings/layouts/SettingsLayout.tsx
index 08314496fa29ce..e981d257c45b0f 100644
--- a/packages/features/settings/layouts/SettingsLayout.tsx
+++ b/packages/features/settings/layouts/SettingsLayout.tsx
@@ -104,6 +104,7 @@ const tabs: VerticalTabItemProps[] = [
name: "billing",
href: "/settings/organizations/billing",
},
+ { name: "OAuth Clients", href: "/settings/organizations/platform/oauth-clients" },
{
name: "directory_sync",
href: "/settings/organizations/dsync",
@@ -149,7 +150,6 @@ const useTabs = () => {
const session = useSession();
const { data: user } = trpc.viewer.me.useQuery();
const orgBranding = useOrgBranding();
-
const isAdmin = session.data?.user.role === UserPermissionRole.ADMIN;
tabs.map((tab) => {
diff --git a/packages/features/shell/Shell.tsx b/packages/features/shell/Shell.tsx
index a26db7f1d5fe50..aa5a1b8bad58ee 100644
--- a/packages/features/shell/Shell.tsx
+++ b/packages/features/shell/Shell.tsx
@@ -312,7 +312,7 @@ const Layout = (props: LayoutProps) => {
type DrawerState = [isOpen: boolean, setDrawerOpen: Dispatch>];
-type LayoutProps = {
+export type LayoutProps = {
centered?: boolean;
title?: string;
description?: string;
diff --git a/packages/features/timezone-buddy/components/AvailabilityEditSheet.tsx b/packages/features/timezone-buddy/components/AvailabilityEditSheet.tsx
index d159a31b6ce1aa..ee4a6ef3b3f11f 100644
--- a/packages/features/timezone-buddy/components/AvailabilityEditSheet.tsx
+++ b/packages/features/timezone-buddy/components/AvailabilityEditSheet.tsx
@@ -41,13 +41,12 @@ type AvailabilityFormValues = {
const useSettings = () => {
const { data } = useMeQuery();
return {
- hour12: data?.timeFormat === 12,
- timeZone: data?.timeZone,
+ userTimeFormat: data?.timeFormat ?? 12,
};
};
const DateOverride = ({ workingHours, disabled }: { workingHours: WorkingHours[]; disabled?: boolean }) => {
- const { hour12 } = useSettings();
+ const { userTimeFormat } = useSettings();
const { append, replace, fields } = useFieldArray({
name: "dateOverrides",
@@ -62,10 +61,12 @@ const DateOverride = ({ workingHours, disabled }: { workingHours: WorkingHours[]
excludedDates={excludedDates}
replace={replace}
fields={fields}
- hour12={hour12}
+ hour12={Boolean(userTimeFormat === 12)}
workingHours={workingHours}
+ userTimeFormat={userTimeFormat}
/>
ranges.forEach((range) => append({ ranges: [range] }))}
diff --git a/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx b/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx
index 235fd4d4c154fb..5f2616ba06e4b9 100644
--- a/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx
+++ b/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx
@@ -59,7 +59,7 @@ function UpgradeTeamTip() {
);
}
-export function AvailabilitySliderTable() {
+export function AvailabilitySliderTable(props: { userTimeFormat: number | null }) {
const tableContainerRef = useRef(null);
const [browsingDate, setBrowsingDate] = useState(dayjs());
const [editSheetOpen, setEditSheetOpen] = useState(false);
diff --git a/packages/features/tsconfig.json b/packages/features/tsconfig.json
index baae362e9a8b6e..3c686980ac70a5 100644
--- a/packages/features/tsconfig.json
+++ b/packages/features/tsconfig.json
@@ -9,5 +9,5 @@
"esModuleInterop": true
},
"include": [".", "../types/next-auth.d.ts"],
- "exclude": ["dist", "build", "node_modules"]
+ "exclude": ["dist", "build", "node_modules", "**/*.test.*", "**/__mocks__/*", "**/__tests__/*"]
}
diff --git a/packages/kysely/.gitignore b/packages/kysely/.gitignore
new file mode 100644
index 00000000000000..ead638761e9da6
--- /dev/null
+++ b/packages/kysely/.gitignore
@@ -0,0 +1,2 @@
+# Generated dynamically based on Prisma schema
+types.ts
\ No newline at end of file
diff --git a/packages/kysely/README.md b/packages/kysely/README.md
new file mode 100644
index 00000000000000..368094fecc563f
--- /dev/null
+++ b/packages/kysely/README.md
@@ -0,0 +1 @@
+# kysely
diff --git a/packages/kysely/index.ts b/packages/kysely/index.ts
new file mode 100644
index 00000000000000..359c68a7b361cb
--- /dev/null
+++ b/packages/kysely/index.ts
@@ -0,0 +1,20 @@
+import { Kysely, PostgresDialect } from "kysely";
+import { Pool } from "pg";
+
+import type { DB } from "./types.ts";
+
+const dialect = new PostgresDialect({
+ pool: new Pool({
+ database: "calendso",
+ host: "localhost",
+ user: "postgres",
+ password: "postgres",
+ port: 5450,
+ }),
+});
+
+const db = new Kysely({
+ dialect,
+});
+
+export default db;
diff --git a/packages/kysely/package.json b/packages/kysely/package.json
new file mode 100644
index 00000000000000..bff3626eab1799
--- /dev/null
+++ b/packages/kysely/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@calcom/kysely",
+ "private": true,
+ "main": "index.ts",
+ "dependencies": {
+ "kysely": "^0.26.3",
+ "pg": "^8.11.3"
+ },
+ "devDependencies": {
+ "@types/pg": "^8.10.7"
+ }
+}
diff --git a/packages/kysely/utils/json/traverse/index.ts b/packages/kysely/utils/json/traverse/index.ts
new file mode 100644
index 00000000000000..10c63ec42c8924
--- /dev/null
+++ b/packages/kysely/utils/json/traverse/index.ts
@@ -0,0 +1,14 @@
+import type { ExpressionBuilder, StringReference } from "kysely";
+import { sql } from "kysely";
+
+export function traverseJSON(
+ eb: ExpressionBuilder,
+ column: StringReference,
+ path: string | [string, ...string[]]
+) {
+ if (!Array.isArray(path)) {
+ path = [path];
+ }
+
+ return sql`${sql.ref(column)}->${sql.raw(path.map((item) => `'${item}'`).join("->"))}`;
+}
diff --git a/packages/trpc/server/routers/publicViewer/cityTimezones.handler.ts b/packages/lib/cityTimezonesHandler.ts
similarity index 83%
rename from packages/trpc/server/routers/publicViewer/cityTimezones.handler.ts
rename to packages/lib/cityTimezonesHandler.ts
index 9d8d0663e4edc2..28db70544cbcb7 100644
--- a/packages/trpc/server/routers/publicViewer/cityTimezones.handler.ts
+++ b/packages/lib/cityTimezonesHandler.ts
@@ -1,9 +1,8 @@
+import { cityMapping as allCities } from "city-timezones";
+
+export type CityTimezones = Awaited>;
+
export const cityTimezonesHandler = async () => {
- /**
- * Lazy loads third party dependency to avoid loading 1.5Mb for ALL tRPC procedures.
- * Thanks @roae for the tip đ
- **/
- const allCities = await import("city-timezones").then((mod) => mod.cityMapping);
/**
* Filter out all cities that have the same "city" key and only use the one with the highest population.
* This way we return a new array of cities without running the risk of having more than one city
diff --git a/packages/lib/getConnectedDestinationCalendars.ts b/packages/lib/getConnectedDestinationCalendars.ts
new file mode 100644
index 00000000000000..0e818cdd8ba85d
--- /dev/null
+++ b/packages/lib/getConnectedDestinationCalendars.ts
@@ -0,0 +1,172 @@
+import { getCalendarCredentials, getConnectedCalendars } from "@calcom/core/CalendarManager";
+import type { PrismaClient } from "@calcom/prisma";
+import type { DestinationCalendar, SelectedCalendar, User } from "@calcom/prisma/client";
+import { AppCategories } from "@calcom/prisma/enums";
+import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
+
+type UserWithCalendars = Pick & {
+ selectedCalendars: Pick[];
+ destinationCalendar: DestinationCalendar | null;
+};
+
+export type ConnectedDestinationCalendars = Awaited>;
+
+export async function getConnectedDestinationCalendars(
+ user: UserWithCalendars,
+ onboarding: boolean,
+ prisma: PrismaClient
+) {
+ const userCredentials = await prisma.credential.findMany({
+ where: {
+ userId: user.id,
+ app: {
+ categories: { has: AppCategories.calendar },
+ enabled: true,
+ },
+ },
+ select: credentialForCalendarServiceSelect,
+ });
+
+ // get user's credentials + their connected integrations
+ const calendarCredentials = getCalendarCredentials(userCredentials);
+
+ // get all the connected integrations' calendars (from third party)
+ const { connectedCalendars, destinationCalendar } = await getConnectedCalendars(
+ calendarCredentials,
+ user.selectedCalendars,
+ user.destinationCalendar?.externalId
+ );
+ let toggledCalendarDetails:
+ | {
+ externalId: string;
+ integration: string;
+ }
+ | undefined;
+
+ if (connectedCalendars.length === 0) {
+ /* As there are no connected calendars, delete the destination calendar if it exists */
+ if (user.destinationCalendar) {
+ await prisma.destinationCalendar.delete({
+ where: { userId: user.id },
+ });
+ user.destinationCalendar = null;
+ }
+ } else if (!user.destinationCalendar) {
+ /*
+ There are connected calendars, but no destination calendar
+ So create a default destination calendar with the first primary connected calendar
+ */
+ const {
+ integration = "",
+ externalId = "",
+ credentialId,
+ email: primaryEmail,
+ } = connectedCalendars[0].primary ?? {};
+ // Select the first calendar matching the primary by default since that will also be the destination calendar
+ if (onboarding && externalId) {
+ const calendarIndex = (connectedCalendars[0].calendars || []).findIndex(
+ (item) => item.externalId === externalId && item.integration === integration
+ );
+ if (calendarIndex >= 0 && connectedCalendars[0].calendars) {
+ connectedCalendars[0].calendars[calendarIndex].isSelected = true;
+ toggledCalendarDetails = {
+ externalId,
+ integration,
+ };
+ }
+ }
+ user.destinationCalendar = await prisma.destinationCalendar.create({
+ data: {
+ userId: user.id,
+ integration,
+ externalId,
+ credentialId,
+ primaryEmail,
+ },
+ });
+ } else {
+ /* There are connected calendars and a destination calendar */
+
+ // Check if destinationCalendar exists in connectedCalendars
+ const allCals = connectedCalendars.map((cal) => cal.calendars ?? []).flat();
+ const destinationCal = allCals.find(
+ (cal) =>
+ cal.externalId === user.destinationCalendar?.externalId &&
+ cal.integration === user.destinationCalendar?.integration
+ );
+
+ if (!destinationCal) {
+ // If destinationCalendar is out of date, update it with the first primary connected calendar
+ const { integration = "", externalId = "", email: primaryEmail } = connectedCalendars[0].primary ?? {};
+ // Select the first calendar matching the primary by default since that will also be the destination calendar
+ if (onboarding && externalId) {
+ const calendarIndex = (connectedCalendars[0].calendars || []).findIndex(
+ (item) => item.externalId === externalId && item.integration === integration
+ );
+ if (calendarIndex >= 0 && connectedCalendars[0].calendars) {
+ connectedCalendars[0].calendars[calendarIndex].isSelected = true;
+ toggledCalendarDetails = {
+ externalId,
+ integration,
+ };
+ }
+ }
+ user.destinationCalendar = await prisma.destinationCalendar.update({
+ where: { userId: user.id },
+ data: {
+ integration,
+ externalId,
+ primaryEmail,
+ },
+ });
+ } else if (onboarding && !destinationCal.isSelected) {
+ // Mark the destination calendar as selected in the calendar list
+ // We use every so that we can exit early once we find the matching calendar
+ connectedCalendars.every((cal) => {
+ const index = (cal.calendars || []).findIndex(
+ (calendar) =>
+ calendar.externalId === destinationCal.externalId &&
+ calendar.integration === destinationCal.integration
+ );
+ if (index >= 0 && cal.calendars) {
+ cal.calendars[index].isSelected = true;
+ toggledCalendarDetails = {
+ externalId: destinationCal.externalId,
+ integration: destinationCal.integration || "",
+ };
+ return false;
+ }
+
+ return true;
+ });
+ }
+ }
+
+ // Insert the newly toggled record to the DB
+ if (toggledCalendarDetails) {
+ await prisma.selectedCalendar.upsert({
+ where: {
+ userId_integration_externalId: {
+ userId: user.id,
+ integration: toggledCalendarDetails.integration,
+ externalId: toggledCalendarDetails.externalId,
+ },
+ },
+ create: {
+ userId: user.id,
+ integration: toggledCalendarDetails.integration,
+ externalId: toggledCalendarDetails.externalId,
+ },
+ // already exists
+ update: {},
+ });
+ }
+
+ return {
+ connectedCalendars,
+ destinationCalendar: {
+ ...(user.destinationCalendar as DestinationCalendar),
+ ...destinationCalendar,
+ },
+ };
+}
diff --git a/packages/lib/getEventTypeById.ts b/packages/lib/getEventTypeById.ts
index 23b224b78e9a6d..bfe71c400960f6 100644
--- a/packages/lib/getEventTypeById.ts
+++ b/packages/lib/getEventTypeById.ts
@@ -26,14 +26,16 @@ interface getEventTypeByIdProps {
currentOrganizationId: number | null;
}
-export default async function getEventTypeById({
+export type EventType = Awaited>;
+
+export const getEventTypeById = async ({
currentOrganizationId,
eventTypeId,
userId,
prisma,
isTrpcCall = false,
isUserOrganizationAdmin,
-}: getEventTypeByIdProps) {
+}: getEventTypeByIdProps) => {
const userSelect = Prisma.validator()({
name: true,
avatarUrl: true,
@@ -436,4 +438,6 @@ export default async function getEventTypeById({
isUserOrganizationAdmin,
};
return finalObj;
-}
+};
+
+export default getEventTypeById;
diff --git a/packages/lib/hasEditPermissionForUser.ts b/packages/lib/hasEditPermissionForUser.ts
index c4adf2de4069ba..6ba4e49df0942e 100644
--- a/packages/lib/hasEditPermissionForUser.ts
+++ b/packages/lib/hasEditPermissionForUser.ts
@@ -4,7 +4,7 @@ import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
type InputOptions = {
ctx: {
- user: NonNullable;
+ user: { id: NonNullable["id"] };
};
input: {
memberId: number;
diff --git a/packages/lib/hooks/useLocale.ts b/packages/lib/hooks/useLocale.ts
index 883e0a793451c6..bfd3e1fe4e682a 100644
--- a/packages/lib/hooks/useLocale.ts
+++ b/packages/lib/hooks/useLocale.ts
@@ -1,9 +1,19 @@
import { useTranslation } from "next-i18next";
+import { useAtomsContext } from "@calcom/platform-atoms";
+
export const useLocale = (namespace: Parameters[0] = "common") => {
+ const context = useAtomsContext();
const { i18n, t } = useTranslation(namespace);
const isLocaleReady = Object.keys(i18n).length > 0;
+ if (context?.clientId) {
+ return { i18n: context.i18n, t: context.t, isLocaleReady: true } as unknown as {
+ i18n: ReturnType["i18n"];
+ t: ReturnType["t"];
+ isLocaleReady: boolean;
+ };
+ }
return {
i18n,
t,
diff --git a/packages/lib/package.json b/packages/lib/package.json
index 99c674c371cce8..c05c3d2e7d60b6 100644
--- a/packages/lib/package.json
+++ b/packages/lib/package.json
@@ -19,6 +19,7 @@
"@sendgrid/client": "^7.7.0",
"@vercel/og": "^0.5.0",
"bcryptjs": "^2.4.3",
+ "city-timezones": "^1.2.1",
"i18next": "^23.2.3",
"ical.js": "^1.4.0",
"ics": "^2.37.0",
diff --git a/packages/lib/server/repository/user.ts b/packages/lib/server/repository/user.ts
index 1a974c13a87e26..308722cee98b9d 100644
--- a/packages/lib/server/repository/user.ts
+++ b/packages/lib/server/repository/user.ts
@@ -338,6 +338,22 @@ export class UserRepository {
};
}
+ static enrichUserWithItsProfileBuiltFromUser({
+ user,
+ }: {
+ user: T;
+ }): T & {
+ nonProfileUsername: string | null;
+ profile: UserProfile;
+ } {
+ // If no organization profile exists, use the personal profile so that the returned user is normalized to have a profile always
+ return {
+ ...user,
+ nonProfileUsername: user.username,
+ profile: ProfileRepository.buildPersonalProfileFromUser({ user }),
+ };
+ }
+
static async enrichEntityWithProfile<
T extends
| {
diff --git a/packages/platform/atoms/.gitignore b/packages/platform/atoms/.gitignore
new file mode 100644
index 00000000000000..3014a83b3b140d
--- /dev/null
+++ b/packages/platform/atoms/.gitignore
@@ -0,0 +1 @@
+globals.min.css
\ No newline at end of file
diff --git a/packages/platform/atoms/README.md b/packages/platform/atoms/README.md
new file mode 100644
index 00000000000000..ae58ba064c01bf
--- /dev/null
+++ b/packages/platform/atoms/README.md
@@ -0,0 +1 @@
+Contains the platform atoms
\ No newline at end of file
diff --git a/packages/platform/atoms/availability/AvailabilitySettings.tsx b/packages/platform/atoms/availability/AvailabilitySettings.tsx
new file mode 100644
index 00000000000000..05b6675b40d4f3
--- /dev/null
+++ b/packages/platform/atoms/availability/AvailabilitySettings.tsx
@@ -0,0 +1,585 @@
+import { useMemo, useState } from "react";
+import { useForm, Controller, useFieldArray, useWatch } from "react-hook-form";
+
+import dayjs from "@calcom/dayjs";
+import { DateOverrideInputDialog, DateOverrideList } from "@calcom/features/schedules";
+import WebSchedule, {
+ ScheduleComponent as PlatformSchedule,
+} from "@calcom/features/schedules/components/Schedule";
+import WebShell from "@calcom/features/shell/Shell";
+import { availabilityAsString } from "@calcom/lib/availability";
+import classNames from "@calcom/lib/classNames";
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import type { WorkingHours } from "@calcom/types/schedule";
+import type { TimeRange } from "@calcom/types/schedule";
+import {
+ VerticalDivider,
+ Button,
+ Form,
+ SkeletonText,
+ ConfirmationDialogContent as WebConfirmationDialogContent,
+ Dialog as WebDialog,
+ DialogTrigger as WebDialogTrigger,
+ Label,
+ Skeleton,
+ Switch,
+ TimezoneSelect as WebTimezoneSelect,
+ SelectSkeletonLoader,
+ Tooltip,
+ EditableHeading,
+} from "@calcom/ui";
+import { MoreVertical, ArrowLeft, Trash, Info, Plus } from "@calcom/ui/components/icon";
+
+import { ConfirmationDialogContent as PlatformConfirmationDialogContent } from "../src/components/ui/confirmation-dialog-content";
+import {
+ Dialog as PlatformDialog,
+ DialogTrigger as PlatformDialogTrigger,
+} from "../src/components/ui/dialog";
+import { Shell as PlatformShell } from "../src/components/ui/shell";
+import { cn } from "../src/lib/utils";
+import { Timezone as PlatformTimzoneSelect } from "../timezone/index";
+import type { AvailabilityFormValues } from "./types";
+
+export type Schedule = {
+ id: number;
+ startTime: Date;
+ endTime: Date;
+ userId: number | null;
+ eventTypeId: number | null;
+ date: Date | null;
+ scheduleId: number | null;
+ days: number[];
+};
+
+export type TranslationsType = {
+ save: string;
+ timezone: string;
+ availability: string;
+ set_to_default: string;
+ delete: string;
+ delete_schedule: string;
+ availability_settings: string;
+ launch_troubleshooter: string;
+ requires_at_least_one_schedule: string;
+ delete_schedule_description: string;
+ name: string;
+ something_doesnt_look_right: string;
+ add_an_override: string;
+ add_time_availability: string;
+ copy_times_to: string;
+};
+
+export type CustomClassNames = {
+ containerClassName?: string;
+ ctaClassName?: string;
+ editableHeadingClassName?: string;
+ formClassName?: string;
+ timezoneSelectClassName?: string;
+ subtitlesClassName?: string;
+ scheduleClassNames?: {
+ scheduleContainer?: string;
+ scheduleDay?: string;
+ dayRanges?: string;
+ timeRanges?: string;
+ labelAndSwitchContainer?: string;
+ };
+};
+
+type AvailabilitySettingsProps = {
+ skeletonLabel?: string;
+ schedule?: {
+ name: string;
+ id: number;
+ availability: TimeRange[][];
+ isLastSchedule: boolean;
+ isDefault: boolean;
+ workingHours: WorkingHours[];
+ dateOverrides: { ranges: TimeRange[] }[];
+ timeZone: string;
+ schedule: Schedule[] | [];
+ };
+ handleDelete: () => void;
+ isDeleting: boolean;
+ isSaving: boolean;
+ isLoading: boolean;
+ timeFormat: number | null;
+ weekStart: string;
+ backPath: string | boolean;
+ handleSubmit: (data: AvailabilityFormValues) => Promise;
+ isPlatform?: boolean;
+ translations?: Partial;
+ customClassNames?: CustomClassNames;
+};
+
+const DeleteDialogButton = ({
+ disabled,
+ buttonClassName,
+ isPending,
+ onDeleteConfirmed,
+ isPlatform,
+ handleDelete,
+}: {
+ disabled?: boolean;
+ onDeleteConfirmed?: () => void;
+ buttonClassName: string;
+ isPlatform: boolean;
+ handleDelete: () => void;
+ isPending: boolean;
+}) => {
+ const [Dialog, DialogTrigger, ConfirmationDialogContent] = useMemo(() => {
+ return isPlatform
+ ? [PlatformDialog, PlatformDialogTrigger, PlatformConfirmationDialogContent]
+ : [WebDialog, WebDialogTrigger, WebConfirmationDialogContent];
+ }, [isPlatform]);
+
+ const { t } = useLocale();
+
+ return (
+
+
+
+
+
+ {
+ handleDelete();
+ onDeleteConfirmed?.();
+ }}>
+ {t("delete_schedule_description")}
+
+
+ );
+};
+
+const useExcludedDates = () => {
+ const watchValues = useWatch({ name: "dateOverrides" }) as {
+ ranges: TimeRange[];
+ }[];
+ return useMemo(() => {
+ return watchValues?.map((field) => dayjs(field.ranges[0].start).utc().format("YYYY-MM-DD"));
+ }, [watchValues]);
+};
+
+const DateOverride = ({
+ workingHours,
+ userTimeFormat,
+}: {
+ workingHours: WorkingHours[];
+ userTimeFormat: number | null;
+}) => {
+ const { append, replace, fields } = useFieldArray({
+ name: "dateOverrides",
+ });
+ const excludedDates = useExcludedDates();
+ const { t } = useLocale();
+ return (
+
+
+ {t("date_overrides")}{" "}
+
+
+
+
+
+
+
{t("date_overrides_subtitle")}
+
+
+ ranges.forEach((range) => append({ ranges: [range] }))}
+ userTimeFormat={userTimeFormat}
+ Trigger={
+
+ {t("add_an_override")}
+
+ }
+ />
+
+
+ );
+};
+
+// Simplify logic by assuming this will never be opened on a large screen
+const SmallScreenSideBar = ({ open, children }: { open: boolean; children: JSX.Element }) => {
+ return (
+
+
+ {open ? children : null}
+
+
+ );
+};
+
+export function AvailabilitySettings({
+ schedule,
+ handleDelete,
+ isDeleting,
+ isLoading,
+ isSaving,
+ timeFormat,
+ weekStart,
+ backPath,
+ handleSubmit,
+ isPlatform = false,
+ translations,
+ customClassNames,
+}: AvailabilitySettingsProps) {
+ const [openSidebar, setOpenSidebar] = useState(false);
+ const { t, i18n } = useLocale();
+
+ const form = useForm({
+ values: schedule && {
+ ...schedule,
+ schedule: schedule?.availability || [],
+ },
+ });
+
+ const [Shell, Schedule, TimezoneSelect] = useMemo(() => {
+ return isPlatform
+ ? [PlatformShell, PlatformSchedule, PlatformTimzoneSelect]
+ : [WebShell, WebSchedule, WebTimezoneSelect];
+ }, [isPlatform]);
+
+ return (
+ (
+
+ )}
+ />
+ }
+ subtitle={
+ schedule ? (
+ schedule.schedule
+ .filter((availability) => !!availability.days.length)
+ .map((availability) => (
+
+ {availabilityAsString(availability, { locale: i18n.language, hour12: timeFormat === 12 })}
+
+
+ ))
+ ) : (
+
+ )
+ }
+ CTA={
+
+
+ {!openSidebar ? (
+ <>
+
+ {translations?.set_to_default ?? t("set_to_default")}
+
+ (
+
+ )}
+ />
+ >
+ ) : null}
+
+
+
+
+
+
+ <>
+
+
+
+
setOpenSidebar(false)} />
+
+ {translations?.availability_settings ?? t("availability_settings")}
+
+ {
+ setOpenSidebar(false);
+ }}
+ />
+
+
+
+ {translations?.name ?? t("name")}
+
+ (
+
+ )}
+ />
+
+
+
+ {translations?.set_to_default ?? t("set_to_default")}
+
+ (
+
+ )}
+ />
+
+
+
+
+
+
+ {translations?.timezone ?? t("timezone")}
+
+
+ value ? (
+ onChange(timezone.value)}
+ />
+ ) : (
+
+ )
+ }
+ />
+
+ {!isPlatform && (
+ <>
+
+
+
+ {translations?.something_doesnt_look_right ?? t("something_doesnt_look_right")}
+
+
+
+ {translations?.launch_troubleshooter ?? t("launch_troubleshooter")}
+
+
+
+ >
+ )}
+
+
+
+
+ >
+
+
+
+ {translations?.save ?? t("save")}
+
+
setOpenSidebar(true)}
+ />
+
+ }>
+
+
+ );
+}
diff --git a/packages/platform/atoms/availability/index.ts b/packages/platform/atoms/availability/index.ts
new file mode 100644
index 00000000000000..904bc1af970ee0
--- /dev/null
+++ b/packages/platform/atoms/availability/index.ts
@@ -0,0 +1,4 @@
+export { PlatformAvailabilitySettingsWrapper } from "./wrappers/PlatformAvailabilitySettingsWrapper";
+export { AvailabilitySettings } from "./AvailabilitySettings";
+
+export * from "../types";
diff --git a/packages/platform/atoms/availability/types.ts b/packages/platform/atoms/availability/types.ts
new file mode 100644
index 00000000000000..9422e9cbd3bffa
--- /dev/null
+++ b/packages/platform/atoms/availability/types.ts
@@ -0,0 +1,22 @@
+import type { Schedule as ScheduleType, TimeRange } from "@calcom/types/schedule";
+
+export type Availability = {
+ id: number;
+ userId: number | null;
+ eventTypeId: number | null;
+ days: number[];
+ startTime: string;
+ endTime: Date;
+ date: Date | null;
+ scheduleId: number | null;
+};
+
+export type WeekdayFormat = "short" | "long";
+
+export type AvailabilityFormValues = {
+ name: string;
+ schedule: ScheduleType;
+ dateOverrides: { ranges: TimeRange[] }[];
+ timeZone: string;
+ isDefault: boolean;
+};
diff --git a/packages/platform/atoms/availability/wrappers/PlatformAvailabilitySettingsWrapper.tsx b/packages/platform/atoms/availability/wrappers/PlatformAvailabilitySettingsWrapper.tsx
new file mode 100644
index 00000000000000..fc8faa25b0da19
--- /dev/null
+++ b/packages/platform/atoms/availability/wrappers/PlatformAvailabilitySettingsWrapper.tsx
@@ -0,0 +1,182 @@
+import type { ScheduleLabelsType } from "@calcom/features/schedules/components/Schedule";
+import type { ScheduleWithAvailabilitiesForWeb, UpdateScheduleOutputType } from "@calcom/platform-libraries";
+import type { ApiErrorResponse, ApiResponse, ApiSuccessResponse } from "@calcom/platform-types";
+
+import useClientSchedule from "../../hooks/useClientSchedule";
+import useDeleteSchedule from "../../hooks/useDeleteSchedule";
+import { useMe } from "../../hooks/useMe";
+import useUpdateSchedule from "../../hooks/useUpdateSchedule";
+import { useToast } from "../../src/components/ui/use-toast";
+import type { Schedule } from "../AvailabilitySettings";
+import type { CustomClassNames, TranslationsType } from "../AvailabilitySettings";
+import { AvailabilitySettings } from "../AvailabilitySettings";
+import type { AvailabilityFormValues } from "../types";
+
+type PlatformAvailabilitySettingsWrapperProps = {
+ id?: string;
+ labels?: {
+ tooltips: Partial;
+ };
+ translations?: Partial;
+ customClassNames?: Partial;
+ onUpdateSuccess?: (res: ApiResponse) => void;
+ onUpdateError?: (err: ApiErrorResponse) => void;
+ onDeleteSuccess?: (res: ApiResponse) => void;
+ onDeleteError?: (err: ApiErrorResponse) => void;
+};
+
+const defaultTranslations: TranslationsType = {
+ availability: "Availability",
+ set_to_default: "Set to default",
+ delete: "Delete",
+ delete_schedule: "Delete schedule",
+ timezone: "Timezone",
+ availability_settings: "Availability settings",
+ launch_troubleshooter: "Launch troubleshooter",
+ requires_at_least_one_schedule: "Requires at least one schedule",
+ delete_schedule_description: "Are you sure you want to delete this schedule?",
+ name: "Name",
+ something_doesnt_look_right: "Something doesn't look right",
+ add_an_override: "Add an override",
+ add_time_availability: "Add new time slot",
+ copy_times_to: "Copy times to âŠ",
+ save: "Save",
+} as const;
+
+export const PlatformAvailabilitySettingsWrapper = ({
+ id,
+ translations = defaultTranslations,
+ customClassNames,
+ onDeleteError,
+ onDeleteSuccess,
+ onUpdateError,
+ onUpdateSuccess,
+}: PlatformAvailabilitySettingsWrapperProps) => {
+ const { isLoading, data: schedule } = useClientSchedule(id);
+ const mySchedule = schedule as ApiSuccessResponse;
+ const { data: me } = useMe();
+ const userSchedule = mySchedule?.data;
+ const { timeFormat } = me?.data || { timeFormat: null };
+ const { toast } = useToast();
+
+ const { mutate: deleteSchedule, isPending: isDeletionInProgress } = useDeleteSchedule({
+ onSuccess: (res) => {
+ onDeleteSuccess?.(res);
+ toast({
+ description: "Schedule deleted successfully",
+ });
+ },
+ onError: (err) => {
+ onDeleteError?.(err);
+ toast({
+ description: "Could not delete schedule",
+ });
+ },
+ });
+
+ const { mutate: updateSchedule, isPending: isSavingInProgress } = useUpdateSchedule({
+ onSuccess: (res) => {
+ onUpdateSuccess?.(res);
+ toast({
+ description: "Schedule updated successfully",
+ });
+ },
+ onError: (err) => {
+ onUpdateError?.(err);
+ toast({
+ description: "Could not update schedule",
+ });
+ },
+ });
+
+ const handleDelete = async (id: number) => {
+ await deleteSchedule({ id });
+ };
+
+ const handleUpdate = async (id: number, body: AvailabilityFormValues) => {
+ const transformedDateOverrides =
+ body.dateOverrides.flatMap(
+ (dateOverridesRanges) =>
+ dateOverridesRanges?.ranges?.map((range) => ({ start: range.start, end: range.end })) ?? []
+ ) ?? [];
+
+ await updateSchedule({ ...body, scheduleId: id, dateOverrides: transformedDateOverrides });
+ };
+
+ if (isLoading) return Loading...
;
+
+ if (!userSchedule) return No user schedule present
;
+
+ return (
+ {
+ userSchedule.id && handleDelete(userSchedule.id);
+ }}
+ handleSubmit={async (data) => {
+ userSchedule.id && handleUpdate(userSchedule.id, data);
+ }}
+ weekStart="Sunday"
+ timeFormat={timeFormat}
+ isLoading={isLoading}
+ schedule={
+ userSchedule
+ ? {
+ name: userSchedule.name,
+ id: userSchedule.id,
+ isLastSchedule: userSchedule.isLastSchedule,
+ isDefault: userSchedule.isDefault,
+ workingHours: userSchedule.workingHours,
+ dateOverrides: userSchedule.dateOverrides,
+ timeZone: userSchedule.timeZone,
+ availability: userSchedule.availability,
+ schedule:
+ userSchedule.schedule.reduce(
+ (acc: Schedule[], avail: Schedule) => [
+ ...acc,
+ { ...avail, startTime: new Date(avail.startTime), endTime: new Date(avail.endTime) },
+ ],
+ [] as Schedule[]
+ ) || [],
+ }
+ : undefined
+ }
+ isDeleting={isDeletionInProgress}
+ isSaving={isSavingInProgress}
+ backPath=""
+ isPlatform={true}
+ translations={{ ...defaultTranslations, ...translations }}
+ customClassNames={customClassNames}
+ />
+ );
+};
+
+[
+ [
+ {
+ start: "2024-03-21T09:00:00.000Z",
+ end: "2024-03-21T17:00:00.000Z",
+ },
+ {
+ start: "2024-03-21T17:00:00.000Z",
+ end: "2024-03-21T18:00:00.000Z",
+ },
+ {
+ start: "2024-03-21T18:00:00.000Z",
+ end: "2024-03-21T19:00:00.000Z",
+ },
+ ], // thursday
+ [
+ {
+ start: "2024-03-21T09:00:00.000Z",
+ end: "2024-03-21T17:00:00.000Z",
+ },
+ {
+ start: "2024-03-21T17:00:00.000Z",
+ end: "2024-03-21T18:00:00.000Z",
+ },
+ {
+ start: "2024-03-21T18:00:00.000Z",
+ end: "2024-03-21T19:00:00.000Z",
+ },
+ ], //friday
+];
diff --git a/packages/atoms/booker/Booker.docs.mdx b/packages/platform/atoms/booker/Booker.docs.mdx
similarity index 79%
rename from packages/atoms/booker/Booker.docs.mdx
rename to packages/platform/atoms/booker/Booker.docs.mdx
index 7355e123764ab5..538b08c165f7eb 100644
--- a/packages/atoms/booker/Booker.docs.mdx
+++ b/packages/platform/atoms/booker/Booker.docs.mdx
@@ -1,6 +1,6 @@
import { Meta, Canvas, ArgsTable } from "@storybook/blocks";
import { Title } from "@calcom/storybook/components";
-import { BookerWebWrapper as Booker } from "./wrappers/BookerWebWrapper";
+import { BookerWebWrapper as Booker } from "./BookerWebWrapper";
import * as BookerStories from "./Booker.stories";
diff --git a/packages/atoms/booker/Booker.stories.tsx b/packages/platform/atoms/booker/Booker.stories.tsx
similarity index 81%
rename from packages/atoms/booker/Booker.stories.tsx
rename to packages/platform/atoms/booker/Booker.stories.tsx
index 7ab0111f37e9db..b86cf7c5c1ddf2 100644
--- a/packages/atoms/booker/Booker.stories.tsx
+++ b/packages/platform/atoms/booker/Booker.stories.tsx
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react";
-import { BookerWebWrapper as Booker } from "./wrappers/BookerWebWrapper";
+import { BookerWebWrapper as Booker } from "./BookerWebWrapper";
const meta: Meta = {
component: Booker,
diff --git a/packages/platform/atoms/booker/BookerPlatformWrapper.tsx b/packages/platform/atoms/booker/BookerPlatformWrapper.tsx
new file mode 100644
index 00000000000000..a2afb0acb21b4a
--- /dev/null
+++ b/packages/platform/atoms/booker/BookerPlatformWrapper.tsx
@@ -0,0 +1,297 @@
+import { useMemo, useEffect } from "react";
+import { shallow } from "zustand/shallow";
+
+import dayjs from "@calcom/dayjs";
+import type { BookerProps } from "@calcom/features/bookings/Booker";
+import { Booker as BookerComponent } from "@calcom/features/bookings/Booker";
+import { useBookerLayout } from "@calcom/features/bookings/Booker/components/hooks/useBookerLayout";
+import { useBookingForm } from "@calcom/features/bookings/Booker/components/hooks/useBookingForm";
+import { useLocalSet } from "@calcom/features/bookings/Booker/components/hooks/useLocalSet";
+import { useBookerStore, useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
+import { useTimePreferences } from "@calcom/features/bookings/lib";
+import { useTimesForSchedule } from "@calcom/features/schedules/lib/use-schedule/useTimesForSchedule";
+import { getUsernameList } from "@calcom/lib/defaultEvents";
+import type { ConnectedDestinationCalendars } from "@calcom/platform-libraries";
+import type { BookingResponse } from "@calcom/platform-libraries";
+import type {
+ ApiErrorResponse,
+ ApiSuccessResponse,
+ ApiSuccessResponseWithoutData,
+} from "@calcom/platform-types";
+import { BookerLayouts } from "@calcom/prisma/zod-utils";
+
+import { useAvailableSlots } from "../hooks/useAvailableSlots";
+import { useCalendarsBusyTimes } from "../hooks/useCalendarsBusyTimes";
+import { useConnectedCalendars } from "../hooks/useConnectedCalendars";
+import { useCreateBooking } from "../hooks/useCreateBooking";
+import { useCreateInstantBooking } from "../hooks/useCreateInstantBooking";
+import { useCreateRecurringBooking } from "../hooks/useCreateRecurringBooking";
+import { useHandleBookEvent } from "../hooks/useHandleBookEvent";
+import { useMe } from "../hooks/useMe";
+import { usePublicEvent } from "../hooks/usePublicEvent";
+import { useSlots } from "../hooks/useSlots";
+
+type BookerPlatformWrapperAtomProps = Omit & {
+ rescheduleUid?: string;
+ bookingUid?: string;
+ firstName?: string;
+ lastName?: string;
+ guests?: string[];
+ name?: string;
+ onCreateBookingSuccess?: (data: ApiSuccessResponse) => void;
+ onCreateBookingError?: (data: ApiErrorResponse | Error) => void;
+ onCreateRecurringBookingSuccess?: (data: ApiSuccessResponse) => void;
+ onCreateRecurringBookingError?: (data: ApiErrorResponse | Error) => void;
+ onCreateInstantBookingSuccess?: (data: ApiSuccessResponse) => void;
+ onCreateInstantBookingError?: (data: ApiErrorResponse | Error) => void;
+ onReserveSlotSuccess?: (data: ApiSuccessResponse) => void;
+ onReserveSlotError?: (data: ApiErrorResponse) => void;
+ onDeleteSlotSuccess?: (data: ApiSuccessResponseWithoutData) => void;
+ onDeleteSlotError?: (data: ApiErrorResponse) => void;
+};
+
+export const BookerPlatformWrapper = (props: BookerPlatformWrapperAtomProps) => {
+ const [bookerState, setBookerState] = useBookerStore((state) => [state.state, state.setState], shallow);
+ const setSelectedDate = useBookerStore((state) => state.setSelectedDate);
+ const setSelectedTimeslot = useBookerStore((state) => state.setSelectedTimeslot);
+
+ useEffect(() => {
+ // reset booker whenever it's unmounted
+ return () => {
+ setBookerState("loading");
+ setSelectedDate(null);
+ setSelectedTimeslot(null);
+ };
+ }, []);
+
+ const event = usePublicEvent({ username: props.username, eventSlug: props.eventSlug });
+ const bookerLayout = useBookerLayout(event.data);
+ useInitializeBookerStore({
+ ...props,
+ eventId: event.data?.id,
+ rescheduleUid: props.rescheduleUid ?? null,
+ bookingUid: props.bookingUid ?? null,
+ layout: bookerLayout.defaultLayout,
+ org: event.data?.entity.orgSlug,
+ });
+ const [dayCount] = useBookerStore((state) => [state.dayCount, state.setDayCount], shallow);
+ const selectedDate = useBookerStore((state) => state.selectedDate);
+
+ const month = useBookerStore((state) => state.month);
+ const eventSlug = useBookerStore((state) => state.eventSlug);
+
+ const selectedDuration = useBookerStore((state) => state.selectedDuration);
+
+ const { data: session } = useMe();
+ const hasSession = !!session;
+ const prefillFormParams = useMemo(() => {
+ return {
+ name: props.name ?? null,
+ guests: props.guests ?? [],
+ };
+ }, [props.name, props.guests]);
+ const date = dayjs(selectedDate).format("YYYY-MM-DD");
+
+ const prefetchNextMonth =
+ (bookerLayout.layout === BookerLayouts.WEEK_VIEW &&
+ !!bookerLayout.extraDays &&
+ dayjs(date).month() !== dayjs(date).add(bookerLayout.extraDays, "day").month()) ||
+ (bookerLayout.layout === BookerLayouts.COLUMN_VIEW &&
+ dayjs(date).month() !== dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month());
+
+ const monthCount =
+ ((bookerLayout.layout !== BookerLayouts.WEEK_VIEW && bookerState === "selecting_time") ||
+ bookerLayout.layout === BookerLayouts.COLUMN_VIEW) &&
+ dayjs(date).add(1, "month").month() !==
+ dayjs(date).add(bookerLayout.columnViewExtraDays.current, "day").month()
+ ? 2
+ : undefined;
+ const { timezone } = useTimePreferences();
+
+ const [startTime, endTime] = useTimesForSchedule({
+ month,
+ monthCount,
+ dayCount,
+ prefetchNextMonth,
+ selectedDate,
+ });
+ const schedule = useAvailableSlots({
+ usernameList: getUsernameList(props.username ?? ""),
+ eventTypeId: event?.data?.id ?? 0,
+ startTime,
+ endTime,
+ timeZone: session?.data?.timeZone,
+ duration: selectedDuration ?? undefined,
+ rescheduleUid: props.rescheduleUid,
+ enabled:
+ Boolean(props.username) &&
+ Boolean(month) &&
+ Boolean(timezone) &&
+ // Should only wait for one or the other, not both.
+ (Boolean(eventSlug) || Boolean(event?.data?.id) || event?.data?.id === 0),
+ });
+
+ const bookerForm = useBookingForm({
+ event: event.data,
+ sessionEmail: session?.data?.email,
+ sessionUsername: session?.data?.username,
+ sessionName: session?.data?.name,
+ hasSession,
+ extraOptions: {},
+ prefillFormParams: prefillFormParams,
+ });
+ const {
+ mutate: createBooking,
+ isPending: creatingBooking,
+ error: createBookingError,
+ isError: isCreateBookingError,
+ } = useCreateBooking({
+ onSuccess: (data) => {
+ schedule.refetch();
+ props.onCreateBookingSuccess?.(data);
+ },
+ onError: props.onCreateBookingError,
+ });
+ const {
+ mutate: createRecBooking,
+ isPending: creatingRecBooking,
+
+ error: createRecBookingError,
+ isError: isCreateRecBookingError,
+ } = useCreateRecurringBooking({
+ onSuccess: (data) => {
+ schedule.refetch();
+ props.onCreateRecurringBookingSuccess?.(data);
+ },
+ onError: props.onCreateRecurringBookingError,
+ });
+ const {
+ mutate: createInstantBooking,
+ isPending: creatingInstantBooking,
+ error: createInstantBookingError,
+ isError: isCreateInstantBookingError,
+ } = useCreateInstantBooking({
+ onSuccess: (data) => {
+ schedule.refetch();
+ props.onCreateInstantBookingSuccess?.(data);
+ },
+ onError: props.onCreateInstantBookingError,
+ });
+
+ const slots = useSlots(event);
+ const { data: connectedCalendars, isPending: fetchingConnectedCalendars } = useConnectedCalendars();
+ const calendars = connectedCalendars as ApiSuccessResponse;
+
+ const { set, clearSet } = useLocalSet<{
+ credentialId: number;
+ externalId: string;
+ }>("toggledConnectedCalendars", []);
+ const { data: overlayBusyDates } = useCalendarsBusyTimes({
+ loggedInUsersTz: session?.data?.timeZone || "Europe/London",
+ dateFrom: selectedDate,
+ dateTo: selectedDate,
+ calendarsToLoad: Array.from(set).map((item) => ({
+ credentialId: item.credentialId,
+ externalId: item.externalId,
+ })),
+ onError: () => {
+ clearSet();
+ },
+ enabled: Boolean(
+ hasSession && set.size > 0 && localStorage?.getItem("overlayCalendarSwitchDefault") === "true"
+ ),
+ });
+
+ const handleBookEvent = useHandleBookEvent({
+ event,
+ bookingForm: bookerForm.bookingForm,
+ hashedLink: props.hashedLink,
+ metadata: {},
+ handleBooking: createBooking,
+ handleInstantBooking: createInstantBooking,
+ handleRecBooking: createRecBooking,
+ });
+
+ return (
+ {
+ handleBookEvent();
+ return;
+ },
+ expiryTime: undefined,
+ bookingForm: bookerForm.bookingForm,
+ bookerFormErrorRef: bookerForm.bookerFormErrorRef,
+ errors: {
+ hasDataErrors: isCreateBookingError || isCreateRecBookingError || isCreateInstantBookingError,
+ dataErrors: createBookingError || createRecBookingError || createInstantBookingError,
+ },
+ loadingStates: {
+ creatingBooking: creatingBooking,
+ creatingRecurringBooking: creatingRecBooking,
+ creatingInstantBooking: creatingInstantBooking,
+ },
+ }}
+ slots={slots}
+ calendars={{
+ overlayBusyDates: overlayBusyDates?.data,
+ isOverlayCalendarEnabled: false,
+ connectedCalendars: calendars?.data.connectedCalendars || [],
+ loadingConnectedCalendar: fetchingConnectedCalendars,
+ onToggleCalendar: () => {
+ return;
+ },
+ }}
+ verifyEmail={{
+ isEmailVerificationModalVisible: false,
+ setEmailVerificationModalVisible: () => {
+ return;
+ },
+ setVerifiedEmail: () => {
+ return;
+ },
+ handleVerifyEmail: () => {
+ return;
+ },
+ renderConfirmNotVerifyEmailButtonCond: true,
+ }}
+ bookerForm={bookerForm}
+ event={event}
+ schedule={schedule}
+ bookerLayout={bookerLayout}
+ verifyCode={undefined}
+ isPlatform
+ />
+ );
+};
diff --git a/packages/atoms/booker/wrappers/BookerWebWrapper.tsx b/packages/platform/atoms/booker/BookerWebWrapper.tsx
similarity index 93%
rename from packages/atoms/booker/wrappers/BookerWebWrapper.tsx
rename to packages/platform/atoms/booker/BookerWebWrapper.tsx
index c785353c033d36..b8dc486d1a068c 100644
--- a/packages/atoms/booker/wrappers/BookerWebWrapper.tsx
+++ b/packages/platform/atoms/booker/BookerWebWrapper.tsx
@@ -16,12 +16,12 @@ import { useVerifyCode } from "@calcom/features/bookings/Booker/components/hooks
import { useVerifyEmail } from "@calcom/features/bookings/Booker/components/hooks/useVerifyEmail";
import { useBookerStore, useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
import { useEvent, useScheduleForEvent } from "@calcom/features/bookings/Booker/utils/event";
+import { useBrandColors } from "@calcom/features/bookings/Booker/utils/use-brand-colors";
+import { DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR } from "@calcom/lib/constants";
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
import { BookerLayouts } from "@calcom/prisma/zod-utils";
-import type { AtomsGlobalConfigProps } from "../../types";
-
-type BookerWebWrapperAtomProps = BookerProps & AtomsGlobalConfigProps;
+type BookerWebWrapperAtomProps = BookerProps;
export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => {
const router = useRouter();
@@ -74,7 +74,7 @@ export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => {
}, [searchParams, firstNameQueryParam, lastNameQueryParam]);
const bookerForm = useBookingForm({
- event,
+ event: event.data,
sessionEmail: session?.user.email,
sessionUsername: session?.user.username,
sessionName: session?.user.name,
@@ -152,6 +152,11 @@ export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => {
},
[searchParams, pathname, router]
);
+ useBrandColors({
+ brandColor: event.data?.profile.brandColor ?? DEFAULT_LIGHT_BRAND_COLOR,
+ darkBrandColor: event.data?.profile.darkBrandColor ?? DEFAULT_DARK_BRAND_COLOR,
+ theme: event.data?.profile.theme,
+ });
return (
{
bookerLayout={bookerLayout}
schedule={schedule}
verifyCode={verifyCode}
+ isPlatform={false}
/>
);
};
diff --git a/packages/atoms/booker/export.ts b/packages/platform/atoms/booker/export.ts
similarity index 63%
rename from packages/atoms/booker/export.ts
rename to packages/platform/atoms/booker/export.ts
index 3c06badbb922f3..d8184e16d39b4e 100644
--- a/packages/atoms/booker/export.ts
+++ b/packages/platform/atoms/booker/export.ts
@@ -1,5 +1,5 @@
/** Export file is only used for building the dist version of this Atom. */
// import "../globals.css";
-export { BookerWebWrapper as Booker } from "./wrappers/BookerWebWrapper";
+export { BookerWebWrapper as Booker } from "./BookerWebWrapper";
export * from "../types";
diff --git a/packages/platform/atoms/booker/index.ts b/packages/platform/atoms/booker/index.ts
new file mode 100644
index 00000000000000..ac7bafc4511dc2
--- /dev/null
+++ b/packages/platform/atoms/booker/index.ts
@@ -0,0 +1,2 @@
+export { BookerWebWrapper } from "./BookerWebWrapper";
+export { BookerPlatformWrapper } from "./BookerPlatformWrapper";
diff --git a/packages/atoms/build.mjs b/packages/platform/atoms/build.mjs
similarity index 100%
rename from packages/atoms/build.mjs
rename to packages/platform/atoms/build.mjs
diff --git a/packages/platform/atoms/cal-provider/BaseCalProvider.tsx b/packages/platform/atoms/cal-provider/BaseCalProvider.tsx
new file mode 100644
index 00000000000000..ae0aeab6e2f135
--- /dev/null
+++ b/packages/platform/atoms/cal-provider/BaseCalProvider.tsx
@@ -0,0 +1,124 @@
+import { TooltipProvider } from "@radix-ui/react-tooltip";
+import { useState } from "react";
+import { useCallback } from "react";
+
+import enTranslations from "@calcom/web/public/static/locales/en/common.json";
+
+import { AtomsContext } from "../hooks/useAtomsContext";
+import { useOAuthClient } from "../hooks/useOAuthClient";
+import { useOAuthFlow } from "../hooks/useOAuthFlow";
+import { useTimezone } from "../hooks/useTimezone";
+import { useUpdateUserTimezone } from "../hooks/useUpdateUserTimezone";
+import http from "../lib/http";
+import { Toaster } from "../src/components/ui/toaster";
+import type { CalProviderProps } from "./CalProvider";
+
+type translationKeys = keyof typeof enTranslations;
+
+export function BaseCalProvider({
+ clientId,
+ accessToken,
+ options,
+ children,
+ autoUpdateTimezone,
+ onTimezoneChange,
+}: CalProviderProps) {
+ const [error, setError] = useState("");
+
+ const { mutateAsync } = useUpdateUserTimezone();
+
+ const handleTimezoneChange = useCallback(
+ async (currentTimezone: string) => {
+ await mutateAsync({ timeZone: currentTimezone });
+ },
+ [mutateAsync]
+ );
+
+ const getTimezoneChangeHandler = useCallback(() => {
+ if (onTimezoneChange) return onTimezoneChange;
+ if (!onTimezoneChange && autoUpdateTimezone) return handleTimezoneChange;
+ return undefined;
+ }, [onTimezoneChange, autoUpdateTimezone, handleTimezoneChange]);
+
+ useTimezone(getTimezoneChangeHandler());
+
+ const { isInit } = useOAuthClient({
+ clientId,
+ apiUrl: options.apiUrl,
+ refreshUrl: options.refreshUrl,
+ onError: setError,
+ onSuccess: () => {
+ setError("");
+ },
+ });
+
+ const { isRefreshing, currentAccessToken } = useOAuthFlow({
+ accessToken,
+ refreshUrl: options.refreshUrl,
+ onError: setError,
+ onSuccess: () => {
+ setError("");
+ },
+ clientId,
+ });
+
+ return isInit ? (
+ http,
+ isRefreshing: isRefreshing,
+ isInit: isInit,
+ isValidClient: Boolean(!error && clientId && isInit),
+ isAuth: Boolean(isInit && !error && clientId && currentAccessToken && http.getAuthorizationHeader()),
+ t: (key, values) => {
+ let translation = String(enTranslations[key as translationKeys] ?? "");
+ if (!translation) {
+ return "";
+ }
+ if (values) {
+ const valueKeys = Object.keys(values) as (keyof typeof values)[];
+ if (valueKeys.length) {
+ valueKeys.forEach((valueKey) => {
+ if (translation)
+ translation = translation.replace(
+ `{{${String(valueKey)}}}`,
+ values[valueKey]?.toString() ?? `{{${String(valueKey)}}}`
+ );
+ });
+ }
+ }
+
+ return replaceOccurrences(translation, enTranslations) ?? "";
+ },
+ i18n: {
+ language: "en",
+ defaultLocale: "en",
+ locales: ["en"],
+ exists: (key: translationKeys | string) => Boolean(enTranslations[key as translationKeys]),
+ },
+ }}>
+ {children}
+
+
+ ) : (
+ <>
+ {children}
+
+ >
+ );
+}
+
+function replaceOccurrences(input: string, replacementMap: { [key: string]: string }): string {
+ const pattern = /\$t\((.*?)\)/g;
+ return input.replace(pattern, (match, key) => {
+ if (key in replacementMap) {
+ return replacementMap[key];
+ }
+ // If the key is not found in the replacement map, you may choose to return the original match
+ return match;
+ });
+}
diff --git a/packages/platform/atoms/cal-provider/CalProvider.tsx b/packages/platform/atoms/cal-provider/CalProvider.tsx
new file mode 100644
index 00000000000000..ff79bd24bcbdcb
--- /dev/null
+++ b/packages/platform/atoms/cal-provider/CalProvider.tsx
@@ -0,0 +1,37 @@
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import type { ReactNode } from "react";
+
+import { BaseCalProvider } from "./BaseCalProvider";
+
+const queryClient = new QueryClient();
+
+export type CalProviderProps = {
+ children?: ReactNode;
+ clientId: string;
+ accessToken: string;
+ options: { refreshUrl?: string; apiUrl: string };
+ autoUpdateTimezone?: boolean;
+ onTimezoneChange?: () => void;
+};
+
+export function CalProvider({
+ clientId,
+ accessToken,
+ options,
+ children,
+ autoUpdateTimezone = true,
+ onTimezoneChange,
+}: CalProviderProps) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/packages/platform/atoms/cal-provider/index.ts b/packages/platform/atoms/cal-provider/index.ts
new file mode 100644
index 00000000000000..30f88a25d2c67e
--- /dev/null
+++ b/packages/platform/atoms/cal-provider/index.ts
@@ -0,0 +1 @@
+export { CalProvider } from "./CalProvider";
diff --git a/packages/platform/atoms/components.json b/packages/platform/atoms/components.json
new file mode 100644
index 00000000000000..5d015cdde7baa5
--- /dev/null
+++ b/packages/platform/atoms/components.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.cjs",
+ "css": "globals.css",
+ "baseColor": "slate",
+ "cssVariables": true
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils"
+ }
+}
diff --git a/packages/platform/atoms/components.ts b/packages/platform/atoms/components.ts
new file mode 100644
index 00000000000000..3ecabb3cdf1776
--- /dev/null
+++ b/packages/platform/atoms/components.ts
@@ -0,0 +1,6 @@
+export { CalProvider } from "./cal-provider";
+export { GcalConnect } from "./gcal-connect";
+export { PlatformAvailabilitySettingsWrapper as AvailabilitySettings } from "./availability";
+export { BookerPlatformWrapper as Booker } from "./booker";
+export { useIsPlatform } from "./hooks/useIsPlatform";
+export { useAtomsContext } from "./hooks/useAtomsContext";
diff --git a/packages/platform/atoms/gcal-connect/GcalConnect.tsx b/packages/platform/atoms/gcal-connect/GcalConnect.tsx
new file mode 100644
index 00000000000000..d3bc5e2569b47c
--- /dev/null
+++ b/packages/platform/atoms/gcal-connect/GcalConnect.tsx
@@ -0,0 +1,37 @@
+import { cn } from "@/lib/utils";
+import type { FC } from "react";
+
+import { Button } from "@calcom/ui";
+import { CalendarDays } from "@calcom/ui/components/icon";
+
+import { useAtomsContext } from "../hooks/useAtomsContext";
+import { useGcal } from "../hooks/useGcal";
+
+interface GcalConnectProps {
+ className?: string;
+ label?: string;
+ alreadyConnectedLabel?: string;
+}
+
+export const GcalConnect: FC = ({
+ label = "Connect Google Calendar",
+ alreadyConnectedLabel = "Connected Google Calendar",
+ className,
+}) => {
+ const { isAuth } = useAtomsContext();
+
+ const { allowConnect, checked, redirectToGcalOAuth } = useGcal({ isAuth });
+
+ if (!isAuth || !checked) return <>>;
+
+ return (
+ redirectToGcalOAuth()}>
+ {allowConnect ? label : alreadyConnectedLabel}
+
+ );
+};
diff --git a/packages/platform/atoms/gcal-connect/index.ts b/packages/platform/atoms/gcal-connect/index.ts
new file mode 100644
index 00000000000000..490a111c941d63
--- /dev/null
+++ b/packages/platform/atoms/gcal-connect/index.ts
@@ -0,0 +1 @@
+export { GcalConnect } from "./GcalConnect";
diff --git a/packages/platform/atoms/globals.css b/packages/platform/atoms/globals.css
new file mode 100644
index 00000000000000..12d4a764d4d52c
--- /dev/null
+++ b/packages/platform/atoms/globals.css
@@ -0,0 +1,13837 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@import "/packages/ui/styles/shared-globals.css";
+@import "/apps/web/styles/globals.css";
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 47.4% 11.2%;
+
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 47.4% 11.2%;
+
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 47.4% 11.2%;
+
+ --primary: 222.2 47.4% 11.2%;
+ --primary-foreground: 210 40% 98%;
+
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+
+ --destructive: 0 100% 50%;
+ --destructive-foreground: 210 40% 98%;
+
+ --ring: 215 20.2% 65.1%;
+
+ --radius: 0.5rem;
+ /* background */
+
+ --cal-bg-emphasis: #e5e7eb;
+ --cal-bg: white;
+ --cal-bg-subtle: #f3f4f6;
+ --cal-bg-muted: #f9fafb;
+ --cal-bg-inverted: #111827;
+
+ /* background -> components*/
+ --cal-bg-info: #dee9fc;
+ --cal-bg-success: #e2fbe8;
+ --cal-bg-attention: #fceed8;
+ --cal-bg-error: #f9e3e2;
+ --cal-bg-dark-error: #752522;
+
+ /* Borders */
+ --cal-border-emphasis: #9ca3af;
+ --cal-border: #d1d5db;
+ --cal-border-subtle: #e5e7eb;
+ --cal-border-booker: #e5e7eb;
+ --cal-border-muted: #f3f4f6;
+ --cal-border-error: #aa2e26;
+
+ /* Content/Text */
+ --cal-text-emphasis: #111827;
+ --cal-text: #374151;
+ --cal-text-subtle: #6b7280;
+ --cal-text-muted: #9ca3af;
+ --cal-text-inverted: white;
+
+ /* Content/Text -> components */
+ --cal-text-info: #253985;
+ --cal-text-success: #285231;
+ --cal-text-attention: #73321b;
+ --cal-text-error: #752522;
+
+ /* Brand shinanigans
+ -> These will be computed for the users theme at runtime.
+ */
+ --cal-brand: #111827;
+ --cal-brand-emphasis: #101010;
+ --cal-brand-text: white;
+ }
+
+ .dark {
+ --background: 224 71% 4%;
+ --foreground: 213 31% 91%;
+
+ --muted: 223 47% 11%;
+ --muted-foreground: 215.4 16.3% 56.9%;
+
+ --accent: 216 34% 17%;
+ --accent-foreground: 210 40% 98%;
+
+ --popover: 224 71% 4%;
+ --popover-foreground: 215 20.2% 65.1%;
+
+ --border: 216 34% 17%;
+ --input: 216 34% 17%;
+
+ --card: 224 71% 4%;
+ --card-foreground: 213 31% 91%;
+
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 1.2%;
+
+ --secondary: 222.2 47.4% 11.2%;
+ --secondary-foreground: 210 40% 98%;
+
+ --destructive: 0 63% 31%;
+ --destructive-foreground: 210 40% 98%;
+
+ --ring: 216 34% 17%;
+
+ --radius: 0.5rem;
+ --cal-bg-emphasis: #2b2b2b;
+ --cal-bg: #101010;
+ --cal-bg-subtle: #2b2b2b;
+ --cal-bg-muted: #1c1c1c;
+ --cal-bg-inverted: #f3f4f6;
+
+ /* background -> components*/
+ --cal-bg-info: #263fa9;
+ --cal-bg-success: #306339;
+ --cal-bg-attention: #8e3b1f;
+ --cal-bg-error: #8c2822;
+ --cal-bg-dark-error: #752522;
+
+ /* Borders */
+ --cal-border-emphasis: #575757;
+ --cal-border: #444444;
+ --cal-border-subtle: #2b2b2b;
+ --cal-border-booker: #2b2b2b;
+ --cal-border-muted: #1c1c1c;
+ --cal-border-error: #aa2e26;
+
+ /* Content/Text */
+ --cal-text-emphasis: #f3f4f6;
+ --cal-text: #d6d6d6;
+ --cal-text-subtle: #a5a5a5;
+ --cal-text-muted: #575757;
+ --cal-text-inverted: #101010;
+
+ /* Content/Text -> components */
+ --cal-text-info: #dee9fc;
+ --cal-text-success: #e2fbe8;
+ --cal-text-attention: #fceed8;
+ --cal-text-error: #f9e3e2;
+
+ /* Brand shenanigans
+ -> These will be computed for the users theme at runtime.
+ */
+ --cal-brand: white;
+ --cal-brand-emphasis: #e1e1e1;
+ --cal-brand-text: black;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ font-feature-settings: "rlig" 1, "calt" 1;
+ }
+ html {
+ font-family: inherit !important;
+ }
+}
+*,:after,:before {
+ box-sizing: border-box;
+ border: 0 solid #e5e7eb
+}
+
+:after,:before {
+ --tw-content: ""
+}
+
+html {
+ line-height: 1.5;
+ -webkit-text-size-adjust: 100%;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ font-family: var(--font-inter),ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji;
+ font-feature-settings: normal;
+ font-variation-settings: normal
+}
+
+body {
+ margin: 0;
+ line-height: inherit
+}
+
+hr {
+ height: 0;
+ color: inherit;
+ border-top-width: 1px
+}
+
+abbr:where([title]) {
+ -webkit-text-decoration: underline dotted;
+ text-decoration: underline dotted
+}
+
+h1,h2,h3,h4,h5,h6 {
+ font-size: inherit;
+ font-weight: inherit
+}
+
+a {
+ color: inherit;
+ text-decoration: inherit
+}
+
+b,strong {
+ font-weight: bolder
+}
+
+code,kbd,pre,samp {
+ font-family: Roboto Mono,monospace;
+ font-size: 1em
+}
+
+small {
+ font-size: 80%
+}
+
+sub,sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline
+}
+
+sub {
+ bottom: -.25em
+}
+
+sup {
+ top: -.5em
+}
+
+table {
+ text-indent: 0;
+ border-color: inherit;
+ border-collapse: collapse
+}
+
+button,input,optgroup,select,textarea {
+ font-family: inherit;
+ font-feature-settings: inherit;
+ font-variation-settings: inherit;
+ font-size: 100%;
+ font-weight: inherit;
+ line-height: inherit;
+ color: inherit;
+ margin: 0;
+ padding: 0
+}
+
+button,select {
+ text-transform: none
+}
+
+[type=button],[type=reset],[type=submit],button {
+ -webkit-appearance: button;
+ background-color: transparent;
+ background-image: none
+}
+
+:-moz-focusring {
+ outline: auto
+}
+
+:-moz-ui-invalid {
+ box-shadow: none
+}
+
+progress {
+ vertical-align: baseline
+}
+
+::-webkit-inner-spin-button,::-webkit-outer-spin-button {
+ height: auto
+}
+
+[type=search] {
+ -webkit-appearance: textfield;
+ outline-offset: -2px
+}
+
+::-webkit-search-decoration {
+ -webkit-appearance: none
+}
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button;
+ font: inherit
+}
+
+summary {
+ display: list-item
+}
+
+blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre {
+ margin: 0
+}
+
+fieldset {
+ margin: 0
+}
+
+fieldset,legend {
+ padding: 0
+}
+
+menu,ol,ul {
+ list-style: none;
+ margin: 0;
+ padding: 0
+}
+
+dialog {
+ padding: 0
+}
+
+textarea {
+ resize: vertical
+}
+
+input::-moz-placeholder,textarea::-moz-placeholder {
+ color: #9ca3af
+}
+
+input::placeholder,textarea::placeholder {
+ color: #9ca3af
+}
+
+[role=button],button {
+ cursor: pointer
+}
+
+:disabled {
+ cursor: default
+}
+
+audio,canvas,embed,iframe,img,object,svg,video {
+ display: block;
+ vertical-align: middle
+}
+
+img,video {
+ max-width: 100%;
+ height: auto
+}
+
+[hidden] {
+ display: none
+}
+
+[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],select,textarea {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background-color: #fff;
+ border-color: #6b7280;
+ border-width: 1px;
+ border-radius: 0;
+ padding: .5rem .75rem;
+ font-size: 1rem;
+ line-height: 1.5rem;
+ --tw-shadow: 0 0 #0000
+}
+
+[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus {
+ outline: 2px solid transparent;
+ outline-offset: 2px;
+ --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-color: #2563eb;
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
+ box-shadow: var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);
+ border-color: #2563eb
+}
+
+input::-moz-placeholder,textarea::-moz-placeholder {
+ color: #6b7280;
+ opacity: 1
+}
+
+input::placeholder,textarea::placeholder {
+ color: #6b7280;
+ opacity: 1
+}
+
+::-webkit-datetime-edit-fields-wrapper {
+ padding: 0
+}
+
+::-webkit-date-and-time-value {
+ min-height: 1.5em
+}
+
+::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field {
+ padding-top: 0;
+ padding-bottom: 0
+}
+
+select {
+ background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9J25vbmUnIHZpZXdCb3g9JzAgMCAyMCAyMCc+PHBhdGggc3Ryb2tlPScjNkI3MjgwJyBzdHJva2UtbGluZWNhcD0ncm91bmQnIHN0cm9rZS1saW5lam9pbj0ncm91bmQnIHN0cm9rZS13aWR0aD0nMS41JyBkPSdNNiA4bDQgNCA0LTQnLz48L3N2Zz4=);
+ background-position: right .5rem center;
+ background-repeat: no-repeat;
+ background-size: 1.5em 1.5em;
+ padding-right: 2.5rem;
+ -webkit-print-color-adjust: exact;
+ print-color-adjust: exact
+}
+
+[multiple] {
+ background-image: none;
+ background-position: 0 0;
+ background-repeat: unset;
+ background-size: initial;
+ padding-right: .75rem;
+ -webkit-print-color-adjust: unset;
+ print-color-adjust: unset
+}
+
+[type=checkbox],[type=radio] {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ padding: 0;
+ -webkit-print-color-adjust: exact;
+ print-color-adjust: exact;
+ display: inline-block;
+ vertical-align: middle;
+ background-origin: border-box;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ flex-shrink: 0;
+ height: 1rem;
+ width: 1rem;
+ color: #2563eb;
+ background-color: #fff;
+ border-color: #6b7280;
+ border-width: 1px;
+ --tw-shadow: 0 0 #0000
+}
+
+[type=checkbox] {
+ border-radius: 0
+}
+
+[type=radio] {
+ border-radius: 100%
+}
+
+[type=checkbox]:focus,[type=radio]:focus {
+ outline: 2px solid transparent;
+ outline-offset: 2px;
+ --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
+ --tw-ring-offset-width: 2px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-color: #2563eb;
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
+ box-shadow: var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)
+}
+
+[type=checkbox]:checked,[type=radio]:checked {
+ border-color: transparent;
+ background-color: currentColor;
+ background-size: 100% 100%;
+ background-position: 50%;
+ background-repeat: no-repeat
+}
+
+[type=checkbox]:checked {
+ background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMTYgMTYnIGZpbGw9J3doaXRlJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxwYXRoIGQ9J00xMi4yMDcgNC43OTNhMSAxIDAgMDEwIDEuNDE0bC01IDVhMSAxIDAgMDEtMS40MTQgMGwtMi0yYTEgMSAwIDAxMS40MTQtMS40MTRMNi41IDkuMDg2bDQuMjkzLTQuMjkzYTEgMSAwIDAxMS40MTQgMHonLz48L3N2Zz4=)
+}
+
+[type=radio]:checked {
+ background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMTYgMTYnIGZpbGw9J3doaXRlJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxjaXJjbGUgY3g9JzgnIGN5PSc4JyByPSczJy8+PC9zdmc+)
+}
+
+[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover {
+ border-color: transparent;
+ background-color: currentColor
+}
+
+[type=checkbox]:indeterminate {
+ background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9J25vbmUnIHZpZXdCb3g9JzAgMCAxNiAxNic+PHBhdGggc3Ryb2tlPSd3aGl0ZScgc3Ryb2tlLWxpbmVjYXA9J3JvdW5kJyBzdHJva2UtbGluZWpvaW49J3JvdW5kJyBzdHJva2Utd2lkdGg9JzInIGQ9J000IDhoOCcvPjwvc3ZnPg==);
+ border-color: transparent;
+ background-color: currentColor;
+ background-size: 100% 100%;
+ background-position: 50%;
+ background-repeat: no-repeat
+}
+
+[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover {
+ border-color: transparent;
+ background-color: currentColor
+}
+
+[type=file] {
+ background: unset;
+ border-color: inherit;
+ border-width: 0;
+ border-radius: 0;
+ padding: 0;
+ font-size: unset;
+ line-height: inherit
+}
+
+[type=file]:focus {
+ outline: 1px solid ButtonText;
+ outline: 1px auto -webkit-focus-ring-color
+}
+
+* {
+ scrollbar-color: auto;
+ scrollbar-width: auto
+}
+
+* {
+ border-color: var(--cal-border,#d1d5db)
+}
+
+*,:after,:before {
+ --tw-border-spacing-x: 0;
+ --tw-border-spacing-y: 0;
+ --tw-translate-x: 0;
+ --tw-translate-y: 0;
+ --tw-rotate: 0;
+ --tw-skew-x: 0;
+ --tw-skew-y: 0;
+ --tw-scale-x: 1;
+ --tw-scale-y: 1;
+ --tw-pan-x: ;
+ --tw-pan-y: ;
+ --tw-pinch-zoom: ;
+ --tw-scroll-snap-strictness: proximity;
+ --tw-gradient-from-position: ;
+ --tw-gradient-via-position: ;
+ --tw-gradient-to-position: ;
+ --tw-ordinal: ;
+ --tw-slashed-zero: ;
+ --tw-numeric-figure: ;
+ --tw-numeric-spacing: ;
+ --tw-numeric-fraction: ;
+ --tw-ring-inset: ;
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-color: rgba(59,130,246,.5);
+ --tw-ring-offset-shadow: 0 0 #0000;
+ --tw-ring-shadow: 0 0 #0000;
+ --tw-shadow: 0 0 #0000;
+ --tw-shadow-colored: 0 0 #0000;
+ --tw-blur: ;
+ --tw-brightness: ;
+ --tw-contrast: ;
+ --tw-grayscale: ;
+ --tw-hue-rotate: ;
+ --tw-invert: ;
+ --tw-saturate: ;
+ --tw-sepia: ;
+ --tw-drop-shadow: ;
+ --tw-backdrop-blur: ;
+ --tw-backdrop-brightness: ;
+ --tw-backdrop-contrast: ;
+ --tw-backdrop-grayscale: ;
+ --tw-backdrop-hue-rotate: ;
+ --tw-backdrop-invert: ;
+ --tw-backdrop-opacity: ;
+ --tw-backdrop-saturate: ;
+ --tw-backdrop-sepia:
+}
+
+::backdrop {
+ --tw-border-spacing-x: 0;
+ --tw-border-spacing-y: 0;
+ --tw-translate-x: 0;
+ --tw-translate-y: 0;
+ --tw-rotate: 0;
+ --tw-skew-x: 0;
+ --tw-skew-y: 0;
+ --tw-scale-x: 1;
+ --tw-scale-y: 1;
+ --tw-pan-x: ;
+ --tw-pan-y: ;
+ --tw-pinch-zoom: ;
+ --tw-scroll-snap-strictness: proximity;
+ --tw-gradient-from-position: ;
+ --tw-gradient-via-position: ;
+ --tw-gradient-to-position: ;
+ --tw-ordinal: ;
+ --tw-slashed-zero: ;
+ --tw-numeric-figure: ;
+ --tw-numeric-spacing: ;
+ --tw-numeric-fraction: ;
+ --tw-ring-inset: ;
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-color: rgba(59,130,246,.5);
+ --tw-ring-offset-shadow: 0 0 #0000;
+ --tw-ring-shadow: 0 0 #0000;
+ --tw-shadow: 0 0 #0000;
+ --tw-shadow-colored: 0 0 #0000;
+ --tw-blur: ;
+ --tw-brightness: ;
+ --tw-contrast: ;
+ --tw-grayscale: ;
+ --tw-hue-rotate: ;
+ --tw-invert: ;
+ --tw-saturate: ;
+ --tw-sepia: ;
+ --tw-drop-shadow: ;
+ --tw-backdrop-blur: ;
+ --tw-backdrop-brightness: ;
+ --tw-backdrop-contrast: ;
+ --tw-backdrop-grayscale: ;
+ --tw-backdrop-hue-rotate: ;
+ --tw-backdrop-invert: ;
+ --tw-backdrop-opacity: ;
+ --tw-backdrop-saturate: ;
+ --tw-backdrop-sepia:
+}
+
+.container {
+ width: 100%
+}
+
+@media(min-width: 640px) {
+ .container {
+ max-width:640px
+ }
+}
+
+@media(min-width: 768px) {
+ .container {
+ max-width:768px
+ }
+}
+
+@media(min-width: 1024px) {
+ .container {
+ max-width:1024px
+ }
+}
+
+@media(min-width: 1280px) {
+ .container {
+ max-width:1280px
+ }
+}
+
+@media(min-width: 1536px) {
+ .container {
+ max-width:1536px
+ }
+}
+
+.prose {
+ color: var(--tw-prose-body);
+ max-width: 65ch
+}
+
+.prose :where([class~=lead]):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-lead);
+ font-size: 1.25em;
+ line-height: 1.6;
+ margin-top: 1.2em;
+ margin-bottom: 1.2em
+}
+
+.prose :where(a):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-links);
+ text-decoration: underline;
+ font-weight: 500
+}
+
+.prose :where(strong):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-bold);
+ font-weight: 600
+}
+
+.prose :where(a strong):not(:where([class~=not-prose] *)) {
+ color: inherit
+}
+
+.prose :where(blockquote strong):not(:where([class~=not-prose] *)) {
+ color: inherit
+}
+
+.prose :where(thead th strong):not(:where([class~=not-prose] *)) {
+ color: inherit
+}
+
+.prose :where(ol):not(:where([class~=not-prose] *)) {
+ list-style-type: decimal;
+ margin-top: 1.25em;
+ margin-bottom: 1.25em;
+ padding-left: 1.625em
+}
+
+.prose :where(ol[type=A]):not(:where([class~=not-prose] *)) {
+ list-style-type: upper-alpha
+}
+
+.prose :where(ol[type=a]):not(:where([class~=not-prose] *)) {
+ list-style-type: lower-alpha
+}
+
+.prose :where(ol[type=As]):not(:where([class~=not-prose] *)) {
+ list-style-type: upper-alpha
+}
+
+.prose :where(ol[type=as]):not(:where([class~=not-prose] *)) {
+ list-style-type: lower-alpha
+}
+
+.prose :where(ol[type=I]):not(:where([class~=not-prose] *)) {
+ list-style-type: upper-roman
+}
+
+.prose :where(ol[type=i]):not(:where([class~=not-prose] *)) {
+ list-style-type: lower-roman
+}
+
+.prose :where(ol[type=Is]):not(:where([class~=not-prose] *)) {
+ list-style-type: upper-roman
+}
+
+.prose :where(ol[type=is]):not(:where([class~=not-prose] *)) {
+ list-style-type: lower-roman
+}
+
+.prose :where(ol[type="1"]):not(:where([class~=not-prose] *)) {
+ list-style-type: decimal
+}
+
+.prose :where(ul):not(:where([class~=not-prose] *)) {
+ list-style-type: disc;
+ margin-top: 1.25em;
+ margin-bottom: 1.25em;
+ padding-left: 1.625em
+}
+
+.prose :where(ol>li):not(:where([class~=not-prose] *))::marker {
+ font-weight: 400;
+ color: var(--tw-prose-counters)
+}
+
+.prose :where(ul>li):not(:where([class~=not-prose] *))::marker {
+ color: var(--tw-prose-bullets)
+}
+
+.prose :where(hr):not(:where([class~=not-prose] *)) {
+ border-color: var(--tw-prose-hr);
+ border-top-width: 1px;
+ margin-top: 3em;
+ margin-bottom: 3em
+}
+
+.prose :where(blockquote):not(:where([class~=not-prose] *)) {
+ font-weight: 500;
+ font-style: italic;
+ color: var(--tw-prose-quotes);
+ border-left-width: .25rem;
+ border-left-color: var(--tw-prose-quote-borders);
+ quotes: "\201C""\201D""\2018""\2019";
+ margin-top: 1.6em;
+ margin-bottom: 1.6em;
+ padding-left: 1em
+}
+
+.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose] *)):before {
+ content: open-quote
+}
+
+.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose] *)):after {
+ content: close-quote
+}
+
+.prose :where(h1):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-headings);
+ font-weight: 800;
+ font-size: 2.25em;
+ margin-top: 0;
+ margin-bottom: .8888889em;
+ line-height: 1.1111111
+}
+
+.prose :where(h1 strong):not(:where([class~=not-prose] *)) {
+ font-weight: 900;
+ color: inherit
+}
+
+.prose :where(h2):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-headings);
+ font-weight: 700;
+ font-size: 1.5em;
+ margin-top: 2em;
+ margin-bottom: 1em;
+ line-height: 1.3333333
+}
+
+.prose :where(h2 strong):not(:where([class~=not-prose] *)) {
+ font-weight: 800;
+ color: inherit
+}
+
+.prose :where(h3):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-headings);
+ font-weight: 600;
+ font-size: 1.25em;
+ margin-top: 1.6em;
+ margin-bottom: .6em;
+ line-height: 1.6
+}
+
+.prose :where(h3 strong):not(:where([class~=not-prose] *)) {
+ font-weight: 700;
+ color: inherit
+}
+
+.prose :where(h4):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-headings);
+ font-weight: 600;
+ margin-top: 1.5em;
+ margin-bottom: .5em;
+ line-height: 1.5
+}
+
+.prose :where(h4 strong):not(:where([class~=not-prose] *)) {
+ font-weight: 700;
+ color: inherit
+}
+
+.prose :where(img):not(:where([class~=not-prose] *)) {
+ margin-top: 2em;
+ margin-bottom: 2em
+}
+
+.prose :where(figure>*):not(:where([class~=not-prose] *)) {
+ margin-top: 0;
+ margin-bottom: 0
+}
+
+.prose :where(figcaption):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-captions);
+ font-size: .875em;
+ line-height: 1.4285714;
+ margin-top: .8571429em
+}
+
+.prose :where(code):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-code);
+ font-weight: 600;
+ font-size: .875em
+}
+
+.prose :where(code):not(:where([class~=not-prose] *)):before {
+ content: "`"
+}
+
+.prose :where(code):not(:where([class~=not-prose] *)):after {
+ content: "`"
+}
+
+.prose :where(a code):not(:where([class~=not-prose] *)) {
+ color: inherit
+}
+
+.prose :where(h1 code):not(:where([class~=not-prose] *)) {
+ color: inherit
+}
+
+.prose :where(h2 code):not(:where([class~=not-prose] *)) {
+ color: inherit;
+ font-size: .875em
+}
+
+.prose :where(h3 code):not(:where([class~=not-prose] *)) {
+ color: inherit;
+ font-size: .9em
+}
+
+.prose :where(h4 code):not(:where([class~=not-prose] *)) {
+ color: inherit
+}
+
+.prose :where(blockquote code):not(:where([class~=not-prose] *)) {
+ color: inherit
+}
+
+.prose :where(thead th code):not(:where([class~=not-prose] *)) {
+ color: inherit
+}
+
+.prose :where(pre):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-pre-code);
+ background-color: var(--tw-prose-pre-bg);
+ overflow-x: auto;
+ font-weight: 400;
+ font-size: .875em;
+ line-height: 1.7142857;
+ margin-top: 1.7142857em;
+ margin-bottom: 1.7142857em;
+ border-radius: .375rem;
+ padding: .8571429em 1.1428571em
+}
+
+.prose :where(pre code):not(:where([class~=not-prose] *)) {
+ background-color: transparent;
+ border-width: 0;
+ border-radius: 0;
+ padding: 0;
+ font-weight: inherit;
+ color: inherit;
+ font-size: inherit;
+ font-family: inherit;
+ line-height: inherit
+}
+
+.prose :where(pre code):not(:where([class~=not-prose] *)):before {
+ content: none
+}
+
+.prose :where(pre code):not(:where([class~=not-prose] *)):after {
+ content: none
+}
+
+.prose :where(table):not(:where([class~=not-prose] *)) {
+ width: 100%;
+ table-layout: auto;
+ text-align: left;
+ margin-top: 2em;
+ margin-bottom: 2em;
+ font-size: .875em;
+ line-height: 1.7142857
+}
+
+.prose :where(thead):not(:where([class~=not-prose] *)) {
+ border-bottom-width: 1px;
+ border-bottom-color: var(--tw-prose-th-borders)
+}
+
+.prose :where(thead th):not(:where([class~=not-prose] *)) {
+ color: var(--tw-prose-headings);
+ font-weight: 600;
+ vertical-align: bottom;
+ padding-right: .5714286em;
+ padding-bottom: .5714286em;
+ padding-left: .5714286em
+}
+
+.prose :where(tbody tr):not(:where([class~=not-prose] *)) {
+ border-bottom-width: 1px;
+ border-bottom-color: var(--tw-prose-td-borders)
+}
+
+.prose :where(tbody tr:last-child):not(:where([class~=not-prose] *)) {
+ border-bottom-width: 0
+}
+
+.prose :where(tbody td):not(:where([class~=not-prose] *)) {
+ vertical-align: baseline
+}
+
+.prose :where(tfoot):not(:where([class~=not-prose] *)) {
+ border-top-width: 1px;
+ border-top-color: var(--tw-prose-th-borders)
+}
+
+.prose :where(tfoot td):not(:where([class~=not-prose] *)) {
+ vertical-align: top
+}
+
+.prose {
+ --tw-prose-body: #374151;
+ --tw-prose-headings: #111827;
+ --tw-prose-lead: #4b5563;
+ --tw-prose-links: #111827;
+ --tw-prose-bold: #111827;
+ --tw-prose-counters: #6b7280;
+ --tw-prose-bullets: #d1d5db;
+ --tw-prose-hr: #e5e7eb;
+ --tw-prose-quotes: #111827;
+ --tw-prose-quote-borders: #e5e7eb;
+ --tw-prose-captions: #6b7280;
+ --tw-prose-code: #111827;
+ --tw-prose-pre-code: #e5e7eb;
+ --tw-prose-pre-bg: #1f2937;
+ --tw-prose-th-borders: #d1d5db;
+ --tw-prose-td-borders: #e5e7eb;
+ --tw-prose-invert-body: #d1d5db;
+ --tw-prose-invert-headings: #fff;
+ --tw-prose-invert-lead: #9ca3af;
+ --tw-prose-invert-links: #fff;
+ --tw-prose-invert-bold: #fff;
+ --tw-prose-invert-counters: #9ca3af;
+ --tw-prose-invert-bullets: #4b5563;
+ --tw-prose-invert-hr: #374151;
+ --tw-prose-invert-quotes: #f3f4f6;
+ --tw-prose-invert-quote-borders: #374151;
+ --tw-prose-invert-captions: #9ca3af;
+ --tw-prose-invert-code: #fff;
+ --tw-prose-invert-pre-code: #d1d5db;
+ --tw-prose-invert-pre-bg: rgba(0,0,0,.5);
+ --tw-prose-invert-th-borders: #4b5563;
+ --tw-prose-invert-td-borders: #374151;
+ font-size: 1rem;
+ line-height: 1.75
+}
+
+.prose :where(p):not(:where([class~=not-prose] *)) {
+ margin-top: 1.25em;
+ margin-bottom: 1.25em
+}
+
+.prose :where(video):not(:where([class~=not-prose] *)) {
+ margin-top: 2em;
+ margin-bottom: 2em
+}
+
+.prose :where(figure):not(:where([class~=not-prose] *)) {
+ margin-top: 2em;
+ margin-bottom: 2em
+}
+
+.prose :where(li):not(:where([class~=not-prose] *)) {
+ margin-top: .5em;
+ margin-bottom: .5em
+}
+
+.prose :where(ol>li):not(:where([class~=not-prose] *)) {
+ padding-left: .375em
+}
+
+.prose :where(ul>li):not(:where([class~=not-prose] *)) {
+ padding-left: .375em
+}
+
+.prose :where(.prose>ul>li p):not(:where([class~=not-prose] *)) {
+ margin-top: .75em;
+ margin-bottom: .75em
+}
+
+.prose :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.25em
+}
+
+.prose :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.25em
+}
+
+.prose :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.25em
+}
+
+.prose :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.25em
+}
+
+.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose] *)) {
+ margin-top: .75em;
+ margin-bottom: .75em
+}
+
+.prose :where(hr+*):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose :where(h2+*):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose :where(h3+*):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose :where(h4+*):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose :where(thead th:first-child):not(:where([class~=not-prose] *)) {
+ padding-left: 0
+}
+
+.prose :where(thead th:last-child):not(:where([class~=not-prose] *)) {
+ padding-right: 0
+}
+
+.prose :where(tbody td,tfoot td):not(:where([class~=not-prose] *)) {
+ padding: .5714286em
+}
+
+.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose] *)) {
+ padding-left: 0
+}
+
+.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose] *)) {
+ padding-right: 0
+}
+
+.prose :where(.prose>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose :where(.prose>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 0
+}
+
+.prose-sm {
+ font-size: .875rem;
+ line-height: 1.7142857
+}
+
+.prose-sm :where(p):not(:where([class~=not-prose] *)) {
+ margin-top: 1.1428571em;
+ margin-bottom: 1.1428571em
+}
+
+.prose-sm :where([class~=lead]):not(:where([class~=not-prose] *)) {
+ font-size: 1.2857143em;
+ line-height: 1.5555556;
+ margin-top: .8888889em;
+ margin-bottom: .8888889em
+}
+
+.prose-sm :where(blockquote):not(:where([class~=not-prose] *)) {
+ margin-top: 1.3333333em;
+ margin-bottom: 1.3333333em;
+ padding-left: 1.1111111em
+}
+
+.prose-sm :where(h1):not(:where([class~=not-prose] *)) {
+ font-size: 2.1428571em;
+ margin-top: 0;
+ margin-bottom: .8em;
+ line-height: 1.2
+}
+
+.prose-sm :where(h2):not(:where([class~=not-prose] *)) {
+ font-size: 1.4285714em;
+ margin-top: 1.6em;
+ margin-bottom: .8em;
+ line-height: 1.4
+}
+
+.prose-sm :where(h3):not(:where([class~=not-prose] *)) {
+ font-size: 1.2857143em;
+ margin-top: 1.5555556em;
+ margin-bottom: .4444444em;
+ line-height: 1.5555556
+}
+
+.prose-sm :where(h4):not(:where([class~=not-prose] *)) {
+ margin-top: 1.4285714em;
+ margin-bottom: .5714286em;
+ line-height: 1.4285714
+}
+
+.prose-sm :where(img):not(:where([class~=not-prose] *)) {
+ margin-top: 1.7142857em;
+ margin-bottom: 1.7142857em
+}
+
+.prose-sm :where(video):not(:where([class~=not-prose] *)) {
+ margin-top: 1.7142857em;
+ margin-bottom: 1.7142857em
+}
+
+.prose-sm :where(figure):not(:where([class~=not-prose] *)) {
+ margin-top: 1.7142857em;
+ margin-bottom: 1.7142857em
+}
+
+.prose-sm :where(figure>*):not(:where([class~=not-prose] *)) {
+ margin-top: 0;
+ margin-bottom: 0
+}
+
+.prose-sm :where(figcaption):not(:where([class~=not-prose] *)) {
+ font-size: .8571429em;
+ line-height: 1.3333333;
+ margin-top: .6666667em
+}
+
+.prose-sm :where(code):not(:where([class~=not-prose] *)) {
+ font-size: .8571429em
+}
+
+.prose-sm :where(h2 code):not(:where([class~=not-prose] *)) {
+ font-size: .9em
+}
+
+.prose-sm :where(h3 code):not(:where([class~=not-prose] *)) {
+ font-size: .8888889em
+}
+
+.prose-sm :where(pre):not(:where([class~=not-prose] *)) {
+ font-size: .8571429em;
+ line-height: 1.6666667;
+ margin-top: 1.6666667em;
+ margin-bottom: 1.6666667em;
+ border-radius: .25rem;
+ padding: .6666667em 1em
+}
+
+.prose-sm :where(ol):not(:where([class~=not-prose] *)) {
+ margin-top: 1.1428571em;
+ margin-bottom: 1.1428571em;
+ padding-left: 1.5714286em
+}
+
+.prose-sm :where(ul):not(:where([class~=not-prose] *)) {
+ margin-top: 1.1428571em;
+ margin-bottom: 1.1428571em;
+ padding-left: 1.5714286em
+}
+
+.prose-sm :where(li):not(:where([class~=not-prose] *)) {
+ margin-top: .2857143em;
+ margin-bottom: .2857143em
+}
+
+.prose-sm :where(ol>li):not(:where([class~=not-prose] *)) {
+ padding-left: .4285714em
+}
+
+.prose-sm :where(ul>li):not(:where([class~=not-prose] *)) {
+ padding-left: .4285714em
+}
+
+.prose-sm :where(.prose>ul>li p):not(:where([class~=not-prose] *)) {
+ margin-top: .5714286em;
+ margin-bottom: .5714286em
+}
+
+.prose-sm :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.1428571em
+}
+
+.prose-sm :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.1428571em
+}
+
+.prose-sm :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.1428571em
+}
+
+.prose-sm :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.1428571em
+}
+
+.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose] *)) {
+ margin-top: .5714286em;
+ margin-bottom: .5714286em
+}
+
+.prose-sm :where(hr):not(:where([class~=not-prose] *)) {
+ margin-top: 2.8571429em;
+ margin-bottom: 2.8571429em
+}
+
+.prose-sm :where(hr+*):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose-sm :where(h2+*):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose-sm :where(h3+*):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose-sm :where(h4+*):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose-sm :where(table):not(:where([class~=not-prose] *)) {
+ font-size: .8571429em;
+ line-height: 1.5
+}
+
+.prose-sm :where(thead th):not(:where([class~=not-prose] *)) {
+ padding-right: 1em;
+ padding-bottom: .6666667em;
+ padding-left: 1em
+}
+
+.prose-sm :where(thead th:first-child):not(:where([class~=not-prose] *)) {
+ padding-left: 0
+}
+
+.prose-sm :where(thead th:last-child):not(:where([class~=not-prose] *)) {
+ padding-right: 0
+}
+
+.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose] *)) {
+ padding: .6666667em 1em
+}
+
+.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose] *)) {
+ padding-left: 0
+}
+
+.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose] *)) {
+ padding-right: 0
+}
+
+.prose-sm :where(.prose>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose-sm :where(.prose>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 0
+}
+
+.prose-base :where(.prose>ul>li p):not(:where([class~=not-prose] *)) {
+ margin-top: .75em;
+ margin-bottom: .75em
+}
+
+.prose-base :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.25em
+}
+
+.prose-base :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.25em
+}
+
+.prose-base :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.25em
+}
+
+.prose-base :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.25em
+}
+
+.prose-base :where(.prose>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose-base :where(.prose>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 0
+}
+
+.prose-lg :where(.prose>ul>li p):not(:where([class~=not-prose] *)) {
+ margin-top: .8888889em;
+ margin-bottom: .8888889em
+}
+
+.prose-lg :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.3333333em
+}
+
+.prose-lg :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.3333333em
+}
+
+.prose-lg :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.3333333em
+}
+
+.prose-lg :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.3333333em
+}
+
+.prose-lg :where(.prose>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose-lg :where(.prose>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 0
+}
+
+.prose-xl :where(.prose>ul>li p):not(:where([class~=not-prose] *)) {
+ margin-top: .8em;
+ margin-bottom: .8em
+}
+
+.prose-xl :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.2em
+}
+
+.prose-xl :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.2em
+}
+
+.prose-xl :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.2em
+}
+
+.prose-xl :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.2em
+}
+
+.prose-xl :where(.prose>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose-xl :where(.prose>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 0
+}
+
+.prose-2xl :where(.prose>ul>li p):not(:where([class~=not-prose] *)) {
+ margin-top: .8333333em;
+ margin-bottom: .8333333em
+}
+
+.prose-2xl :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.3333333em
+}
+
+.prose-2xl :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.3333333em
+}
+
+.prose-2xl :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 1.3333333em
+}
+
+.prose-2xl :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 1.3333333em
+}
+
+.prose-2xl :where(.prose>:first-child):not(:where([class~=not-prose] *)) {
+ margin-top: 0
+}
+
+.prose-2xl :where(.prose>:last-child):not(:where([class~=not-prose] *)) {
+ margin-bottom: 0
+}
+
+.prose-invert {
+ --tw-prose-body: var(--tw-prose-invert-body);
+ --tw-prose-headings: var(--tw-prose-invert-headings);
+ --tw-prose-lead: var(--tw-prose-invert-lead);
+ --tw-prose-links: var(--tw-prose-invert-links);
+ --tw-prose-bold: var(--tw-prose-invert-bold);
+ --tw-prose-counters: var(--tw-prose-invert-counters);
+ --tw-prose-bullets: var(--tw-prose-invert-bullets);
+ --tw-prose-hr: var(--tw-prose-invert-hr);
+ --tw-prose-quotes: var(--tw-prose-invert-quotes);
+ --tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);
+ --tw-prose-captions: var(--tw-prose-invert-captions);
+ --tw-prose-code: var(--tw-prose-invert-code);
+ --tw-prose-pre-code: var(--tw-prose-invert-pre-code);
+ --tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);
+ --tw-prose-th-borders: var(--tw-prose-invert-th-borders);
+ --tw-prose-td-borders: var(--tw-prose-invert-td-borders)
+}
+
+.scroll-bar {
+ --scrollbar-track: initial;
+ --scrollbar-thumb: initial;
+ --scrollbar-corner: initial;
+ --scrollbar-track-hover: var(--scrollbar-track);
+ --scrollbar-thumb-hover: var(--scrollbar-thumb);
+ --scrollbar-corner-hover: var(--scrollbar-corner);
+ --scrollbar-track-active: var(--scrollbar-track-hover);
+ --scrollbar-thumb-active: var(--scrollbar-thumb-hover);
+ --scrollbar-corner-active: var(--scrollbar-corner-hover);
+ scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
+ overflow: overlay
+}
+
+.scroll-bar.overflow-x-hidden {
+ overflow-x: hidden
+}
+
+.scroll-bar.overflow-y-hidden {
+ overflow-y: hidden
+}
+
+.scroll-bar::-webkit-scrollbar-track {
+ background-color: var(--scrollbar-track)
+}
+
+.scroll-bar::-webkit-scrollbar-thumb {
+ background-color: var(--scrollbar-thumb)
+}
+
+.scroll-bar::-webkit-scrollbar-corner {
+ background-color: var(--scrollbar-corner)
+}
+
+.scroll-bar::-webkit-scrollbar-track:hover {
+ background-color: var(--scrollbar-track-hover)
+}
+
+.scroll-bar::-webkit-scrollbar-thumb:hover {
+ background-color: var(--scrollbar-thumb-hover)
+}
+
+.scroll-bar::-webkit-scrollbar-corner:hover {
+ background-color: var(--scrollbar-corner-hover)
+}
+
+.scroll-bar::-webkit-scrollbar-track:active {
+ background-color: var(--scrollbar-track-active)
+}
+
+.scroll-bar::-webkit-scrollbar-thumb:active {
+ background-color: var(--scrollbar-thumb-active)
+}
+
+.scroll-bar::-webkit-scrollbar-corner:active {
+ background-color: var(--scrollbar-corner-active)
+}
+
+.scroll-bar {
+ scrollbar-width: thin
+}
+
+.scroll-bar::-webkit-scrollbar {
+ width: 8px;
+ height: 8px
+}
+
+.scroll-bar {
+ --scrollbar-track: transparent;
+ --scrollbar-thumb: #d1d5db
+}
+
+.scroll-bar::-webkit-scrollbar-thumb {
+ border-radius: .375rem
+}
+
+:is(.dark .scroll-bar) {
+ --scrollbar-thumb: #444
+}
+
+.slider {
+ position: relative;
+ display: flex;
+ height: 1rem;
+ width: 10rem;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ align-items: center
+}
+
+.slider>.slider-track {
+ position: relative;
+ height: .25rem;
+ flex-grow: 1;
+ border-radius: .375rem;
+ --tw-bg-opacity: 1;
+ background-color: rgb(156 163 175/var(--tw-bg-opacity))
+}
+
+.slider .slider-range {
+ position: absolute;
+ height: 100%
+}
+
+.slider .slider-range,.slider .slider-thumb {
+ border-radius: 9999px;
+ --tw-bg-opacity: 1;
+ background-color: rgb(55 65 81/var(--tw-bg-opacity))
+}
+
+.slider .slider-thumb {
+ display: block;
+ height: .75rem;
+ width: .75rem;
+ cursor: pointer;
+ transition-property: all;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ transition-duration: .15s
+}
+
+.slider .slider-thumb:hover {
+ --tw-bg-opacity: 1;
+ background-color: rgb(75 85 99/var(--tw-bg-opacity))
+}
+
+.slider .slider-thumb:focus {
+ box-shadow: 0 0 0 4px rgba(0,0,0,.2)
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0,0,0,0);
+ white-space: nowrap;
+ border-width: 0
+}
+
+.not-sr-only {
+ position: static;
+ width: auto;
+ height: auto;
+ padding: 0;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+ white-space: normal
+}
+
+.pointer-events-none {
+ pointer-events: none
+}
+
+.\!visible {
+ visibility: visible!important
+}
+
+.visible {
+ visibility: visible
+}
+
+.invisible {
+ visibility: hidden
+}
+
+.collapse {
+ visibility: collapse
+}
+
+.\!static {
+ position: static!important
+}
+
+.static {
+ position: static
+}
+
+.fixed {
+ position: fixed
+}
+
+.absolute {
+ position: absolute
+}
+
+.relative {
+ position: relative
+}
+
+.sticky {
+ position: sticky
+}
+
+.-inset-1 {
+ inset: -.25rem
+}
+
+.inset-0 {
+ inset: 0
+}
+
+.inset-x-0 {
+ left: 0;
+ right: 0
+}
+
+.inset-x-1 {
+ left: .25rem;
+ right: .25rem
+}
+
+.inset-y-0 {
+ top: 0;
+ bottom: 0
+}
+
+.-bottom-0 {
+ bottom: 0
+}
+
+.-bottom-0\.5 {
+ bottom: -.125rem
+}
+
+.-bottom-1 {
+ bottom: -.25rem
+}
+
+.-left-\[12px\] {
+ left: -12px
+}
+
+.-right-0 {
+ right: 0
+}
+
+.-right-0\.5 {
+ right: -.125rem
+}
+
+.-right-1 {
+ right: -.25rem
+}
+
+.-top-\[2px\] {
+ top: -2px
+}
+
+.-top-px {
+ top: -1px
+}
+
+.bottom-0 {
+ bottom: 0
+}
+
+.bottom-1 {
+ bottom: .25rem
+}
+
+.bottom-2 {
+ bottom: .5rem
+}
+
+.bottom-20 {
+ bottom: 5rem
+}
+
+.bottom-3 {
+ bottom: .75rem
+}
+
+.bottom-4 {
+ bottom: 1rem
+}
+
+.bottom-6 {
+ bottom: 1.5rem
+}
+
+.bottom-7 {
+ bottom: 1.75rem
+}
+
+.bottom-\[6px\] {
+ bottom: 6px
+}
+
+.bottom-px {
+ bottom: 1px
+}
+
+.left-0 {
+ left: 0
+}
+
+.left-0\.5 {
+ left: .125rem
+}
+
+.left-1 {
+ left: .25rem
+}
+
+.left-1\/2 {
+ left: 50%
+}
+
+.left-1\/4 {
+ left: 25%
+}
+
+.left-2 {
+ left: .5rem
+}
+
+.left-3 {
+ left: .75rem
+}
+
+.left-4 {
+ left: 1rem
+}
+
+.left-6 {
+ left: 1.5rem
+}
+
+.left-8 {
+ left: 2rem
+}
+
+.left-\[calc\(50\%\+calc\(var\(--booker-meta-width\2c 0px\)\/2\)\)\] {
+ left: calc(50% + calc(var(--booker-meta-width,0px)/2))
+}
+
+.left-px {
+ left: 1px
+}
+
+.right-0 {
+ right: 0
+}
+
+.right-1 {
+ right: .25rem
+}
+
+.right-1\/2 {
+ right: 50%
+}
+
+.right-2 {
+ right: .5rem
+}
+
+.right-3 {
+ right: .75rem
+}
+
+.right-4 {
+ right: 1rem
+}
+
+.right-6 {
+ right: 1.5rem
+}
+
+.right-8 {
+ right: 2rem
+}
+
+.right-\[2px\] {
+ right: 2px
+}
+
+.right-px {
+ right: 1px
+}
+
+.top-0 {
+ top: 0
+}
+
+.top-1 {
+ top: .25rem
+}
+
+.top-1\/2 {
+ top: 50%
+}
+
+.top-2 {
+ top: .5rem
+}
+
+.top-2\.5 {
+ top: .625rem
+}
+
+.top-3 {
+ top: .75rem
+}
+
+.top-4 {
+ top: 1rem
+}
+
+.top-40 {
+ top: 10rem
+}
+
+.top-6 {
+ top: 1.5rem
+}
+
+.top-8 {
+ top: 2rem
+}
+
+.top-\[0\.9rem\] {
+ top: .9rem
+}
+
+.top-\[var\(--calendar-dates-sticky-offset\2c 0px\)\] {
+ top: var(--calendar-dates-sticky-offset,0)
+}
+
+.top-px {
+ top: 1px
+}
+
+.isolate {
+ isolation: isolate
+}
+
+.isolation-auto {
+ isolation: auto
+}
+
+.-z-10 {
+ z-index: -10
+}
+
+.z-0 {
+ z-index: 0
+}
+
+.z-10 {
+ z-index: 10
+}
+
+.z-20 {
+ z-index: 20
+}
+
+.z-30 {
+ z-index: 30
+}
+
+.z-40 {
+ z-index: 40
+}
+
+.z-50 {
+ z-index: 50
+}
+
+.z-\[2\] {
+ z-index: 2
+}
+
+.z-\[60\] {
+ z-index: 60
+}
+
+.z-\[80\] {
+ z-index: 80
+}
+
+.order-2 {
+ order: 2
+}
+
+.order-none {
+ order: 0
+}
+
+.col-span-1 {
+ grid-column: span 1/span 1
+}
+
+.col-span-10 {
+ grid-column: span 10/span 10
+}
+
+.col-span-11 {
+ grid-column: span 11/span 11
+}
+
+.col-span-12 {
+ grid-column: span 12/span 12
+}
+
+.col-span-2 {
+ grid-column: span 2/span 2
+}
+
+.col-span-3 {
+ grid-column: span 3/span 3
+}
+
+.col-span-4 {
+ grid-column: span 4/span 4
+}
+
+.col-span-5 {
+ grid-column: span 5/span 5
+}
+
+.col-span-6 {
+ grid-column: span 6/span 6
+}
+
+.col-span-7 {
+ grid-column: span 7/span 7
+}
+
+.col-span-8 {
+ grid-column: span 8/span 8
+}
+
+.col-span-9 {
+ grid-column: span 9/span 9
+}
+
+.col-start-1 {
+ grid-column-start: 1
+}
+
+.col-start-2 {
+ grid-column-start: 2
+}
+
+.col-start-3 {
+ grid-column-start: 3
+}
+
+.col-start-4 {
+ grid-column-start: 4
+}
+
+.col-start-5 {
+ grid-column-start: 5
+}
+
+.col-start-6 {
+ grid-column-start: 6
+}
+
+.col-start-7 {
+ grid-column-start: 7
+}
+
+.col-end-1 {
+ grid-column-end: 1
+}
+
+.col-end-2 {
+ grid-column-end: 2
+}
+
+.row-span-full {
+ grid-row: 1/-1
+}
+
+.row-start-1 {
+ grid-row-start: 1
+}
+
+.row-end-1 {
+ grid-row-end: 1
+}
+
+.float-right {
+ float: right
+}
+
+.float-left {
+ float: left
+}
+
+.\!m-0 {
+ margin: 0!important
+}
+
+.m-0 {
+ margin: 0
+}
+
+.m-1 {
+ margin: .25rem
+}
+
+.m-10 {
+ margin: 2.5rem
+}
+
+.m-2 {
+ margin: .5rem
+}
+
+.m-4 {
+ margin: 1rem
+}
+
+.m-8 {
+ margin: 2rem
+}
+
+.m-auto {
+ margin: auto
+}
+
+.\!my-0 {
+ margin-top: 0!important;
+ margin-bottom: 0!important
+}
+
+.-mx-1 {
+ margin-left: -.25rem;
+ margin-right: -.25rem
+}
+
+.-mx-2 {
+ margin-left: -.5rem;
+ margin-right: -.5rem
+}
+
+.-mx-4 {
+ margin-left: -1rem;
+ margin-right: -1rem
+}
+
+.-mx-8 {
+ margin-left: -2rem;
+ margin-right: -2rem
+}
+
+.-mx-9 {
+ margin-left: -2.25rem;
+ margin-right: -2.25rem
+}
+
+.-my-1 {
+ margin-top: -.25rem;
+ margin-bottom: -.25rem
+}
+
+.mx-0 {
+ margin-left: 0;
+ margin-right: 0
+}
+
+.mx-1 {
+ margin-left: .25rem;
+ margin-right: .25rem
+}
+
+.mx-2 {
+ margin-left: .5rem;
+ margin-right: .5rem
+}
+
+.mx-3 {
+ margin-left: .75rem;
+ margin-right: .75rem
+}
+
+.mx-4 {
+ margin-left: 1rem;
+ margin-right: 1rem
+}
+
+.mx-5 {
+ margin-left: 1.25rem;
+ margin-right: 1.25rem
+}
+
+.mx-6 {
+ margin-left: 1.5rem;
+ margin-right: 1.5rem
+}
+
+.mx-8 {
+ margin-left: 2rem;
+ margin-right: 2rem
+}
+
+.mx-auto {
+ margin-left: auto;
+ margin-right: auto
+}
+
+.my-0 {
+ margin-top: 0;
+ margin-bottom: 0
+}
+
+.my-0\.5 {
+ margin-top: .125rem;
+ margin-bottom: .125rem
+}
+
+.my-1 {
+ margin-top: .25rem;
+ margin-bottom: .25rem
+}
+
+.my-1\.5 {
+ margin-top: .375rem;
+ margin-bottom: .375rem
+}
+
+.my-2 {
+ margin-top: .5rem;
+ margin-bottom: .5rem
+}
+
+.my-2\.5 {
+ margin-top: .625rem;
+ margin-bottom: .625rem
+}
+
+.my-24 {
+ margin-top: 6rem;
+ margin-bottom: 6rem
+}
+
+.my-3 {
+ margin-top: .75rem;
+ margin-bottom: .75rem
+}
+
+.my-4 {
+ margin-top: 1rem;
+ margin-bottom: 1rem
+}
+
+.my-5 {
+ margin-top: 1.25rem;
+ margin-bottom: 1.25rem
+}
+
+.my-6 {
+ margin-top: 1.5rem;
+ margin-bottom: 1.5rem
+}
+
+.my-7 {
+ margin-top: 1.75rem;
+ margin-bottom: 1.75rem
+}
+
+.my-8 {
+ margin-top: 2rem;
+ margin-bottom: 2rem
+}
+
+.my-auto {
+ margin-top: auto;
+ margin-bottom: auto
+}
+
+.\!mb-0 {
+ margin-bottom: 0!important
+}
+
+.\!mb-3 {
+ margin-bottom: .75rem!important
+}
+
+.\!mt-0 {
+ margin-top: 0!important
+}
+
+.\!mt-2 {
+ margin-top: .5rem!important
+}
+
+.\!mt-4 {
+ margin-top: 1rem!important
+}
+
+.-mb-0 {
+ margin-bottom: 0
+}
+
+.-mb-1 {
+ margin-bottom: -.25rem
+}
+
+.-mb-2 {
+ margin-bottom: -.5rem
+}
+
+.-mb-20 {
+ margin-bottom: -5rem
+}
+
+.-mb-3 {
+ margin-bottom: -.75rem
+}
+
+.-mb-4 {
+ margin-bottom: -1rem
+}
+
+.-mb-6 {
+ margin-bottom: -1.5rem
+}
+
+.-mb-px {
+ margin-bottom: -1px
+}
+
+.-ml-0 {
+ margin-left: 0
+}
+
+.-ml-0\.5 {
+ margin-left: -.125rem
+}
+
+.-ml-1 {
+ margin-left: -.25rem
+}
+
+.-ml-14 {
+ margin-left: -3.5rem
+}
+
+.-ml-2 {
+ margin-left: -.5rem
+}
+
+.-ml-3 {
+ margin-left: -.75rem
+}
+
+.-ml-4 {
+ margin-left: -1rem
+}
+
+.-ml-6 {
+ margin-left: -1.5rem
+}
+
+.-ml-8 {
+ margin-left: -2rem
+}
+
+.-ml-\[13px\] {
+ margin-left: -13px
+}
+
+.-ml-\[3px\] {
+ margin-left: -3px
+}
+
+.-ml-px {
+ margin-left: -1px
+}
+
+.-mr-0 {
+ margin-right: 0
+}
+
+.-mr-1 {
+ margin-right: -.25rem
+}
+
+.-mr-2 {
+ margin-right: -.5rem
+}
+
+.-mr-3 {
+ margin-right: -.75rem
+}
+
+.-mr-4 {
+ margin-right: -1rem
+}
+
+.-mr-6 {
+ margin-right: -1.5rem
+}
+
+.-mr-px {
+ margin-right: -1px
+}
+
+.-mt-0 {
+ margin-top: 0
+}
+
+.-mt-0\.5 {
+ margin-top: -.125rem
+}
+
+.-mt-1 {
+ margin-top: -.25rem
+}
+
+.-mt-1\.5 {
+ margin-top: -.375rem
+}
+
+.-mt-10 {
+ margin-top: -2.5rem
+}
+
+.-mt-11 {
+ margin-top: -2.75rem
+}
+
+.-mt-2 {
+ margin-top: -.5rem
+}
+
+.-mt-2\.5 {
+ margin-top: -.625rem
+}
+
+.-mt-3 {
+ margin-top: -.75rem
+}
+
+.-mt-4 {
+ margin-top: -1rem
+}
+
+.-mt-6 {
+ margin-top: -1.5rem
+}
+
+.-mt-7 {
+ margin-top: -1.75rem
+}
+
+.-mt-\[2px\] {
+ margin-top: -2px
+}
+
+.-mt-px {
+ margin-top: -1px
+}
+
+.mb-0 {
+ margin-bottom: 0
+}
+
+.mb-0\.5 {
+ margin-bottom: .125rem
+}
+
+.mb-1 {
+ margin-bottom: .25rem
+}
+
+.mb-10 {
+ margin-bottom: 2.5rem
+}
+
+.mb-16 {
+ margin-bottom: 4rem
+}
+
+.mb-2 {
+ margin-bottom: .5rem
+}
+
+.mb-2\.5 {
+ margin-bottom: .625rem
+}
+
+.mb-3 {
+ margin-bottom: .75rem
+}
+
+.mb-4 {
+ margin-bottom: 1rem
+}
+
+.mb-5 {
+ margin-bottom: 1.25rem
+}
+
+.mb-6 {
+ margin-bottom: 1.5rem
+}
+
+.mb-8 {
+ margin-bottom: 2rem
+}
+
+.mb-\[-1px\] {
+ margin-bottom: -1px
+}
+
+.mb-\[9px\] {
+ margin-bottom: 9px
+}
+
+.mb-auto {
+ margin-bottom: auto
+}
+
+.mb-px {
+ margin-bottom: 1px
+}
+
+.me-1 {
+ -webkit-margin-end: .25rem;
+ margin-inline-end:.25rem}
+
+.me-1\.5 {
+ -webkit-margin-end: .375rem;
+ margin-inline-end:.375rem}
+
+.me-2 {
+ -webkit-margin-end: .5rem;
+ margin-inline-end:.5rem}
+
+.me-3 {
+ -webkit-margin-end: .75rem;
+ margin-inline-end:.75rem}
+
+.me-4 {
+ -webkit-margin-end: 1rem;
+ margin-inline-end:1rem}
+
+.me-5 {
+ -webkit-margin-end: 1.25rem;
+ margin-inline-end:1.25rem}
+
+.me-\[10px\] {
+ -webkit-margin-end: 10px;
+ margin-inline-end:10px}
+
+.ml-0 {
+ margin-left: 0
+}
+
+.ml-1 {
+ margin-left: .25rem
+}
+
+.ml-1\.5 {
+ margin-left: .375rem
+}
+
+.ml-11 {
+ margin-left: 2.75rem
+}
+
+.ml-16 {
+ margin-left: 4rem
+}
+
+.ml-2 {
+ margin-left: .5rem
+}
+
+.ml-3 {
+ margin-left: .75rem
+}
+
+.ml-4 {
+ margin-left: 1rem
+}
+
+.ml-5 {
+ margin-left: 1.25rem
+}
+
+.ml-6 {
+ margin-left: 1.5rem
+}
+
+.ml-7 {
+ margin-left: 1.75rem
+}
+
+.ml-8 {
+ margin-left: 2rem
+}
+
+.ml-\[-1px\] {
+ margin-left: -1px
+}
+
+.ml-auto {
+ margin-left: auto
+}
+
+.ml-px {
+ margin-left: 1px
+}
+
+.mr-0 {
+ margin-right: 0
+}
+
+.mr-1 {
+ margin-right: .25rem
+}
+
+.mr-1\.5 {
+ margin-right: .375rem
+}
+
+.mr-12 {
+ margin-right: 3rem
+}
+
+.mr-2 {
+ margin-right: .5rem
+}
+
+.mr-3 {
+ margin-right: .75rem
+}
+
+.mr-4 {
+ margin-right: 1rem
+}
+
+.mr-5 {
+ margin-right: 1.25rem
+}
+
+.mr-6 {
+ margin-right: 1.5rem
+}
+
+.mr-7 {
+ margin-right: 1.75rem
+}
+
+.mr-8 {
+ margin-right: 2rem
+}
+
+.mr-\[11px\] {
+ margin-right: 11px
+}
+
+.mr-\[9px\] {
+ margin-right: 9px
+}
+
+.mr-auto {
+ margin-right: auto
+}
+
+.mr-px {
+ margin-right: 1px
+}
+
+.ms-1 {
+ -webkit-margin-start: .25rem;
+ margin-inline-start:.25rem}
+
+.ms-2 {
+ -webkit-margin-start: .5rem;
+ margin-inline-start:.5rem}
+
+.ms-3 {
+ -webkit-margin-start: .75rem;
+ margin-inline-start:.75rem}
+
+.ms-4 {
+ -webkit-margin-start: 1rem;
+ margin-inline-start:1rem}
+
+.ms-auto {
+ -webkit-margin-start: auto;
+ margin-inline-start:auto}
+
+.mt-0 {
+ margin-top: 0
+}
+
+.mt-0\.5 {
+ margin-top: .125rem
+}
+
+.mt-1 {
+ margin-top: .25rem
+}
+
+.mt-1\.5 {
+ margin-top: .375rem
+}
+
+.mt-10 {
+ margin-top: 2.5rem
+}
+
+.mt-12 {
+ margin-top: 3rem
+}
+
+.mt-2 {
+ margin-top: .5rem
+}
+
+.mt-2\.5 {
+ margin-top: .625rem
+}
+
+.mt-24 {
+ margin-top: 6rem
+}
+
+.mt-3 {
+ margin-top: .75rem
+}
+
+.mt-4 {
+ margin-top: 1rem
+}
+
+.mt-5 {
+ margin-top: 1.25rem
+}
+
+.mt-6 {
+ margin-top: 1.5rem
+}
+
+.mt-7 {
+ margin-top: 1.75rem
+}
+
+.mt-8 {
+ margin-top: 2rem
+}
+
+.mt-9 {
+ margin-top: 2.25rem
+}
+
+.mt-\[22px\] {
+ margin-top: 22px
+}
+
+.mt-\[2px\] {
+ margin-top: 2px
+}
+
+.mt-\[3px\] {
+ margin-top: 3px
+}
+
+.mt-\[9px\] {
+ margin-top: 9px
+}
+
+.mt-auto {
+ margin-top: auto
+}
+
+.mt-px {
+ margin-top: 1px
+}
+
+.box-border {
+ box-sizing: border-box
+}
+
+.line-clamp-1 {
+ -webkit-line-clamp: 1
+}
+
+.line-clamp-1,.line-clamp-2 {
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-box-orient: vertical
+}
+
+.line-clamp-2 {
+ -webkit-line-clamp: 2
+}
+
+.line-clamp-3 {
+ -webkit-line-clamp: 3
+}
+
+.line-clamp-3,.line-clamp-4 {
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-box-orient: vertical
+}
+
+.line-clamp-4 {
+ -webkit-line-clamp: 4
+}
+
+.block {
+ display: block
+}
+
+.inline-block {
+ display: inline-block
+}
+
+.inline {
+ display: inline
+}
+
+.\!flex {
+ display: flex!important
+}
+
+.flex {
+ display: flex
+}
+
+.inline-flex {
+ display: inline-flex
+}
+
+.\!table {
+ display: table!important
+}
+
+.table {
+ display: table
+}
+
+.inline-table {
+ display: inline-table
+}
+
+.table-caption {
+ display: table-caption
+}
+
+.table-cell {
+ display: table-cell
+}
+
+.table-column {
+ display: table-column
+}
+
+.table-column-group {
+ display: table-column-group
+}
+
+.table-footer-group {
+ display: table-footer-group
+}
+
+.table-header-group {
+ display: table-header-group
+}
+
+.table-row-group {
+ display: table-row-group
+}
+
+.table-row {
+ display: table-row
+}
+
+.flow-root {
+ display: flow-root
+}
+
+.grid {
+ display: grid
+}
+
+.inline-grid {
+ display: inline-grid
+}
+
+.contents {
+ display: contents
+}
+
+.list-item {
+ display: list-item
+}
+
+.hidden {
+ display: none
+}
+
+.aspect-square {
+ aspect-ratio: 1/1
+}
+
+.aspect-video {
+ aspect-ratio: 16/9
+}
+
+.\!h-32 {
+ height: 8rem!important
+}
+
+.h-0 {
+ height: 0
+}
+
+.h-0\.5 {
+ height: .125rem
+}
+
+.h-1 {
+ height: .25rem
+}
+
+.h-1\/2 {
+ height: 50%
+}
+
+.h-1\/3 {
+ height: 33.333333%
+}
+
+.h-1\/4 {
+ height: 25%
+}
+
+.h-10 {
+ height: 2.5rem
+}
+
+.h-11 {
+ height: 2.75rem
+}
+
+.h-12 {
+ height: 3rem
+}
+
+.h-14 {
+ height: 3.5rem
+}
+
+.h-16 {
+ height: 4rem
+}
+
+.h-2 {
+ height: .5rem
+}
+
+.h-2\.5 {
+ height: .625rem
+}
+
+.h-20 {
+ height: 5rem
+}
+
+.h-24 {
+ height: 6rem
+}
+
+.h-3 {
+ height: .75rem
+}
+
+.h-3\.5 {
+ height: .875rem
+}
+
+.h-32 {
+ height: 8rem
+}
+
+.h-4 {
+ height: 1rem
+}
+
+.h-40 {
+ height: 10rem
+}
+
+.h-44 {
+ height: 11rem
+}
+
+.h-5 {
+ height: 1.25rem
+}
+
+.h-5\/6 {
+ height: 83.333333%
+}
+
+.h-6 {
+ height: 1.5rem
+}
+
+.h-60 {
+ height: 15rem
+}
+
+.h-64 {
+ height: 16rem
+}
+
+.h-7 {
+ height: 1.75rem
+}
+
+.h-8 {
+ height: 2rem
+}
+
+.h-80 {
+ height: 20rem
+}
+
+.h-9 {
+ height: 2.25rem
+}
+
+.h-\[--calendar-offset-top\] {
+ height: var(--calendar-offset-top)
+}
+
+.h-\[100vh\] {
+ height: 100vh
+}
+
+.h-\[10px\] {
+ height: 10px
+}
+
+.h-\[110px\] {
+ height: 110px
+}
+
+.h-\[14px\] {
+ height: 14px
+}
+
+.h-\[16px\] {
+ height: 16px
+}
+
+.h-\[1px\] {
+ height: 1px
+}
+
+.h-\[200px\] {
+ height: 200px
+}
+
+.h-\[20px\] {
+ height: 20px
+}
+
+.h-\[212px\] {
+ height: 212px
+}
+
+.h-\[26px\] {
+ height: 26px
+}
+
+.h-\[28px\] {
+ height: 28px
+}
+
+.h-\[34px\] {
+ height: 34px
+}
+
+.h-\[36px\] {
+ height: 36px
+}
+
+.h-\[38px\] {
+ height: 38px
+}
+
+.h-\[400px\] {
+ height: 400px
+}
+
+.h-\[40px\] {
+ height: 40px
+}
+
+.h-\[414px\] {
+ height: 414px
+}
+
+.h-\[54px\] {
+ height: 54px
+}
+
+.h-\[55vh\] {
+ height: 55vh
+}
+
+.h-\[5px\] {
+ height: 5px
+}
+
+.h-\[70px\] {
+ height: 70px
+}
+
+.h-\[72px\] {
+ height: 72px
+}
+
+.h-\[90px\] {
+ height: 90px
+}
+
+.h-\[90vh\] {
+ height: 90vh
+}
+
+.h-\[calc\(100\%-50px\)\] {
+ height: calc(100% - 50px)
+}
+
+.h-\[calc\(100vh-2rem\)\] {
+ height: calc(100vh - 2rem)
+}
+
+.h-auto {
+ height: auto
+}
+
+.h-fit {
+ height: -moz-fit-content;
+ height: fit-content
+}
+
+.h-full {
+ height: 100%
+}
+
+.h-min {
+ height: -moz-min-content;
+ height: min-content
+}
+
+.h-px {
+ height: 1px
+}
+
+.h-screen {
+ height: 100vh
+}
+
+.max-h-20 {
+ max-height: 5rem
+}
+
+.max-h-24 {
+ max-height: 6rem
+}
+
+.max-h-4 {
+ max-height: 1rem
+}
+
+.max-h-40 {
+ max-height: 10rem
+}
+
+.max-h-48 {
+ max-height: 12rem
+}
+
+.max-h-6 {
+ max-height: 1.5rem
+}
+
+.max-h-64 {
+ max-height: 16rem
+}
+
+.max-h-72 {
+ max-height: 18rem
+}
+
+.max-h-80 {
+ max-height: 20rem
+}
+
+.max-h-9 {
+ max-height: 2.25rem
+}
+
+.max-h-\[180px\] {
+ max-height: 180px
+}
+
+.max-h-\[300px\] {
+ max-height: 300px
+}
+
+.max-h-\[390px\] {
+ max-height: 390px
+}
+
+.max-h-\[400px\] {
+ max-height: 400px
+}
+
+.max-h-\[45vh\] {
+ max-height: 45vh
+}
+
+.max-h-\[500px\] {
+ max-height: 500px
+}
+
+.max-h-\[80vh\] {
+ max-height: 80vh
+}
+
+.max-h-\[85vh\] {
+ max-height: 85vh
+}
+
+.max-h-\[95vh\] {
+ max-height: 95vh
+}
+
+.max-h-\[calc\(100vh-2rem\)\] {
+ max-height: calc(100vh - 2rem)
+}
+
+.max-h-fit {
+ max-height: -moz-fit-content;
+ max-height: fit-content
+}
+
+.max-h-full {
+ max-height: 100%
+}
+
+.max-h-none {
+ max-height: none
+}
+
+.max-h-screen {
+ max-height: 100vh
+}
+
+.\!min-h-0 {
+ min-height: 0!important
+}
+
+.\!min-h-9 {
+ min-height: 2.25rem!important
+}
+
+.\!min-h-\[36px\] {
+ min-height: 36px!important
+}
+
+.min-h-10 {
+ min-height: 2.5rem
+}
+
+.min-h-16 {
+ min-height: 4rem
+}
+
+.min-h-24 {
+ min-height: 6rem
+}
+
+.min-h-3 {
+ min-height: .75rem
+}
+
+.min-h-3\.5 {
+ min-height: .875rem
+}
+
+.min-h-36 {
+ min-height: 9rem
+}
+
+.min-h-4 {
+ min-height: 1rem
+}
+
+.min-h-48 {
+ min-height: 12rem
+}
+
+.min-h-5 {
+ min-height: 1.25rem
+}
+
+.min-h-52 {
+ min-height: 13rem
+}
+
+.min-h-6 {
+ min-height: 1.5rem
+}
+
+.min-h-8 {
+ min-height: 2rem
+}
+
+.min-h-80 {
+ min-height: 20rem
+}
+
+.min-h-9 {
+ min-height: 2.25rem
+}
+
+.min-h-\[100dvh\] {
+ min-height: 100dvh
+}
+
+.min-h-\[180px\] {
+ min-height: 180px
+}
+
+.min-h-\[200px\] {
+ min-height: 200px
+}
+
+.min-h-\[295px\] {
+ min-height: 295px
+}
+
+.min-h-\[315px\] {
+ min-height: 315px
+}
+
+.min-h-\[36px\] {
+ min-height: 36px
+}
+
+.min-h-\[450px\] {
+ min-height: 450px
+}
+
+.min-h-\[calc\(100dvh\)\] {
+ min-height: calc(100dvh)
+}
+
+.min-h-full {
+ min-height: 100%
+}
+
+.min-h-screen {
+ min-height: 100vh
+}
+
+.\!w-32 {
+ width: 8rem!important
+}
+
+.\!w-48 {
+ width: 12rem!important
+}
+
+.\!w-64 {
+ width: 16rem!important
+}
+
+.\!w-auto {
+ width: auto!important
+}
+
+.w-0 {
+ width: 0
+}
+
+.w-1 {
+ width: .25rem
+}
+
+.w-1\/12 {
+ width: 8.333333%
+}
+
+.w-1\/2 {
+ width: 50%
+}
+
+.w-1\/3 {
+ width: 33.333333%
+}
+
+.w-1\/4 {
+ width: 25%
+}
+
+.w-1\/6 {
+ width: 16.666667%
+}
+
+.w-10 {
+ width: 2.5rem
+}
+
+.w-10\/12 {
+ width: 83.333333%
+}
+
+.w-11 {
+ width: 2.75rem
+}
+
+.w-12 {
+ width: 3rem
+}
+
+.w-14 {
+ width: 3.5rem
+}
+
+.w-16 {
+ width: 4rem
+}
+
+.w-2 {
+ width: .5rem
+}
+
+.w-2\.5 {
+ width: .625rem
+}
+
+.w-2\/3 {
+ width: 66.666667%
+}
+
+.w-20 {
+ width: 5rem
+}
+
+.w-24 {
+ width: 6rem
+}
+
+.w-28 {
+ width: 7rem
+}
+
+.w-3 {
+ width: .75rem
+}
+
+.w-3\.5 {
+ width: .875rem
+}
+
+.w-3\/4 {
+ width: 75%
+}
+
+.w-32 {
+ width: 8rem
+}
+
+.w-36 {
+ width: 9rem
+}
+
+.w-4 {
+ width: 1rem
+}
+
+.w-4\/5 {
+ width: 80%
+}
+
+.w-40 {
+ width: 10rem
+}
+
+.w-44 {
+ width: 11rem
+}
+
+.w-48 {
+ width: 12rem
+}
+
+.w-5 {
+ width: 1.25rem
+}
+
+.w-5\/6 {
+ width: 83.333333%
+}
+
+.w-52 {
+ width: 13rem
+}
+
+.w-56 {
+ width: 14rem
+}
+
+.w-6 {
+ width: 1.5rem
+}
+
+.w-60 {
+ width: 15rem
+}
+
+.w-64 {
+ width: 16rem
+}
+
+.w-7 {
+ width: 1.75rem
+}
+
+.w-72 {
+ width: 18rem
+}
+
+.w-8 {
+ width: 2rem
+}
+
+.w-80 {
+ width: 20rem
+}
+
+.w-9 {
+ width: 2.25rem
+}
+
+.w-\[100px\] {
+ width: 100px
+}
+
+.w-\[106px\] {
+ width: 106px
+}
+
+.w-\[120px\] {
+ width: 120px
+}
+
+.w-\[130px\] {
+ width: 130px
+}
+
+.w-\[14px\] {
+ width: 14px
+}
+
+.w-\[16px\] {
+ width: 16px
+}
+
+.w-\[20\%\] {
+ width: 20%
+}
+
+.w-\[200px\] {
+ width: 200px
+}
+
+.w-\[20px\] {
+ width: 20px
+}
+
+.w-\[276px\] {
+ width: 276px
+}
+
+.w-\[30rem\] {
+ width: 30rem
+}
+
+.w-\[34px\] {
+ width: 34px
+}
+
+.w-\[36px\] {
+ width: 36px
+}
+
+.w-\[42px\] {
+ width: 42px
+}
+
+.w-\[46px\] {
+ width: 46px
+}
+
+.w-\[49px\] {
+ width: 49px
+}
+
+.w-\[5px\] {
+ width: 5px
+}
+
+.w-\[70px\] {
+ width: 70px
+}
+
+.w-\[72px\] {
+ width: 72px
+}
+
+.w-\[90px\] {
+ width: 90px
+}
+
+.w-\[calc\(100\%\+16px\)\] {
+ width: calc(100% + 16px)
+}
+
+.w-\[calc\(100\%-1px\)\] {
+ width: calc(100% - 1px)
+}
+
+.w-\[calc\(100\%-2rem\)\] {
+ width: calc(100% - 2rem)
+}
+
+.w-\[var\(--booker-timeslots-width\)\] {
+ width: var(--booker-timeslots-width)
+}
+
+.w-auto {
+ width: auto
+}
+
+.w-fit {
+ width: -moz-fit-content;
+ width: fit-content
+}
+
+.w-full {
+ width: 100%
+}
+
+.w-max {
+ width: -moz-max-content;
+ width: max-content
+}
+
+.w-px {
+ width: 1px
+}
+
+.w-screen {
+ width: 100vw
+}
+
+.min-w-0 {
+ min-width: 0
+}
+
+.min-w-10 {
+ min-width: 2.5rem
+}
+
+.min-w-16 {
+ min-width: 4rem
+}
+
+.min-w-24 {
+ min-width: 6rem
+}
+
+.min-w-3 {
+ min-width: .75rem
+}
+
+.min-w-3\.5 {
+ min-width: .875rem
+}
+
+.min-w-32 {
+ min-width: 8rem
+}
+
+.min-w-4 {
+ min-width: 1rem
+}
+
+.min-w-40 {
+ min-width: 10rem
+}
+
+.min-w-48 {
+ min-width: 12rem
+}
+
+.min-w-5 {
+ min-width: 1.25rem
+}
+
+.min-w-56 {
+ min-width: 14rem
+}
+
+.min-w-6 {
+ min-width: 1.5rem
+}
+
+.min-w-60 {
+ min-width: 15rem
+}
+
+.min-w-8 {
+ min-width: 2rem
+}
+
+.min-w-80 {
+ min-width: 20rem
+}
+
+.min-w-9 {
+ min-width: 2.25rem
+}
+
+.min-w-\[10rem\] {
+ min-width: 10rem
+}
+
+.min-w-\[150px\] {
+ min-width: 150px
+}
+
+.min-w-\[34px\] {
+ min-width: 34px
+}
+
+.min-w-\[36px\] {
+ min-width: 36px
+}
+
+.min-w-\[40px\] {
+ min-width: 40px
+}
+
+.min-w-\[42px\] {
+ min-width: 42px
+}
+
+.min-w-\[88px\] {
+ min-width: 88px
+}
+
+.min-w-\[90\%\] {
+ min-width: 90%
+}
+
+.min-w-fit {
+ min-width: -moz-fit-content;
+ min-width: fit-content
+}
+
+.min-w-full {
+ min-width: 100%
+}
+
+.min-w-max {
+ min-width: -moz-max-content;
+ min-width: max-content
+}
+
+.min-w-min {
+ min-width: -moz-min-content;
+ min-width: min-content
+}
+
+.max-w-28 {
+ max-width: 7rem
+}
+
+.max-w-2xl {
+ max-width: 42rem
+}
+
+.max-w-36 {
+ max-width: 9rem
+}
+
+.max-w-3xl {
+ max-width: 48rem
+}
+
+.max-w-44 {
+ max-width: 11rem
+}
+
+.max-w-4xl {
+ max-width: 56rem
+}
+
+.max-w-52 {
+ max-width: 13rem
+}
+
+.max-w-56 {
+ max-width: 14rem
+}
+
+.max-w-5xl {
+ max-width: 64rem
+}
+
+.max-w-60 {
+ max-width: 15rem
+}
+
+.max-w-64 {
+ max-width: 16rem
+}
+
+.max-w-7xl {
+ max-width: 80rem
+}
+
+.max-w-80 {
+ max-width: 20rem
+}
+
+.max-w-96 {
+ max-width: 24rem
+}
+
+.max-w-\[1440px\] {
+ max-width: 1440px
+}
+
+.max-w-\[2000px\] {
+ max-width: 2000px
+}
+
+.max-w-\[22rem\] {
+ max-width: 22rem
+}
+
+.max-w-\[250px\] {
+ max-width: 250px
+}
+
+.max-w-\[280px\] {
+ max-width: 280px
+}
+
+.max-w-\[420px\] {
+ max-width: 420px
+}
+
+.max-w-\[43em\] {
+ max-width: 43em
+}
+
+.max-w-\[500px\] {
+ max-width: 500px
+}
+
+.max-w-\[50ch\] {
+ max-width: 50ch
+}
+
+.max-w-\[600px\] {
+ max-width: 600px
+}
+
+.max-w-\[90\%\] {
+ max-width: 90%
+}
+
+.max-w-\[90vw\] {
+ max-width: 90vw
+}
+
+.max-w-full {
+ max-width: 100%
+}
+
+.max-w-md {
+ max-width: 28rem
+}
+
+.max-w-none {
+ max-width: none
+}
+
+.max-w-screen {
+ max-width: 100vw
+}
+
+.max-w-screen-lg {
+ max-width: 1024px
+}
+
+.max-w-screen-sm {
+ max-width: 640px
+}
+
+.max-w-sm {
+ max-width: 24rem
+}
+
+.max-w-xl {
+ max-width: 36rem
+}
+
+.max-w-xs {
+ max-width: 20rem
+}
+
+.flex-1 {
+ flex: 1 1 0%
+}
+
+.flex-auto {
+ flex: 1 1 auto
+}
+
+.flex-none {
+ flex: none
+}
+
+.flex-shrink {
+ flex-shrink: 1
+}
+
+.flex-shrink-0 {
+ flex-shrink: 0
+}
+
+.shrink {
+ flex-shrink: 1
+}
+
+.shrink-0 {
+ flex-shrink: 0
+}
+
+.flex-grow {
+ flex-grow: 1
+}
+
+.flex-grow-0 {
+ flex-grow: 0
+}
+
+.grow {
+ flex-grow: 1
+}
+
+.basis-1\/5 {
+ flex-basis: 20%
+}
+
+.basis-2\/5 {
+ flex-basis: 40%
+}
+
+.basis-3\/5 {
+ flex-basis: 60%
+}
+
+.table-fixed {
+ table-layout: fixed
+}
+
+.caption-bottom {
+ caption-side: bottom
+}
+
+.border-collapse {
+ border-collapse: collapse
+}
+
+.border-separate {
+ border-collapse: separate
+}
+
+.border-spacing-0 {
+ --tw-border-spacing-x: 0px;
+ --tw-border-spacing-y: 0px;
+ border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y)
+}
+
+.origin-center {
+ transform-origin: center
+}
+
+.origin-top-right {
+ transform-origin: top right
+}
+
+.-translate-x-0 {
+ --tw-translate-x: -0px
+}
+
+.-translate-x-0,.-translate-x-0\.5 {
+ transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
+}
+
+.-translate-x-0\.5 {
+ --tw-translate-x: -0.125rem
+}
+
+.-translate-x-1\/2 {
+ --tw-translate-x: -50%
+}
+
+.-translate-x-1\/2,.-translate-x-\[228px\] {
+ transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
+}
+
+.-translate-x-\[228px\] {
+ --tw-translate-x: -228px
+}
+
+.-translate-x-\[232px\] {
+ --tw-translate-x: -232px
+}
+
+.-translate-x-\[232px\],.-translate-x-full {
+ transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
+}
+
+.-translate-x-full {
+ --tw-translate-x: -100%
+}
+
+.-translate-y-1\/2 {
+ --tw-translate-y: -50%
+}
+
+.-translate-y-1\/2,.translate-x-0 {
+ transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
+}
+
+.translate-x-0 {
+ --tw-translate-x: 0px
+}
+
+.translate-x-1\/2 {
+ --tw-translate-x: 50%
+}
+
+.translate-x-1\/2,.translate-x-full {
+ transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
+}
+
+.translate-x-full {
+ --tw-translate-x: 100%
+}
+
+.translate-y-\[2px\] {
+ --tw-translate-y: 2px
+}
+
+.translate-y-\[2px\],.translate-y-\[8px\] {
+ transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
+}
+
+.translate-y-\[8px\] {
+ --tw-translate-y: 8px
+}
+
+.-rotate-180 {
+ --tw-rotate: -180deg
+}
+
+.-rotate-180,.rotate-180 {
+ transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
+}
+
+.rotate-180 {
+ --tw-rotate: 180deg
+}
+
+.rotate-45 {
+ --tw-rotate: 45deg
+}
+
+.rotate-45,.scale-0 {
+ transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
+}
+
+.scale-0 {
+ --tw-scale-x: 0;
+ --tw-scale-y: 0
+}
+
+.scale-100 {
+ --tw-scale-x: 1;
+ --tw-scale-y: 1
+}
+
+.scale-100,.transform {
+ transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
+}
+
+@keyframes fade-in-up {
+ 0% {
+ opacity: 0;
+ transform: translateY(10px)
+ }
+
+ to {
+ opacity: 1;
+ transform: none
+ }
+}
+
+.animate-fade-in-up {
+ animation: fade-in-up .6s var(--animation-delay,0ms) cubic-bezier(.21,1.02,.73,1) forwards
+}
+
+.animate-pulse {
+ animation: pulse 2s cubic-bezier(.4,0,.6,1) infinite
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(1turn)
+ }
+}
+
+.animate-spin {
+ animation: spin 1s linear infinite
+}
+
+@keyframes spinning {
+ to {
+ transform: rotate(1turn)
+ }
+}
+
+.animate-spinning {
+ animation: spinning .75s linear infinite
+}
+
+.\!cursor-pointer {
+ cursor: pointer!important
+}
+
+.cursor-default {
+ cursor: default
+}
+
+.cursor-not-allowed {
+ cursor: not-allowed
+}
+
+.cursor-pointer {
+ cursor: pointer
+}
+
+.cursor-text {
+ cursor: text
+}
+
+.cursor-wait {
+ cursor: wait
+}
+
+.select-none {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none
+}
+
+.resize {
+ resize: both
+}
+
+.snap-x {
+ scroll-snap-type: x var(--tw-scroll-snap-strictness)
+}
+
+.snap-mandatory {
+ --tw-scroll-snap-strictness: mandatory
+}
+
+.snap-center {
+ scroll-snap-align: center
+}
+
+.list-decimal {
+ list-style-type: decimal
+}
+
+.list-disc {
+ list-style-type: disc
+}
+
+.appearance-none {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none
+}
+
+.auto-cols-auto {
+ grid-auto-columns: auto
+}
+
+.auto-cols-fr {
+ grid-auto-columns: minmax(0,1fr)
+}
+
+.grid-cols-1 {
+ grid-template-columns: repeat(1,minmax(0,1fr))
+}
+
+.grid-cols-10 {
+ grid-template-columns: repeat(10,minmax(0,1fr))
+}
+
+.grid-cols-11 {
+ grid-template-columns: repeat(11,minmax(0,1fr))
+}
+
+.grid-cols-12 {
+ grid-template-columns: repeat(12,minmax(0,1fr))
+}
+
+.grid-cols-2 {
+ grid-template-columns: repeat(2,minmax(0,1fr))
+}
+
+.grid-cols-3 {
+ grid-template-columns: repeat(3,minmax(0,1fr))
+}
+
+.grid-cols-4 {
+ grid-template-columns: repeat(4,minmax(0,1fr))
+}
+
+.grid-cols-5 {
+ grid-template-columns: repeat(5,minmax(0,1fr))
+}
+
+.grid-cols-6 {
+ grid-template-columns: repeat(6,minmax(0,1fr))
+}
+
+.grid-cols-7 {
+ grid-template-columns: repeat(7,minmax(0,1fr))
+}
+
+.grid-cols-8 {
+ grid-template-columns: repeat(8,minmax(0,1fr))
+}
+
+.grid-cols-9 {
+ grid-template-columns: repeat(9,minmax(0,1fr))
+}
+
+.grid-cols-none {
+ grid-template-columns: none
+}
+
+.grid-rows-1 {
+ grid-template-rows: repeat(1,minmax(0,1fr))
+}
+
+.grid-rows-2 {
+ grid-template-rows: repeat(2,minmax(0,1fr))
+}
+
+.flex-row {
+ flex-direction: row
+}
+
+.flex-row-reverse {
+ flex-direction: row-reverse
+}
+
+.flex-col {
+ flex-direction: column
+}
+
+.flex-col-reverse {
+ flex-direction: column-reverse
+}
+
+.flex-wrap {
+ flex-wrap: wrap
+}
+
+.flex-nowrap {
+ flex-wrap: nowrap
+}
+
+.content-center {
+ align-content: center
+}
+
+.items-start {
+ align-items: flex-start
+}
+
+.items-end {
+ align-items: flex-end
+}
+
+.items-center {
+ align-items: center
+}
+
+.items-baseline {
+ align-items: baseline
+}
+
+.items-stretch {
+ align-items: stretch
+}
+
+.justify-start {
+ justify-content: flex-start
+}
+
+.justify-end {
+ justify-content: flex-end
+}
+
+.justify-center {
+ justify-content: center
+}
+
+.justify-between {
+ justify-content: space-between
+}
+
+.justify-around {
+ justify-content: space-around
+}
+
+.justify-evenly {
+ justify-content: space-evenly
+}
+
+.justify-items-center {
+ justify-items: center
+}
+
+.gap-0 {
+ gap: 0
+}
+
+.gap-0\.5 {
+ gap: .125rem
+}
+
+.gap-1 {
+ gap: .25rem;
+}
+
+.gap-2 {
+ gap: .5rem
+}
+
+.gap-3 {
+ gap: .75rem
+}
+
+.gap-4 {
+ gap: 1rem
+}
+
+.gap-5 {
+ gap: 1.25rem
+}
+
+.gap-6 {
+ gap: 1.5rem
+}
+
+.gap-8 {
+ gap: 2rem
+}
+
+.gap-x-1 {
+ -moz-column-gap: .25rem;
+ column-gap: .25rem
+}
+
+.gap-x-2 {
+ -moz-column-gap: .5rem;
+ column-gap: .5rem
+}
+
+.gap-x-3 {
+ -moz-column-gap: .75rem;
+ column-gap: .75rem
+}
+
+.gap-x-4 {
+ -moz-column-gap: 1rem;
+ column-gap: 1rem
+}
+
+.gap-x-6 {
+ -moz-column-gap: 1.5rem;
+ column-gap: 1.5rem
+}
+
+.gap-y-1 {
+ row-gap: .25rem
+}
+
+.gap-y-2 {
+ row-gap: .5rem
+}
+
+.gap-y-3 {
+ row-gap: .75rem
+}
+
+.gap-y-4 {
+ row-gap: 1rem
+}
+
+.gap-y-6 {
+ row-gap: 1.5rem
+}
+
+.gap-y-8 {
+ row-gap: 2rem
+}
+
+.space-x-0>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(0px * var(--tw-space-x-reverse));
+ margin-left: calc(0px * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-1>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(.25rem * var(--tw-space-x-reverse));
+ margin-left: calc(.25rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-11>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(2.75rem * var(--tw-space-x-reverse));
+ margin-left: calc(2.75rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-2>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(.5rem * var(--tw-space-x-reverse));
+ margin-left: calc(.5rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-2\.5>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(.625rem * var(--tw-space-x-reverse));
+ margin-left: calc(.625rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-3>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(.75rem * var(--tw-space-x-reverse));
+ margin-left: calc(.75rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-3\.5>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(.875rem * var(--tw-space-x-reverse));
+ margin-left: calc(.875rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-4>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(1rem * var(--tw-space-x-reverse));
+ margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-5>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(1.25rem * var(--tw-space-x-reverse));
+ margin-left: calc(1.25rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-6>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(1.5rem * var(--tw-space-x-reverse));
+ margin-left: calc(1.5rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-8>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(2rem * var(--tw-space-x-reverse));
+ margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-x-px>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(1px * var(--tw-space-x-reverse));
+ margin-left: calc(1px * calc(1 - var(--tw-space-x-reverse)))
+}
+
+.space-y-0>:not([hidden])~:not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(0px * var(--tw-space-y-reverse))
+}
+
+.space-y-0\.5>:not([hidden])~:not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(.125rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(.125rem * var(--tw-space-y-reverse))
+}
+
+.space-y-1>:not([hidden])~:not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(.25rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(.25rem * var(--tw-space-y-reverse))
+}
+
+.space-y-2>:not([hidden])~:not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(.5rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(.5rem * var(--tw-space-y-reverse))
+}
+
+.space-y-3>:not([hidden])~:not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(.75rem * var(--tw-space-y-reverse))
+}
+
+.space-y-4>:not([hidden])~:not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(1rem * var(--tw-space-y-reverse))
+}
+
+.space-y-5>:not([hidden])~:not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(1.25rem * var(--tw-space-y-reverse))
+}
+
+.space-y-6>:not([hidden])~:not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(1.5rem * var(--tw-space-y-reverse))
+}
+
+.space-y-reverse>:not([hidden])~:not([hidden]) {
+ --tw-space-y-reverse: 1
+}
+
+.space-x-reverse>:not([hidden])~:not([hidden]) {
+ --tw-space-x-reverse: 1
+}
+
+.divide-x>:not([hidden])~:not([hidden]) {
+ --tw-divide-x-reverse: 0;
+ border-right-width: calc(1px * var(--tw-divide-x-reverse));
+ border-left-width: calc(1px * calc(1 - var(--tw-divide-x-reverse)))
+}
+
+.divide-y>:not([hidden])~:not([hidden]) {
+ --tw-divide-y-reverse: 0;
+ border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
+ border-bottom-width: calc(1px * var(--tw-divide-y-reverse))
+}
+
+.divide-y-2>:not([hidden])~:not([hidden]) {
+ --tw-divide-y-reverse: 0;
+ border-top-width: calc(2px * calc(1 - var(--tw-divide-y-reverse)));
+ border-bottom-width: calc(2px * var(--tw-divide-y-reverse))
+}
+
+.divide-y-reverse>:not([hidden])~:not([hidden]) {
+ --tw-divide-y-reverse: 1
+}
+
+.divide-x-reverse>:not([hidden])~:not([hidden]) {
+ --tw-divide-x-reverse: 1
+}
+
+.divide-amber-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(254 243 199/var(--tw-divide-opacity))
+}
+
+.divide-amber-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(253 230 138/var(--tw-divide-opacity))
+}
+
+.divide-amber-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(252 211 77/var(--tw-divide-opacity))
+}
+
+.divide-amber-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(251 191 36/var(--tw-divide-opacity))
+}
+
+.divide-amber-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(255 251 235/var(--tw-divide-opacity))
+}
+
+.divide-amber-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(245 158 11/var(--tw-divide-opacity))
+}
+
+.divide-amber-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(217 119 6/var(--tw-divide-opacity))
+}
+
+.divide-amber-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(180 83 9/var(--tw-divide-opacity))
+}
+
+.divide-amber-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(146 64 14/var(--tw-divide-opacity))
+}
+
+.divide-amber-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(120 53 15/var(--tw-divide-opacity))
+}
+
+.divide-black>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(17 17 17/var(--tw-divide-opacity))
+}
+
+.divide-blue-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(219 234 254/var(--tw-divide-opacity))
+}
+
+.divide-blue-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(191 219 254/var(--tw-divide-opacity))
+}
+
+.divide-blue-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(147 197 253/var(--tw-divide-opacity))
+}
+
+.divide-blue-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(96 165 250/var(--tw-divide-opacity))
+}
+
+.divide-blue-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(239 246 255/var(--tw-divide-opacity))
+}
+
+.divide-blue-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(59 130 246/var(--tw-divide-opacity))
+}
+
+.divide-blue-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(37 99 235/var(--tw-divide-opacity))
+}
+
+.divide-blue-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(29 78 216/var(--tw-divide-opacity))
+}
+
+.divide-blue-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(30 64 175/var(--tw-divide-opacity))
+}
+
+.divide-blue-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(30 58 138/var(--tw-divide-opacity))
+}
+
+.divide-cyan-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(207 250 254/var(--tw-divide-opacity))
+}
+
+.divide-cyan-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(165 243 252/var(--tw-divide-opacity))
+}
+
+.divide-cyan-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(103 232 249/var(--tw-divide-opacity))
+}
+
+.divide-cyan-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(34 211 238/var(--tw-divide-opacity))
+}
+
+.divide-cyan-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(236 254 255/var(--tw-divide-opacity))
+}
+
+.divide-cyan-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(6 182 212/var(--tw-divide-opacity))
+}
+
+.divide-cyan-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(8 145 178/var(--tw-divide-opacity))
+}
+
+.divide-cyan-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(14 116 144/var(--tw-divide-opacity))
+}
+
+.divide-cyan-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(21 94 117/var(--tw-divide-opacity))
+}
+
+.divide-cyan-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(22 78 99/var(--tw-divide-opacity))
+}
+
+.divide-default>:not([hidden])~:not([hidden]) {
+ border-color: var(--cal-border,#d1d5db)
+}
+
+.divide-emerald-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(209 250 229/var(--tw-divide-opacity))
+}
+
+.divide-emerald-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(167 243 208/var(--tw-divide-opacity))
+}
+
+.divide-emerald-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(110 231 183/var(--tw-divide-opacity))
+}
+
+.divide-emerald-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(52 211 153/var(--tw-divide-opacity))
+}
+
+.divide-emerald-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(236 253 245/var(--tw-divide-opacity))
+}
+
+.divide-emerald-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(16 185 129/var(--tw-divide-opacity))
+}
+
+.divide-emerald-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(5 150 105/var(--tw-divide-opacity))
+}
+
+.divide-emerald-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(4 120 87/var(--tw-divide-opacity))
+}
+
+.divide-emerald-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(6 95 70/var(--tw-divide-opacity))
+}
+
+.divide-emerald-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(6 78 59/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(250 232 255/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(245 208 254/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(240 171 252/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(232 121 249/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(253 244 255/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(217 70 239/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(192 38 211/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(162 28 175/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(134 25 143/var(--tw-divide-opacity))
+}
+
+.divide-fuchsia-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(112 26 117/var(--tw-divide-opacity))
+}
+
+.divide-gray-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(243 244 246/var(--tw-divide-opacity))
+}
+
+.divide-gray-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(229 231 235/var(--tw-divide-opacity))
+}
+
+.divide-gray-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(209 213 219/var(--tw-divide-opacity))
+}
+
+.divide-gray-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(156 163 175/var(--tw-divide-opacity))
+}
+
+.divide-gray-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(249 250 251/var(--tw-divide-opacity))
+}
+
+.divide-gray-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(107 114 128/var(--tw-divide-opacity))
+}
+
+.divide-gray-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(75 85 99/var(--tw-divide-opacity))
+}
+
+.divide-gray-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(55 65 81/var(--tw-divide-opacity))
+}
+
+.divide-gray-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(31 41 55/var(--tw-divide-opacity))
+}
+
+.divide-gray-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(17 24 39/var(--tw-divide-opacity))
+}
+
+.divide-green-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(220 252 231/var(--tw-divide-opacity))
+}
+
+.divide-green-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(187 247 208/var(--tw-divide-opacity))
+}
+
+.divide-green-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(134 239 172/var(--tw-divide-opacity))
+}
+
+.divide-green-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(74 222 128/var(--tw-divide-opacity))
+}
+
+.divide-green-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(240 253 244/var(--tw-divide-opacity))
+}
+
+.divide-green-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(34 197 94/var(--tw-divide-opacity))
+}
+
+.divide-green-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(22 163 74/var(--tw-divide-opacity))
+}
+
+.divide-green-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(21 128 61/var(--tw-divide-opacity))
+}
+
+.divide-green-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(22 101 52/var(--tw-divide-opacity))
+}
+
+.divide-green-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(20 83 45/var(--tw-divide-opacity))
+}
+
+.divide-indigo-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(224 231 255/var(--tw-divide-opacity))
+}
+
+.divide-indigo-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(199 210 254/var(--tw-divide-opacity))
+}
+
+.divide-indigo-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(165 180 252/var(--tw-divide-opacity))
+}
+
+.divide-indigo-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(129 140 248/var(--tw-divide-opacity))
+}
+
+.divide-indigo-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(238 242 255/var(--tw-divide-opacity))
+}
+
+.divide-indigo-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(99 102 241/var(--tw-divide-opacity))
+}
+
+.divide-indigo-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(79 70 229/var(--tw-divide-opacity))
+}
+
+.divide-indigo-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(67 56 202/var(--tw-divide-opacity))
+}
+
+.divide-indigo-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(55 48 163/var(--tw-divide-opacity))
+}
+
+.divide-indigo-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(49 46 129/var(--tw-divide-opacity))
+}
+
+.divide-lime-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(236 252 203/var(--tw-divide-opacity))
+}
+
+.divide-lime-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(217 249 157/var(--tw-divide-opacity))
+}
+
+.divide-lime-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(190 242 100/var(--tw-divide-opacity))
+}
+
+.divide-lime-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(163 230 53/var(--tw-divide-opacity))
+}
+
+.divide-lime-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(247 254 231/var(--tw-divide-opacity))
+}
+
+.divide-lime-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(132 204 22/var(--tw-divide-opacity))
+}
+
+.divide-lime-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(101 163 13/var(--tw-divide-opacity))
+}
+
+.divide-lime-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(77 124 15/var(--tw-divide-opacity))
+}
+
+.divide-lime-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(63 98 18/var(--tw-divide-opacity))
+}
+
+.divide-lime-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(54 83 20/var(--tw-divide-opacity))
+}
+
+.divide-neutral-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(245 245 245/var(--tw-divide-opacity))
+}
+
+.divide-neutral-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(229 229 229/var(--tw-divide-opacity))
+}
+
+.divide-neutral-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(212 212 212/var(--tw-divide-opacity))
+}
+
+.divide-neutral-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(163 163 163/var(--tw-divide-opacity))
+}
+
+.divide-neutral-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(250 250 250/var(--tw-divide-opacity))
+}
+
+.divide-neutral-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(115 115 115/var(--tw-divide-opacity))
+}
+
+.divide-neutral-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(82 82 82/var(--tw-divide-opacity))
+}
+
+.divide-neutral-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(64 64 64/var(--tw-divide-opacity))
+}
+
+.divide-neutral-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(38 38 38/var(--tw-divide-opacity))
+}
+
+.divide-neutral-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(23 23 23/var(--tw-divide-opacity))
+}
+
+.divide-orange-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(255 237 213/var(--tw-divide-opacity))
+}
+
+.divide-orange-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(254 215 170/var(--tw-divide-opacity))
+}
+
+.divide-orange-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(253 186 116/var(--tw-divide-opacity))
+}
+
+.divide-orange-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(251 146 60/var(--tw-divide-opacity))
+}
+
+.divide-orange-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(255 247 237/var(--tw-divide-opacity))
+}
+
+.divide-orange-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(249 115 22/var(--tw-divide-opacity))
+}
+
+.divide-orange-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(234 88 12/var(--tw-divide-opacity))
+}
+
+.divide-orange-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(194 65 12/var(--tw-divide-opacity))
+}
+
+.divide-orange-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(154 52 18/var(--tw-divide-opacity))
+}
+
+.divide-orange-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(124 45 18/var(--tw-divide-opacity))
+}
+
+.divide-pink-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(252 231 243/var(--tw-divide-opacity))
+}
+
+.divide-pink-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(251 207 232/var(--tw-divide-opacity))
+}
+
+.divide-pink-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(249 168 212/var(--tw-divide-opacity))
+}
+
+.divide-pink-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(244 114 182/var(--tw-divide-opacity))
+}
+
+.divide-pink-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(253 242 248/var(--tw-divide-opacity))
+}
+
+.divide-pink-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(236 72 153/var(--tw-divide-opacity))
+}
+
+.divide-pink-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(219 39 119/var(--tw-divide-opacity))
+}
+
+.divide-pink-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(190 24 93/var(--tw-divide-opacity))
+}
+
+.divide-pink-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(131 24 67/var(--tw-divide-opacity))
+}
+
+.divide-purple-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(243 232 255/var(--tw-divide-opacity))
+}
+
+.divide-purple-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(233 213 255/var(--tw-divide-opacity))
+}
+
+.divide-purple-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(216 180 254/var(--tw-divide-opacity))
+}
+
+.divide-purple-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(192 132 252/var(--tw-divide-opacity))
+}
+
+.divide-purple-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(250 245 255/var(--tw-divide-opacity))
+}
+
+.divide-purple-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(168 85 247/var(--tw-divide-opacity))
+}
+
+.divide-purple-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(147 51 234/var(--tw-divide-opacity))
+}
+
+.divide-purple-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(126 34 206/var(--tw-divide-opacity))
+}
+
+.divide-purple-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(107 33 168/var(--tw-divide-opacity))
+}
+
+.divide-purple-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(88 28 135/var(--tw-divide-opacity))
+}
+
+.divide-red-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(254 226 226/var(--tw-divide-opacity))
+}
+
+.divide-red-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(254 202 202/var(--tw-divide-opacity))
+}
+
+.divide-red-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(252 165 165/var(--tw-divide-opacity))
+}
+
+.divide-red-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(248 113 113/var(--tw-divide-opacity))
+}
+
+.divide-red-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(254 242 242/var(--tw-divide-opacity))
+}
+
+.divide-red-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(239 68 68/var(--tw-divide-opacity))
+}
+
+.divide-red-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(220 38 38/var(--tw-divide-opacity))
+}
+
+.divide-red-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(185 28 28/var(--tw-divide-opacity))
+}
+
+.divide-red-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(153 27 27/var(--tw-divide-opacity))
+}
+
+.divide-red-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(127 29 29/var(--tw-divide-opacity))
+}
+
+.divide-rose-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(255 228 230/var(--tw-divide-opacity))
+}
+
+.divide-rose-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(254 205 211/var(--tw-divide-opacity))
+}
+
+.divide-rose-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(253 164 175/var(--tw-divide-opacity))
+}
+
+.divide-rose-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(251 113 133/var(--tw-divide-opacity))
+}
+
+.divide-rose-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(255 241 242/var(--tw-divide-opacity))
+}
+
+.divide-rose-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(244 63 94/var(--tw-divide-opacity))
+}
+
+.divide-rose-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(225 29 72/var(--tw-divide-opacity))
+}
+
+.divide-rose-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(190 18 60/var(--tw-divide-opacity))
+}
+
+.divide-rose-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(159 18 57/var(--tw-divide-opacity))
+}
+
+.divide-rose-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(136 19 55/var(--tw-divide-opacity))
+}
+
+.divide-sky-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(224 242 254/var(--tw-divide-opacity))
+}
+
+.divide-sky-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(186 230 253/var(--tw-divide-opacity))
+}
+
+.divide-sky-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(125 211 252/var(--tw-divide-opacity))
+}
+
+.divide-sky-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(56 189 248/var(--tw-divide-opacity))
+}
+
+.divide-sky-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(240 249 255/var(--tw-divide-opacity))
+}
+
+.divide-sky-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(14 165 233/var(--tw-divide-opacity))
+}
+
+.divide-sky-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(2 132 199/var(--tw-divide-opacity))
+}
+
+.divide-sky-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(3 105 161/var(--tw-divide-opacity))
+}
+
+.divide-sky-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(7 89 133/var(--tw-divide-opacity))
+}
+
+.divide-sky-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(12 74 110/var(--tw-divide-opacity))
+}
+
+.divide-slate-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(241 245 249/var(--tw-divide-opacity))
+}
+
+.divide-slate-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(226 232 240/var(--tw-divide-opacity))
+}
+
+.divide-slate-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(203 213 225/var(--tw-divide-opacity))
+}
+
+.divide-slate-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(148 163 184/var(--tw-divide-opacity))
+}
+
+.divide-slate-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(248 250 252/var(--tw-divide-opacity))
+}
+
+.divide-slate-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(100 116 139/var(--tw-divide-opacity))
+}
+
+.divide-slate-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(71 85 105/var(--tw-divide-opacity))
+}
+
+.divide-slate-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(51 65 85/var(--tw-divide-opacity))
+}
+
+.divide-slate-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(30 41 59/var(--tw-divide-opacity))
+}
+
+.divide-slate-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(15 23 42/var(--tw-divide-opacity))
+}
+
+.divide-stone-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(245 245 244/var(--tw-divide-opacity))
+}
+
+.divide-stone-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(231 229 228/var(--tw-divide-opacity))
+}
+
+.divide-stone-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(214 211 209/var(--tw-divide-opacity))
+}
+
+.divide-stone-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(168 162 158/var(--tw-divide-opacity))
+}
+
+.divide-stone-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(250 250 249/var(--tw-divide-opacity))
+}
+
+.divide-stone-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(120 113 108/var(--tw-divide-opacity))
+}
+
+.divide-stone-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(87 83 78/var(--tw-divide-opacity))
+}
+
+.divide-stone-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(68 64 60/var(--tw-divide-opacity))
+}
+
+.divide-stone-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(41 37 36/var(--tw-divide-opacity))
+}
+
+.divide-stone-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(28 25 23/var(--tw-divide-opacity))
+}
+
+.divide-subtle>:not([hidden])~:not([hidden]) {
+ border-color: var(--cal-border-subtle,#e5e7eb)
+}
+
+.divide-teal-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(204 251 241/var(--tw-divide-opacity))
+}
+
+.divide-teal-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(153 246 228/var(--tw-divide-opacity))
+}
+
+.divide-teal-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(94 234 212/var(--tw-divide-opacity))
+}
+
+.divide-teal-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(45 212 191/var(--tw-divide-opacity))
+}
+
+.divide-teal-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(240 253 250/var(--tw-divide-opacity))
+}
+
+.divide-teal-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(20 184 166/var(--tw-divide-opacity))
+}
+
+.divide-teal-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(13 148 136/var(--tw-divide-opacity))
+}
+
+.divide-teal-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(15 118 110/var(--tw-divide-opacity))
+}
+
+.divide-teal-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(17 94 89/var(--tw-divide-opacity))
+}
+
+.divide-teal-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(19 78 74/var(--tw-divide-opacity))
+}
+
+.divide-transparent>:not([hidden])~:not([hidden]) {
+ border-color: transparent
+}
+
+.divide-violet-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(237 233 254/var(--tw-divide-opacity))
+}
+
+.divide-violet-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(221 214 254/var(--tw-divide-opacity))
+}
+
+.divide-violet-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(196 181 253/var(--tw-divide-opacity))
+}
+
+.divide-violet-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(167 139 250/var(--tw-divide-opacity))
+}
+
+.divide-violet-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(245 243 255/var(--tw-divide-opacity))
+}
+
+.divide-violet-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(139 92 246/var(--tw-divide-opacity))
+}
+
+.divide-violet-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(124 58 237/var(--tw-divide-opacity))
+}
+
+.divide-violet-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(109 40 217/var(--tw-divide-opacity))
+}
+
+.divide-violet-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(91 33 182/var(--tw-divide-opacity))
+}
+
+.divide-violet-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(76 29 149/var(--tw-divide-opacity))
+}
+
+.divide-white>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(255 255 255/var(--tw-divide-opacity))
+}
+
+.divide-yellow-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(254 249 195/var(--tw-divide-opacity))
+}
+
+.divide-yellow-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(254 240 138/var(--tw-divide-opacity))
+}
+
+.divide-yellow-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(253 224 71/var(--tw-divide-opacity))
+}
+
+.divide-yellow-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(250 204 21/var(--tw-divide-opacity))
+}
+
+.divide-yellow-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(254 252 232/var(--tw-divide-opacity))
+}
+
+.divide-yellow-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(234 179 8/var(--tw-divide-opacity))
+}
+
+.divide-yellow-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(202 138 4/var(--tw-divide-opacity))
+}
+
+.divide-yellow-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(161 98 7/var(--tw-divide-opacity))
+}
+
+.divide-yellow-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(133 77 14/var(--tw-divide-opacity))
+}
+
+.divide-yellow-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(113 63 18/var(--tw-divide-opacity))
+}
+
+.divide-zinc-100>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(244 244 245/var(--tw-divide-opacity))
+}
+
+.divide-zinc-200>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(228 228 231/var(--tw-divide-opacity))
+}
+
+.divide-zinc-300>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(212 212 216/var(--tw-divide-opacity))
+}
+
+.divide-zinc-400>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(161 161 170/var(--tw-divide-opacity))
+}
+
+.divide-zinc-50>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(250 250 250/var(--tw-divide-opacity))
+}
+
+.divide-zinc-500>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(113 113 122/var(--tw-divide-opacity))
+}
+
+.divide-zinc-600>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(82 82 91/var(--tw-divide-opacity))
+}
+
+.divide-zinc-700>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(63 63 70/var(--tw-divide-opacity))
+}
+
+.divide-zinc-800>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(39 39 42/var(--tw-divide-opacity))
+}
+
+.divide-zinc-900>:not([hidden])~:not([hidden]) {
+ --tw-divide-opacity: 1;
+ border-color: rgb(24 24 27/var(--tw-divide-opacity))
+}
+
+.place-self-center {
+ place-self: center
+}
+
+.self-start {
+ align-self: flex-start
+}
+
+.self-end {
+ align-self: flex-end
+}
+
+.self-center {
+ align-self: center
+}
+
+.self-stretch {
+ align-self: stretch
+}
+
+.justify-self-end {
+ justify-self: end
+}
+
+.overflow-auto {
+ overflow: auto
+}
+
+.overflow-hidden {
+ overflow: hidden
+}
+
+.overflow-clip {
+ overflow: clip
+}
+
+.overflow-visible {
+ overflow: visible
+}
+
+.overflow-scroll {
+ overflow: scroll
+}
+
+.overflow-x-auto {
+ overflow-x: auto
+}
+
+.overflow-y-auto {
+ overflow-y: auto
+}
+
+.overflow-x-hidden {
+ overflow-x: hidden
+}
+
+.overflow-y-hidden {
+ overflow-y: hidden
+}
+
+.overflow-x-clip {
+ overflow-x: clip
+}
+
+.overflow-x-scroll {
+ overflow-x: scroll
+}
+
+.\!overflow-y-scroll {
+ overflow-y: scroll!important
+}
+
+.overflow-y-scroll {
+ overflow-y: scroll
+}
+
+.scroll-auto {
+ scroll-behavior: auto
+}
+
+.truncate {
+ overflow: hidden;
+ white-space: nowrap
+}
+
+.overflow-ellipsis,.text-ellipsis,.truncate {
+ text-overflow: ellipsis
+}
+
+.text-clip {
+ text-overflow: clip
+}
+
+.whitespace-normal {
+ white-space: normal
+}
+
+.whitespace-nowrap {
+ white-space: nowrap
+}
+
+.whitespace-pre {
+ white-space: pre
+}
+
+.whitespace-pre-wrap {
+ white-space: pre-wrap
+}
+
+.break-words {
+ overflow-wrap: break-word
+}
+
+.break-all {
+ word-break: break-all
+}
+
+.rounded {
+ border-radius: .25rem
+}
+
+.rounded-2xl {
+ border-radius: 1rem
+}
+
+.rounded-3xl {
+ border-radius: 1.5rem
+}
+
+.rounded-\[1px\] {
+ border-radius: 1px
+}
+
+.rounded-\[32px\] {
+ border-radius: 32px
+}
+
+.rounded-\[4px\] {
+ border-radius: 4px
+}
+
+.rounded-\[5px\] {
+ border-radius: 5px
+}
+
+.rounded-\[6px\] {
+ border-radius: 6px
+}
+
+.rounded-full {
+ border-radius: 9999px
+}
+
+.rounded-lg {
+ border-radius: .5rem
+}
+
+.rounded-md {
+ border-radius: .375rem
+}
+
+.rounded-none {
+ border-radius: 0
+}
+
+.rounded-sm {
+ border-radius: .125rem
+}
+
+.rounded-xl {
+ border-radius: .75rem
+}
+
+.rounded-b {
+ border-bottom-right-radius: .25rem;
+ border-bottom-left-radius: .25rem
+}
+
+.rounded-b-full {
+ border-bottom-right-radius: 9999px;
+ border-bottom-left-radius: 9999px
+}
+
+.rounded-b-lg {
+ border-bottom-right-radius: .5rem;
+ border-bottom-left-radius: .5rem
+}
+
+.rounded-b-md {
+ border-bottom-right-radius: .375rem;
+ border-bottom-left-radius: .375rem
+}
+
+.rounded-b-none {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0
+}
+
+.rounded-b-xl {
+ border-bottom-right-radius: .75rem;
+ border-bottom-left-radius: .75rem
+}
+
+.rounded-l {
+ border-top-left-radius: .25rem;
+ border-bottom-left-radius: .25rem
+}
+
+.rounded-l-2xl {
+ border-top-left-radius: 1rem;
+ border-bottom-left-radius: 1rem
+}
+
+.rounded-l-full {
+ border-top-left-radius: 9999px;
+ border-bottom-left-radius: 9999px
+}
+
+.rounded-l-lg {
+ border-top-left-radius: .5rem;
+ border-bottom-left-radius: .5rem
+}
+
+.rounded-l-md {
+ border-top-left-radius: .375rem;
+ border-bottom-left-radius: .375rem
+}
+
+.rounded-l-none {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0
+}
+
+.rounded-l-sm {
+ border-top-left-radius: .125rem;
+ border-bottom-left-radius: .125rem
+}
+
+.rounded-r {
+ border-top-right-radius: .25rem;
+ border-bottom-right-radius: .25rem
+}
+
+.rounded-r-full {
+ border-top-right-radius: 9999px;
+ border-bottom-right-radius: 9999px
+}
+
+.rounded-r-lg {
+ border-top-right-radius: .5rem;
+ border-bottom-right-radius: .5rem
+}
+
+.rounded-r-md {
+ border-top-right-radius: .375rem;
+ border-bottom-right-radius: .375rem
+}
+
+.rounded-r-none {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0
+}
+
+.rounded-r-sm {
+ border-top-right-radius: .125rem;
+ border-bottom-right-radius: .125rem
+}
+
+.rounded-t {
+ border-top-left-radius: .25rem;
+ border-top-right-radius: .25rem
+}
+
+.rounded-t-full {
+ border-top-left-radius: 9999px;
+ border-top-right-radius: 9999px
+}
+
+.rounded-t-lg {
+ border-top-left-radius: .5rem;
+ border-top-right-radius: .5rem
+}
+
+.rounded-t-md {
+ border-top-left-radius: .375rem;
+ border-top-right-radius: .375rem
+}
+
+.rounded-t-none {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0
+}
+
+.rounded-t-xl {
+ border-top-left-radius: .75rem;
+ border-top-right-radius: .75rem
+}
+
+.rounded-bl {
+ border-bottom-left-radius: .25rem
+}
+
+.rounded-bl-2xl {
+ border-bottom-left-radius: 1rem
+}
+
+.rounded-bl-md {
+ border-bottom-left-radius: .375rem
+}
+
+.rounded-br {
+ border-bottom-right-radius: .25rem
+}
+
+.rounded-br-md {
+ border-bottom-right-radius: .375rem
+}
+
+.rounded-br-none {
+ border-bottom-right-radius: 0
+}
+
+.rounded-tl {
+ border-top-left-radius: .25rem
+}
+
+.rounded-tl-2xl {
+ border-top-left-radius: 1rem
+}
+
+.rounded-tl-lg {
+ border-top-left-radius: .5rem
+}
+
+.rounded-tr {
+ border-top-right-radius: .25rem
+}
+
+.rounded-tr-lg {
+ border-top-right-radius: .5rem
+}
+
+.\!border-0 {
+ border-width: 0!important
+}
+
+.border {
+ border-width: 1px
+}
+
+.border-0 {
+ border-width: 0
+}
+
+.border-2 {
+ border-width: 2px
+}
+
+.border-4 {
+ border-width: 4px
+}
+
+.border-\[1\.5px\] {
+ border-width: 1.5px
+}
+
+.border-\[1px\] {
+ border-width: 1px
+}
+
+.border-booker-width {
+ border-width: var(--cal-border-booker-width,1px)
+}
+
+.border-x {
+ border-left-width: 1px;
+ border-right-width: 1px
+}
+
+.border-y {
+ border-top-width: 1px;
+ border-bottom-width: 1px
+}
+
+.border-y-0 {
+ border-top-width: 0;
+ border-bottom-width: 0
+}
+
+.border-b {
+ border-bottom-width: 1px
+}
+
+.border-b-0 {
+ border-bottom-width: 0
+}
+
+.border-b-2 {
+ border-bottom-width: 2px
+}
+
+.border-b-4 {
+ border-bottom-width: 4px
+}
+
+.border-l {
+ border-left-width: 1px
+}
+
+.border-l-0 {
+ border-left-width: 0
+}
+
+.border-l-2 {
+ border-left-width: 2px
+}
+
+.border-l-4 {
+ border-left-width: 4px
+}
+
+.border-r {
+ border-right-width: 1px
+}
+
+.border-r-0 {
+ border-right-width: 0
+}
+
+.border-r-2 {
+ border-right-width: 2px
+}
+
+.border-r-4 {
+ border-right-width: 4px
+}
+
+.border-t {
+ border-top-width: 1px
+}
+
+.border-t-0 {
+ border-top-width: 0
+}
+
+.border-t-2 {
+ border-top-width: 2px
+}
+
+.border-t-4 {
+ border-top-width: 4px
+}
+
+.border-solid {
+ border-style: solid
+}
+
+.border-dashed {
+ border-style: dashed
+}
+
+.border-none {
+ border-style: none
+}
+
+.\!border-muted {
+ border-color: var(--cal-border-muted,#f3f4f6)!important
+}
+
+.\!border-red-700 {
+ --tw-border-opacity: 1!important;
+ border-color: rgb(185 28 28/var(--tw-border-opacity))!important
+}
+
+.border-amber-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(254 243 199/var(--tw-border-opacity))
+}
+
+.border-amber-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(253 230 138/var(--tw-border-opacity))
+}
+
+.border-amber-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(252 211 77/var(--tw-border-opacity))
+}
+
+.border-amber-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(251 191 36/var(--tw-border-opacity))
+}
+
+.border-amber-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(255 251 235/var(--tw-border-opacity))
+}
+
+.border-amber-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(245 158 11/var(--tw-border-opacity))
+}
+
+.border-amber-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(217 119 6/var(--tw-border-opacity))
+}
+
+.border-amber-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(180 83 9/var(--tw-border-opacity))
+}
+
+.border-amber-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(146 64 14/var(--tw-border-opacity))
+}
+
+.border-amber-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(120 53 15/var(--tw-border-opacity))
+}
+
+.border-black {
+ --tw-border-opacity: 1;
+ border-color: rgb(17 17 17/var(--tw-border-opacity))
+}
+
+.border-blue-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(219 234 254/var(--tw-border-opacity))
+}
+
+.border-blue-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(191 219 254/var(--tw-border-opacity))
+}
+
+.border-blue-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(147 197 253/var(--tw-border-opacity))
+}
+
+.border-blue-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(96 165 250/var(--tw-border-opacity))
+}
+
+.border-blue-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(239 246 255/var(--tw-border-opacity))
+}
+
+.border-blue-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(59 130 246/var(--tw-border-opacity))
+}
+
+.border-blue-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(37 99 235/var(--tw-border-opacity))
+}
+
+.border-blue-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(29 78 216/var(--tw-border-opacity))
+}
+
+.border-blue-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(30 64 175/var(--tw-border-opacity))
+}
+
+.border-blue-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(30 58 138/var(--tw-border-opacity))
+}
+
+.border-booker {
+ border-color: var(--cal-border-booker,#e5e7eb)
+}
+
+.border-brand-default {
+ border-color: var(--cal-brand,#111827)
+}
+
+.border-cyan-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(207 250 254/var(--tw-border-opacity))
+}
+
+.border-cyan-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(165 243 252/var(--tw-border-opacity))
+}
+
+.border-cyan-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(103 232 249/var(--tw-border-opacity))
+}
+
+.border-cyan-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(34 211 238/var(--tw-border-opacity))
+}
+
+.border-cyan-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(236 254 255/var(--tw-border-opacity))
+}
+
+.border-cyan-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(6 182 212/var(--tw-border-opacity))
+}
+
+.border-cyan-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(8 145 178/var(--tw-border-opacity))
+}
+
+.border-cyan-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(14 116 144/var(--tw-border-opacity))
+}
+
+.border-cyan-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(21 94 117/var(--tw-border-opacity))
+}
+
+.border-cyan-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(22 78 99/var(--tw-border-opacity))
+}
+
+.border-default {
+ border-color: var(--cal-border,#d1d5db)
+}
+
+.border-emerald-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(209 250 229/var(--tw-border-opacity))
+}
+
+.border-emerald-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(167 243 208/var(--tw-border-opacity))
+}
+
+.border-emerald-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(110 231 183/var(--tw-border-opacity))
+}
+
+.border-emerald-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(52 211 153/var(--tw-border-opacity))
+}
+
+.border-emerald-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(236 253 245/var(--tw-border-opacity))
+}
+
+.border-emerald-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(16 185 129/var(--tw-border-opacity))
+}
+
+.border-emerald-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(5 150 105/var(--tw-border-opacity))
+}
+
+.border-emerald-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(4 120 87/var(--tw-border-opacity))
+}
+
+.border-emerald-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(6 95 70/var(--tw-border-opacity))
+}
+
+.border-emerald-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(6 78 59/var(--tw-border-opacity))
+}
+
+.border-emphasis {
+ border-color: var(--cal-border-emphasis,#9ca3af)
+}
+
+.border-error {
+ border-color: var(--cal-border-error,#aa2e26)
+}
+
+.border-fuchsia-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(250 232 255/var(--tw-border-opacity))
+}
+
+.border-fuchsia-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(245 208 254/var(--tw-border-opacity))
+}
+
+.border-fuchsia-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(240 171 252/var(--tw-border-opacity))
+}
+
+.border-fuchsia-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(232 121 249/var(--tw-border-opacity))
+}
+
+.border-fuchsia-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(253 244 255/var(--tw-border-opacity))
+}
+
+.border-fuchsia-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(217 70 239/var(--tw-border-opacity))
+}
+
+.border-fuchsia-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(192 38 211/var(--tw-border-opacity))
+}
+
+.border-fuchsia-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(162 28 175/var(--tw-border-opacity))
+}
+
+.border-fuchsia-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(134 25 143/var(--tw-border-opacity))
+}
+
+.border-fuchsia-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(112 26 117/var(--tw-border-opacity))
+}
+
+.border-gray-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(243 244 246/var(--tw-border-opacity))
+}
+
+.border-gray-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(229 231 235/var(--tw-border-opacity))
+}
+
+.border-gray-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(209 213 219/var(--tw-border-opacity))
+}
+
+.border-gray-300\/20 {
+ border-color: rgba(209,213,219,.2)
+}
+
+.border-gray-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(156 163 175/var(--tw-border-opacity))
+}
+
+.border-gray-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(249 250 251/var(--tw-border-opacity))
+}
+
+.border-gray-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(107 114 128/var(--tw-border-opacity))
+}
+
+.border-gray-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(75 85 99/var(--tw-border-opacity))
+}
+
+.border-gray-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(55 65 81/var(--tw-border-opacity))
+}
+
+.border-gray-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(31 41 55/var(--tw-border-opacity))
+}
+
+.border-gray-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(17 24 39/var(--tw-border-opacity))
+}
+
+.border-green-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(220 252 231/var(--tw-border-opacity))
+}
+
+.border-green-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(187 247 208/var(--tw-border-opacity))
+}
+
+.border-green-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(134 239 172/var(--tw-border-opacity))
+}
+
+.border-green-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(74 222 128/var(--tw-border-opacity))
+}
+
+.border-green-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(240 253 244/var(--tw-border-opacity))
+}
+
+.border-green-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(34 197 94/var(--tw-border-opacity))
+}
+
+.border-green-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(22 163 74/var(--tw-border-opacity))
+}
+
+.border-green-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(21 128 61/var(--tw-border-opacity))
+}
+
+.border-green-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(22 101 52/var(--tw-border-opacity))
+}
+
+.border-green-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(20 83 45/var(--tw-border-opacity))
+}
+
+.border-indigo-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(224 231 255/var(--tw-border-opacity))
+}
+
+.border-indigo-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(199 210 254/var(--tw-border-opacity))
+}
+
+.border-indigo-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(165 180 252/var(--tw-border-opacity))
+}
+
+.border-indigo-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(129 140 248/var(--tw-border-opacity))
+}
+
+.border-indigo-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(238 242 255/var(--tw-border-opacity))
+}
+
+.border-indigo-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(99 102 241/var(--tw-border-opacity))
+}
+
+.border-indigo-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(79 70 229/var(--tw-border-opacity))
+}
+
+.border-indigo-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(67 56 202/var(--tw-border-opacity))
+}
+
+.border-indigo-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(55 48 163/var(--tw-border-opacity))
+}
+
+.border-indigo-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(49 46 129/var(--tw-border-opacity))
+}
+
+.border-inverted {
+ border-color: var(--cal-bg-inverted)
+}
+
+.border-lime-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(236 252 203/var(--tw-border-opacity))
+}
+
+.border-lime-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(217 249 157/var(--tw-border-opacity))
+}
+
+.border-lime-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(190 242 100/var(--tw-border-opacity))
+}
+
+.border-lime-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(163 230 53/var(--tw-border-opacity))
+}
+
+.border-lime-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(247 254 231/var(--tw-border-opacity))
+}
+
+.border-lime-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(132 204 22/var(--tw-border-opacity))
+}
+
+.border-lime-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(101 163 13/var(--tw-border-opacity))
+}
+
+.border-lime-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(77 124 15/var(--tw-border-opacity))
+}
+
+.border-lime-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(63 98 18/var(--tw-border-opacity))
+}
+
+.border-lime-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(54 83 20/var(--tw-border-opacity))
+}
+
+.border-muted {
+ border-color: var(--cal-border-muted,#f3f4f6)
+}
+
+.border-neutral-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(245 245 245/var(--tw-border-opacity))
+}
+
+.border-neutral-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(229 229 229/var(--tw-border-opacity))
+}
+
+.border-neutral-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(212 212 212/var(--tw-border-opacity))
+}
+
+.border-neutral-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(163 163 163/var(--tw-border-opacity))
+}
+
+.border-neutral-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(250 250 250/var(--tw-border-opacity))
+}
+
+.border-neutral-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(115 115 115/var(--tw-border-opacity))
+}
+
+.border-neutral-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(82 82 82/var(--tw-border-opacity))
+}
+
+.border-neutral-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(64 64 64/var(--tw-border-opacity))
+}
+
+.border-neutral-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(38 38 38/var(--tw-border-opacity))
+}
+
+.border-neutral-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(23 23 23/var(--tw-border-opacity))
+}
+
+.border-orange-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(255 237 213/var(--tw-border-opacity))
+}
+
+.border-orange-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(254 215 170/var(--tw-border-opacity))
+}
+
+.border-orange-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(253 186 116/var(--tw-border-opacity))
+}
+
+.border-orange-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(251 146 60/var(--tw-border-opacity))
+}
+
+.border-orange-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(255 247 237/var(--tw-border-opacity))
+}
+
+.border-orange-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(249 115 22/var(--tw-border-opacity))
+}
+
+.border-orange-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(234 88 12/var(--tw-border-opacity))
+}
+
+.border-orange-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(194 65 12/var(--tw-border-opacity))
+}
+
+.border-orange-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(154 52 18/var(--tw-border-opacity))
+}
+
+.border-orange-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(124 45 18/var(--tw-border-opacity))
+}
+
+.border-pink-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(252 231 243/var(--tw-border-opacity))
+}
+
+.border-pink-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(251 207 232/var(--tw-border-opacity))
+}
+
+.border-pink-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(249 168 212/var(--tw-border-opacity))
+}
+
+.border-pink-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(244 114 182/var(--tw-border-opacity))
+}
+
+.border-pink-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(253 242 248/var(--tw-border-opacity))
+}
+
+.border-pink-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(236 72 153/var(--tw-border-opacity))
+}
+
+.border-pink-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(219 39 119/var(--tw-border-opacity))
+}
+
+.border-pink-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(190 24 93/var(--tw-border-opacity))
+}
+
+.border-pink-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(131 24 67/var(--tw-border-opacity))
+}
+
+.border-purple-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(243 232 255/var(--tw-border-opacity))
+}
+
+.border-purple-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(233 213 255/var(--tw-border-opacity))
+}
+
+.border-purple-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(216 180 254/var(--tw-border-opacity))
+}
+
+.border-purple-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(192 132 252/var(--tw-border-opacity))
+}
+
+.border-purple-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(250 245 255/var(--tw-border-opacity))
+}
+
+.border-purple-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(168 85 247/var(--tw-border-opacity))
+}
+
+.border-purple-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(147 51 234/var(--tw-border-opacity))
+}
+
+.border-purple-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(126 34 206/var(--tw-border-opacity))
+}
+
+.border-purple-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(107 33 168/var(--tw-border-opacity))
+}
+
+.border-purple-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(88 28 135/var(--tw-border-opacity))
+}
+
+.border-red-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(254 226 226/var(--tw-border-opacity))
+}
+
+.border-red-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(254 202 202/var(--tw-border-opacity))
+}
+
+.border-red-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(252 165 165/var(--tw-border-opacity))
+}
+
+.border-red-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(248 113 113/var(--tw-border-opacity))
+}
+
+.border-red-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(254 242 242/var(--tw-border-opacity))
+}
+
+.border-red-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(239 68 68/var(--tw-border-opacity))
+}
+
+.border-red-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(220 38 38/var(--tw-border-opacity))
+}
+
+.border-red-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(185 28 28/var(--tw-border-opacity))
+}
+
+.border-red-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(153 27 27/var(--tw-border-opacity))
+}
+
+.border-red-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(127 29 29/var(--tw-border-opacity))
+}
+
+.border-rose-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(255 228 230/var(--tw-border-opacity))
+}
+
+.border-rose-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(254 205 211/var(--tw-border-opacity))
+}
+
+.border-rose-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(253 164 175/var(--tw-border-opacity))
+}
+
+.border-rose-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(251 113 133/var(--tw-border-opacity))
+}
+
+.border-rose-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(255 241 242/var(--tw-border-opacity))
+}
+
+.border-rose-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(244 63 94/var(--tw-border-opacity))
+}
+
+.border-rose-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(225 29 72/var(--tw-border-opacity))
+}
+
+.border-rose-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(190 18 60/var(--tw-border-opacity))
+}
+
+.border-rose-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(159 18 57/var(--tw-border-opacity))
+}
+
+.border-rose-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(136 19 55/var(--tw-border-opacity))
+}
+
+.border-sky-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(224 242 254/var(--tw-border-opacity))
+}
+
+.border-sky-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(186 230 253/var(--tw-border-opacity))
+}
+
+.border-sky-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(125 211 252/var(--tw-border-opacity))
+}
+
+.border-sky-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(56 189 248/var(--tw-border-opacity))
+}
+
+.border-sky-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(240 249 255/var(--tw-border-opacity))
+}
+
+.border-sky-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(14 165 233/var(--tw-border-opacity))
+}
+
+.border-sky-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(2 132 199/var(--tw-border-opacity))
+}
+
+.border-sky-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(3 105 161/var(--tw-border-opacity))
+}
+
+.border-sky-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(7 89 133/var(--tw-border-opacity))
+}
+
+.border-sky-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(12 74 110/var(--tw-border-opacity))
+}
+
+.border-slate-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(241 245 249/var(--tw-border-opacity))
+}
+
+.border-slate-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(226 232 240/var(--tw-border-opacity))
+}
+
+.border-slate-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(203 213 225/var(--tw-border-opacity))
+}
+
+.border-slate-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(148 163 184/var(--tw-border-opacity))
+}
+
+.border-slate-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(248 250 252/var(--tw-border-opacity))
+}
+
+.border-slate-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(100 116 139/var(--tw-border-opacity))
+}
+
+.border-slate-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(71 85 105/var(--tw-border-opacity))
+}
+
+.border-slate-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(51 65 85/var(--tw-border-opacity))
+}
+
+.border-slate-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(30 41 59/var(--tw-border-opacity))
+}
+
+.border-slate-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(15 23 42/var(--tw-border-opacity))
+}
+
+.border-stone-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(245 245 244/var(--tw-border-opacity))
+}
+
+.border-stone-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(231 229 228/var(--tw-border-opacity))
+}
+
+.border-stone-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(214 211 209/var(--tw-border-opacity))
+}
+
+.border-stone-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(168 162 158/var(--tw-border-opacity))
+}
+
+.border-stone-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(250 250 249/var(--tw-border-opacity))
+}
+
+.border-stone-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(120 113 108/var(--tw-border-opacity))
+}
+
+.border-stone-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(87 83 78/var(--tw-border-opacity))
+}
+
+.border-stone-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(68 64 60/var(--tw-border-opacity))
+}
+
+.border-stone-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(41 37 36/var(--tw-border-opacity))
+}
+
+.border-stone-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(28 25 23/var(--tw-border-opacity))
+}
+
+.border-subtle {
+ border-color: var(--cal-border-subtle,#e5e7eb)
+}
+
+.border-teal-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(204 251 241/var(--tw-border-opacity))
+}
+
+.border-teal-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(153 246 228/var(--tw-border-opacity))
+}
+
+.border-teal-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(94 234 212/var(--tw-border-opacity))
+}
+
+.border-teal-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(45 212 191/var(--tw-border-opacity))
+}
+
+.border-teal-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(240 253 250/var(--tw-border-opacity))
+}
+
+.border-teal-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(20 184 166/var(--tw-border-opacity))
+}
+
+.border-teal-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(13 148 136/var(--tw-border-opacity))
+}
+
+.border-teal-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(15 118 110/var(--tw-border-opacity))
+}
+
+.border-teal-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(17 94 89/var(--tw-border-opacity))
+}
+
+.border-teal-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(19 78 74/var(--tw-border-opacity))
+}
+
+.border-transparent {
+ border-color: transparent
+}
+
+.border-violet-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(237 233 254/var(--tw-border-opacity))
+}
+
+.border-violet-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(221 214 254/var(--tw-border-opacity))
+}
+
+.border-violet-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(196 181 253/var(--tw-border-opacity))
+}
+
+.border-violet-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(167 139 250/var(--tw-border-opacity))
+}
+
+.border-violet-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(245 243 255/var(--tw-border-opacity))
+}
+
+.border-violet-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(139 92 246/var(--tw-border-opacity))
+}
+
+.border-violet-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(124 58 237/var(--tw-border-opacity))
+}
+
+.border-violet-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(109 40 217/var(--tw-border-opacity))
+}
+
+.border-violet-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(91 33 182/var(--tw-border-opacity))
+}
+
+.border-violet-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(76 29 149/var(--tw-border-opacity))
+}
+
+.border-white {
+ --tw-border-opacity: 1;
+ border-color: rgb(255 255 255/var(--tw-border-opacity))
+}
+
+.border-yellow-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(254 249 195/var(--tw-border-opacity))
+}
+
+.border-yellow-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(254 240 138/var(--tw-border-opacity))
+}
+
+.border-yellow-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(253 224 71/var(--tw-border-opacity))
+}
+
+.border-yellow-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(250 204 21/var(--tw-border-opacity))
+}
+
+.border-yellow-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(254 252 232/var(--tw-border-opacity))
+}
+
+.border-yellow-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(234 179 8/var(--tw-border-opacity))
+}
+
+.border-yellow-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(202 138 4/var(--tw-border-opacity))
+}
+
+.border-yellow-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(161 98 7/var(--tw-border-opacity))
+}
+
+.border-yellow-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(133 77 14/var(--tw-border-opacity))
+}
+
+.border-yellow-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(113 63 18/var(--tw-border-opacity))
+}
+
+.border-zinc-100 {
+ --tw-border-opacity: 1;
+ border-color: rgb(244 244 245/var(--tw-border-opacity))
+}
+
+.border-zinc-200 {
+ --tw-border-opacity: 1;
+ border-color: rgb(228 228 231/var(--tw-border-opacity))
+}
+
+.border-zinc-300 {
+ --tw-border-opacity: 1;
+ border-color: rgb(212 212 216/var(--tw-border-opacity))
+}
+
+.border-zinc-400 {
+ --tw-border-opacity: 1;
+ border-color: rgb(161 161 170/var(--tw-border-opacity))
+}
+
+.border-zinc-50 {
+ --tw-border-opacity: 1;
+ border-color: rgb(250 250 250/var(--tw-border-opacity))
+}
+
+.border-zinc-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(113 113 122/var(--tw-border-opacity))
+}
+
+.border-zinc-600 {
+ --tw-border-opacity: 1;
+ border-color: rgb(82 82 91/var(--tw-border-opacity))
+}
+
+.border-zinc-700 {
+ --tw-border-opacity: 1;
+ border-color: rgb(63 63 70/var(--tw-border-opacity))
+}
+
+.border-zinc-800 {
+ --tw-border-opacity: 1;
+ border-color: rgb(39 39 42/var(--tw-border-opacity))
+}
+
+.border-zinc-900 {
+ --tw-border-opacity: 1;
+ border-color: rgb(24 24 27/var(--tw-border-opacity))
+}
+
+.border-b-black {
+ --tw-border-opacity: 1;
+ border-bottom-color: rgb(17 17 17/var(--tw-border-opacity))
+}
+
+.border-b-subtle {
+ border-bottom-color: var(--cal-border-subtle,#e5e7eb)
+}
+
+.border-l-default {
+ border-left-color: var(--cal-border,#d1d5db)
+}
+
+.border-r-transparent {
+ border-right-color: transparent
+}
+
+.\!bg-default {
+ background-color: var(--cal-bg,#fff)!important
+}
+
+.\!bg-orange-100 {
+ --tw-bg-opacity: 1!important;
+ background-color: rgb(255 237 213/var(--tw-bg-opacity))!important
+}
+
+.\!bg-subtle {
+ background-color: var(--cal-bg-subtle)!important
+}
+
+.\!bg-success {
+ background-color: var(--cal-bg-success)!important
+}
+
+.\!bg-transparent {
+ background-color: transparent!important
+}
+
+.bg-\[\#FAFAFA\] {
+ --tw-bg-opacity: 1;
+ background-color: rgb(250 250 250/var(--tw-bg-opacity))
+}
+
+.bg-\[\#ffc439\] {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 196 57/var(--tw-bg-opacity))
+}
+
+.bg-amber-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 243 199/var(--tw-bg-opacity))
+}
+
+.bg-amber-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(253 230 138/var(--tw-bg-opacity))
+}
+
+.bg-amber-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(252 211 77/var(--tw-bg-opacity))
+}
+
+.bg-amber-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(251 191 36/var(--tw-bg-opacity))
+}
+
+.bg-amber-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 251 235/var(--tw-bg-opacity))
+}
+
+.bg-amber-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(245 158 11/var(--tw-bg-opacity))
+}
+
+.bg-amber-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(217 119 6/var(--tw-bg-opacity))
+}
+
+.bg-amber-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(180 83 9/var(--tw-bg-opacity))
+}
+
+.bg-amber-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(146 64 14/var(--tw-bg-opacity))
+}
+
+.bg-amber-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(120 53 15/var(--tw-bg-opacity))
+}
+
+.bg-attention {
+ background-color: var(--cal-bg-attention)
+}
+
+.bg-black {
+ --tw-bg-opacity: 1;
+ background-color: rgb(17 17 17/var(--tw-bg-opacity))
+}
+
+.bg-black\/50 {
+ background-color: hsla(0,0%,7%,.5)
+}
+
+.bg-black\/60 {
+ background-color: hsla(0,0%,7%,.6)
+}
+
+.bg-black\/80 {
+ background-color: hsla(0,0%,7%,.8)
+}
+
+.bg-black\/\[3\%\] {
+ background-color: hsla(0,0%,7%,.03)
+}
+
+.bg-blue-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(219 234 254/var(--tw-bg-opacity))
+}
+
+.bg-blue-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(191 219 254/var(--tw-bg-opacity))
+}
+
+.bg-blue-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(147 197 253/var(--tw-bg-opacity))
+}
+
+.bg-blue-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(96 165 250/var(--tw-bg-opacity))
+}
+
+.bg-blue-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(239 246 255/var(--tw-bg-opacity))
+}
+
+.bg-blue-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(59 130 246/var(--tw-bg-opacity))
+}
+
+.bg-blue-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(37 99 235/var(--tw-bg-opacity))
+}
+
+.bg-blue-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(29 78 216/var(--tw-bg-opacity))
+}
+
+.bg-blue-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(30 64 175/var(--tw-bg-opacity))
+}
+
+.bg-blue-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(30 58 138/var(--tw-bg-opacity))
+}
+
+.bg-brand-accent {
+ background-color: var(--cal-brand-accent,#fff)
+}
+
+.bg-brand-default {
+ background-color: var(--cal-brand,#111827)
+}
+
+.bg-brand-subtle {
+ background-color: var(--cal-brand-subtle,#9ca3af)
+}
+
+.bg-cyan-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(207 250 254/var(--tw-bg-opacity))
+}
+
+.bg-cyan-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(165 243 252/var(--tw-bg-opacity))
+}
+
+.bg-cyan-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(103 232 249/var(--tw-bg-opacity))
+}
+
+.bg-cyan-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(34 211 238/var(--tw-bg-opacity))
+}
+
+.bg-cyan-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(236 254 255/var(--tw-bg-opacity))
+}
+
+.bg-cyan-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(6 182 212/var(--tw-bg-opacity))
+}
+
+.bg-cyan-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(8 145 178/var(--tw-bg-opacity))
+}
+
+.bg-cyan-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(14 116 144/var(--tw-bg-opacity))
+}
+
+.bg-cyan-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(21 94 117/var(--tw-bg-opacity))
+}
+
+.bg-cyan-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(22 78 99/var(--tw-bg-opacity))
+}
+
+.bg-default {
+ background-color: var(--cal-bg,#fff)
+}
+
+.bg-emerald-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(209 250 229/var(--tw-bg-opacity))
+}
+
+.bg-emerald-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(167 243 208/var(--tw-bg-opacity))
+}
+
+.bg-emerald-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(110 231 183/var(--tw-bg-opacity))
+}
+
+.bg-emerald-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(52 211 153/var(--tw-bg-opacity))
+}
+
+.bg-emerald-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(236 253 245/var(--tw-bg-opacity))
+}
+
+.bg-emerald-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(16 185 129/var(--tw-bg-opacity))
+}
+
+.bg-emerald-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(5 150 105/var(--tw-bg-opacity))
+}
+
+.bg-emerald-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(4 120 87/var(--tw-bg-opacity))
+}
+
+.bg-emerald-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(6 95 70/var(--tw-bg-opacity))
+}
+
+.bg-emerald-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(6 78 59/var(--tw-bg-opacity))
+}
+
+.bg-emphasis {
+ background-color: var(--cal-bg-emphasis)
+}
+
+.bg-error {
+ background-color: var(--cal-bg-error)
+}
+
+.bg-fuchsia-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(250 232 255/var(--tw-bg-opacity))
+}
+
+.bg-fuchsia-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(245 208 254/var(--tw-bg-opacity))
+}
+
+.bg-fuchsia-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(240 171 252/var(--tw-bg-opacity))
+}
+
+.bg-fuchsia-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(232 121 249/var(--tw-bg-opacity))
+}
+
+.bg-fuchsia-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(253 244 255/var(--tw-bg-opacity))
+}
+
+.bg-fuchsia-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(217 70 239/var(--tw-bg-opacity))
+}
+
+.bg-fuchsia-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(192 38 211/var(--tw-bg-opacity))
+}
+
+.bg-fuchsia-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(162 28 175/var(--tw-bg-opacity))
+}
+
+.bg-fuchsia-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(134 25 143/var(--tw-bg-opacity))
+}
+
+.bg-fuchsia-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(112 26 117/var(--tw-bg-opacity))
+}
+
+.bg-gray-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(243 244 246/var(--tw-bg-opacity))
+}
+
+.bg-gray-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(229 231 235/var(--tw-bg-opacity))
+}
+
+.bg-gray-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(209 213 219/var(--tw-bg-opacity))
+}
+
+.bg-gray-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(156 163 175/var(--tw-bg-opacity))
+}
+
+.bg-gray-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(249 250 251/var(--tw-bg-opacity))
+}
+
+.bg-gray-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(107 114 128/var(--tw-bg-opacity))
+}
+
+.bg-gray-500\/10 {
+ background-color: hsla(220,9%,46%,.1)
+}
+
+.bg-gray-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(75 85 99/var(--tw-bg-opacity))
+}
+
+.bg-gray-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(55 65 81/var(--tw-bg-opacity))
+}
+
+.bg-gray-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(31 41 55/var(--tw-bg-opacity))
+}
+
+.bg-gray-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(17 24 39/var(--tw-bg-opacity))
+}
+
+.bg-green-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(220 252 231/var(--tw-bg-opacity))
+}
+
+.bg-green-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(187 247 208/var(--tw-bg-opacity))
+}
+
+.bg-green-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(134 239 172/var(--tw-bg-opacity))
+}
+
+.bg-green-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(74 222 128/var(--tw-bg-opacity))
+}
+
+.bg-green-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(240 253 244/var(--tw-bg-opacity))
+}
+
+.bg-green-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(34 197 94/var(--tw-bg-opacity))
+}
+
+.bg-green-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(22 163 74/var(--tw-bg-opacity))
+}
+
+.bg-green-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(21 128 61/var(--tw-bg-opacity))
+}
+
+.bg-green-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(22 101 52/var(--tw-bg-opacity))
+}
+
+.bg-green-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(20 83 45/var(--tw-bg-opacity))
+}
+
+.bg-indigo-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(224 231 255/var(--tw-bg-opacity))
+}
+
+.bg-indigo-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(199 210 254/var(--tw-bg-opacity))
+}
+
+.bg-indigo-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(165 180 252/var(--tw-bg-opacity))
+}
+
+.bg-indigo-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(129 140 248/var(--tw-bg-opacity))
+}
+
+.bg-indigo-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(238 242 255/var(--tw-bg-opacity))
+}
+
+.bg-indigo-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(99 102 241/var(--tw-bg-opacity))
+}
+
+.bg-indigo-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(79 70 229/var(--tw-bg-opacity))
+}
+
+.bg-indigo-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(67 56 202/var(--tw-bg-opacity))
+}
+
+.bg-indigo-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(55 48 163/var(--tw-bg-opacity))
+}
+
+.bg-indigo-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(49 46 129/var(--tw-bg-opacity))
+}
+
+.bg-info {
+ background-color: var(--cal-bg-info)
+}
+
+.bg-inherit {
+ background-color: inherit
+}
+
+.bg-inverted {
+ background-color: var(--cal-bg-inverted)
+}
+
+.bg-lime-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(236 252 203/var(--tw-bg-opacity))
+}
+
+.bg-lime-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(217 249 157/var(--tw-bg-opacity))
+}
+
+.bg-lime-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(190 242 100/var(--tw-bg-opacity))
+}
+
+.bg-lime-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(163 230 53/var(--tw-bg-opacity))
+}
+
+.bg-lime-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(247 254 231/var(--tw-bg-opacity))
+}
+
+.bg-lime-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(132 204 22/var(--tw-bg-opacity))
+}
+
+.bg-lime-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(101 163 13/var(--tw-bg-opacity))
+}
+
+.bg-lime-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(77 124 15/var(--tw-bg-opacity))
+}
+
+.bg-lime-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(63 98 18/var(--tw-bg-opacity))
+}
+
+.bg-lime-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(54 83 20/var(--tw-bg-opacity))
+}
+
+.bg-muted {
+ background-color: var(--cal-bg-muted)
+}
+
+.bg-neutral-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(245 245 245/var(--tw-bg-opacity))
+}
+
+.bg-neutral-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(229 229 229/var(--tw-bg-opacity))
+}
+
+.bg-neutral-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(212 212 212/var(--tw-bg-opacity))
+}
+
+.bg-neutral-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(163 163 163/var(--tw-bg-opacity))
+}
+
+.bg-neutral-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(250 250 250/var(--tw-bg-opacity))
+}
+
+.bg-neutral-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(115 115 115/var(--tw-bg-opacity))
+}
+
+.bg-neutral-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(82 82 82/var(--tw-bg-opacity))
+}
+
+.bg-neutral-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(64 64 64/var(--tw-bg-opacity))
+}
+
+.bg-neutral-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(38 38 38/var(--tw-bg-opacity))
+}
+
+.bg-neutral-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(23 23 23/var(--tw-bg-opacity))
+}
+
+.bg-orange-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 237 213/var(--tw-bg-opacity))
+}
+
+.bg-orange-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 215 170/var(--tw-bg-opacity))
+}
+
+.bg-orange-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(253 186 116/var(--tw-bg-opacity))
+}
+
+.bg-orange-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(251 146 60/var(--tw-bg-opacity))
+}
+
+.bg-orange-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 247 237/var(--tw-bg-opacity))
+}
+
+.bg-orange-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(249 115 22/var(--tw-bg-opacity))
+}
+
+.bg-orange-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(234 88 12/var(--tw-bg-opacity))
+}
+
+.bg-orange-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(194 65 12/var(--tw-bg-opacity))
+}
+
+.bg-orange-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(154 52 18/var(--tw-bg-opacity))
+}
+
+.bg-orange-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(124 45 18/var(--tw-bg-opacity))
+}
+
+.bg-pink-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(252 231 243/var(--tw-bg-opacity))
+}
+
+.bg-pink-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(251 207 232/var(--tw-bg-opacity))
+}
+
+.bg-pink-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(249 168 212/var(--tw-bg-opacity))
+}
+
+.bg-pink-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(244 114 182/var(--tw-bg-opacity))
+}
+
+.bg-pink-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(253 242 248/var(--tw-bg-opacity))
+}
+
+.bg-pink-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(236 72 153/var(--tw-bg-opacity))
+}
+
+.bg-pink-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(219 39 119/var(--tw-bg-opacity))
+}
+
+.bg-pink-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(190 24 93/var(--tw-bg-opacity))
+}
+
+.bg-pink-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(131 24 67/var(--tw-bg-opacity))
+}
+
+.bg-purple-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(243 232 255/var(--tw-bg-opacity))
+}
+
+.bg-purple-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(233 213 255/var(--tw-bg-opacity))
+}
+
+.bg-purple-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(216 180 254/var(--tw-bg-opacity))
+}
+
+.bg-purple-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(192 132 252/var(--tw-bg-opacity))
+}
+
+.bg-purple-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(250 245 255/var(--tw-bg-opacity))
+}
+
+.bg-purple-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(168 85 247/var(--tw-bg-opacity))
+}
+
+.bg-purple-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(147 51 234/var(--tw-bg-opacity))
+}
+
+.bg-purple-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(126 34 206/var(--tw-bg-opacity))
+}
+
+.bg-purple-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(107 33 168/var(--tw-bg-opacity))
+}
+
+.bg-purple-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(88 28 135/var(--tw-bg-opacity))
+}
+
+.bg-red-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 226 226/var(--tw-bg-opacity))
+}
+
+.bg-red-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 202 202/var(--tw-bg-opacity))
+}
+
+.bg-red-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(252 165 165/var(--tw-bg-opacity))
+}
+
+.bg-red-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(248 113 113/var(--tw-bg-opacity))
+}
+
+.bg-red-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 242 242/var(--tw-bg-opacity))
+}
+
+.bg-red-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(239 68 68/var(--tw-bg-opacity))
+}
+
+.bg-red-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(220 38 38/var(--tw-bg-opacity))
+}
+
+.bg-red-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(185 28 28/var(--tw-bg-opacity))
+}
+
+.bg-red-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(153 27 27/var(--tw-bg-opacity))
+}
+
+.bg-red-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(127 29 29/var(--tw-bg-opacity))
+}
+
+.bg-rose-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 228 230/var(--tw-bg-opacity))
+}
+
+.bg-rose-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 205 211/var(--tw-bg-opacity))
+}
+
+.bg-rose-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(253 164 175/var(--tw-bg-opacity))
+}
+
+.bg-rose-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(251 113 133/var(--tw-bg-opacity))
+}
+
+.bg-rose-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 241 242/var(--tw-bg-opacity))
+}
+
+.bg-rose-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(244 63 94/var(--tw-bg-opacity))
+}
+
+.bg-rose-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(225 29 72/var(--tw-bg-opacity))
+}
+
+.bg-rose-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(190 18 60/var(--tw-bg-opacity))
+}
+
+.bg-rose-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(159 18 57/var(--tw-bg-opacity))
+}
+
+.bg-rose-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(136 19 55/var(--tw-bg-opacity))
+}
+
+.bg-sky-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(224 242 254/var(--tw-bg-opacity))
+}
+
+.bg-sky-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(186 230 253/var(--tw-bg-opacity))
+}
+
+.bg-sky-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(125 211 252/var(--tw-bg-opacity))
+}
+
+.bg-sky-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(56 189 248/var(--tw-bg-opacity))
+}
+
+.bg-sky-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(240 249 255/var(--tw-bg-opacity))
+}
+
+.bg-sky-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(14 165 233/var(--tw-bg-opacity))
+}
+
+.bg-sky-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(2 132 199/var(--tw-bg-opacity))
+}
+
+.bg-sky-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(3 105 161/var(--tw-bg-opacity))
+}
+
+.bg-sky-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(7 89 133/var(--tw-bg-opacity))
+}
+
+.bg-sky-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(12 74 110/var(--tw-bg-opacity))
+}
+
+.bg-slate-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(241 245 249/var(--tw-bg-opacity))
+}
+
+.bg-slate-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(226 232 240/var(--tw-bg-opacity))
+}
+
+.bg-slate-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(203 213 225/var(--tw-bg-opacity))
+}
+
+.bg-slate-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(148 163 184/var(--tw-bg-opacity))
+}
+
+.bg-slate-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(248 250 252/var(--tw-bg-opacity))
+}
+
+.bg-slate-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(100 116 139/var(--tw-bg-opacity))
+}
+
+.bg-slate-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(71 85 105/var(--tw-bg-opacity))
+}
+
+.bg-slate-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(51 65 85/var(--tw-bg-opacity))
+}
+
+.bg-slate-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(30 41 59/var(--tw-bg-opacity))
+}
+
+.bg-slate-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(15 23 42/var(--tw-bg-opacity))
+}
+
+.bg-stone-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(245 245 244/var(--tw-bg-opacity))
+}
+
+.bg-stone-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(231 229 228/var(--tw-bg-opacity))
+}
+
+.bg-stone-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(214 211 209/var(--tw-bg-opacity))
+}
+
+.bg-stone-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(168 162 158/var(--tw-bg-opacity))
+}
+
+.bg-stone-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(250 250 249/var(--tw-bg-opacity))
+}
+
+.bg-stone-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(120 113 108/var(--tw-bg-opacity))
+}
+
+.bg-stone-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(87 83 78/var(--tw-bg-opacity))
+}
+
+.bg-stone-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(68 64 60/var(--tw-bg-opacity))
+}
+
+.bg-stone-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(41 37 36/var(--tw-bg-opacity))
+}
+
+.bg-stone-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(28 25 23/var(--tw-bg-opacity))
+}
+
+.bg-subtle {
+ background-color: var(--cal-bg-subtle)
+}
+
+.bg-success {
+ background-color: var(--cal-bg-success)
+}
+
+.bg-teal-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(204 251 241/var(--tw-bg-opacity))
+}
+
+.bg-teal-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(153 246 228/var(--tw-bg-opacity))
+}
+
+.bg-teal-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(94 234 212/var(--tw-bg-opacity))
+}
+
+.bg-teal-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(45 212 191/var(--tw-bg-opacity))
+}
+
+.bg-teal-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(240 253 250/var(--tw-bg-opacity))
+}
+
+.bg-teal-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(20 184 166/var(--tw-bg-opacity))
+}
+
+.bg-teal-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(13 148 136/var(--tw-bg-opacity))
+}
+
+.bg-teal-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(15 118 110/var(--tw-bg-opacity))
+}
+
+.bg-teal-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(17 94 89/var(--tw-bg-opacity))
+}
+
+.bg-teal-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(19 78 74/var(--tw-bg-opacity))
+}
+
+.bg-transparent {
+ background-color: transparent
+}
+
+.bg-violet-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(237 233 254/var(--tw-bg-opacity))
+}
+
+.bg-violet-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(221 214 254/var(--tw-bg-opacity))
+}
+
+.bg-violet-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(196 181 253/var(--tw-bg-opacity))
+}
+
+.bg-violet-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(167 139 250/var(--tw-bg-opacity))
+}
+
+.bg-violet-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(245 243 255/var(--tw-bg-opacity))
+}
+
+.bg-violet-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(139 92 246/var(--tw-bg-opacity))
+}
+
+.bg-violet-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(124 58 237/var(--tw-bg-opacity))
+}
+
+.bg-violet-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(109 40 217/var(--tw-bg-opacity))
+}
+
+.bg-violet-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(91 33 182/var(--tw-bg-opacity))
+}
+
+.bg-violet-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(76 29 149/var(--tw-bg-opacity))
+}
+
+.bg-white {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 255 255/var(--tw-bg-opacity))
+}
+
+.bg-yellow-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 249 195/var(--tw-bg-opacity))
+}
+
+.bg-yellow-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 240 138/var(--tw-bg-opacity))
+}
+
+.bg-yellow-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(253 224 71/var(--tw-bg-opacity))
+}
+
+.bg-yellow-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(250 204 21/var(--tw-bg-opacity))
+}
+
+.bg-yellow-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 252 232/var(--tw-bg-opacity))
+}
+
+.bg-yellow-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(234 179 8/var(--tw-bg-opacity))
+}
+
+.bg-yellow-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(202 138 4/var(--tw-bg-opacity))
+}
+
+.bg-yellow-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(161 98 7/var(--tw-bg-opacity))
+}
+
+.bg-yellow-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(133 77 14/var(--tw-bg-opacity))
+}
+
+.bg-yellow-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(113 63 18/var(--tw-bg-opacity))
+}
+
+.bg-zinc-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(244 244 245/var(--tw-bg-opacity))
+}
+
+.bg-zinc-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(228 228 231/var(--tw-bg-opacity))
+}
+
+.bg-zinc-300 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(212 212 216/var(--tw-bg-opacity))
+}
+
+.bg-zinc-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(161 161 170/var(--tw-bg-opacity))
+}
+
+.bg-zinc-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(250 250 250/var(--tw-bg-opacity))
+}
+
+.bg-zinc-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(113 113 122/var(--tw-bg-opacity))
+}
+
+.bg-zinc-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(82 82 91/var(--tw-bg-opacity))
+}
+
+.bg-zinc-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(63 63 70/var(--tw-bg-opacity))
+}
+
+.bg-zinc-800 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(39 39 42/var(--tw-bg-opacity))
+}
+
+.bg-zinc-900 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(24 24 27/var(--tw-bg-opacity))
+}
+
+.bg-opacity-30 {
+ --tw-bg-opacity: 0.3
+}
+
+.bg-opacity-40 {
+ --tw-bg-opacity: 0.4
+}
+
+.bg-opacity-5 {
+ --tw-bg-opacity: 0.05
+}
+
+.bg-opacity-50 {
+ --tw-bg-opacity: 0.5
+}
+
+.bg-opacity-70 {
+ --tw-bg-opacity: 0.7
+}
+
+.bg-opacity-90 {
+ --tw-bg-opacity: 0.9
+}
+
+.bg-gradient-primary {
+ background-image: radial-gradient(162.05% 170% at 109.58% 35%,#667593 0,#e3e3e3 100%)
+}
+
+.bg-gradient-to-l {
+ background-image: linear-gradient(to left,var(--tw-gradient-stops))
+}
+
+.bg-gradient-to-r {
+ background-image: linear-gradient(to right,var(--tw-gradient-stops))
+}
+
+.from-transparent {
+ --tw-gradient-from: transparent var(--tw-gradient-from-position);
+ --tw-gradient-to: transparent var(--tw-gradient-to-position);
+ --tw-gradient-stops: var(--tw-gradient-from),var(--tw-gradient-to)
+}
+
+.to-default {
+ --tw-gradient-to: var(--cal-bg,#fff) var(--tw-gradient-to-position)
+}
+
+.bg-cover {
+ background-size: cover
+}
+
+.bg-clip-text {
+ -webkit-background-clip: text;
+ background-clip: text
+}
+
+.bg-center {
+ background-position: 50%
+}
+
+.bg-repeat {
+ background-repeat: repeat
+}
+
+.bg-no-repeat {
+ background-repeat: no-repeat
+}
+
+.fill-blue-500 {
+ fill: #3b82f6
+}
+
+.fill-current {
+ fill: currentColor
+}
+
+.fill-default {
+ fill: var(--cal-text,#374151)
+}
+
+.fill-emphasis {
+ fill: var(--cal-text-emphasis,#111827)
+}
+
+.fill-muted {
+ fill: var(--cal-text-muted,#9ca3af)
+}
+
+.fill-orange-400 {
+ fill: #fb923c
+}
+
+.fill-transparent {
+ fill: transparent
+}
+
+.stroke-2 {
+ stroke-width: 2
+}
+
+.stroke-\[1\.3px\] {
+ stroke-width: 1.3px
+}
+
+.stroke-\[1\.5px\] {
+ stroke-width: 1.5px
+}
+
+.stroke-\[2\.5px\] {
+ stroke-width: 2.5px
+}
+
+.stroke-\[2px\] {
+ stroke-width: 2px
+}
+
+.stroke-\[3px\] {
+ stroke-width: 3px
+}
+
+.object-contain {
+ -o-object-fit: contain;
+ object-fit: contain
+}
+
+.object-cover {
+ -o-object-fit: cover;
+ object-fit: cover
+}
+
+.object-left {
+ -o-object-position: left;
+ object-position: left
+}
+
+.\!p-0 {
+ padding: 0!important
+}
+
+.\!p-1 {
+ padding: .25rem!important
+}
+
+.\!p-2 {
+ padding: .5rem!important
+}
+
+.\!p-5 {
+ padding: 1.25rem!important
+}
+
+.p-0 {
+ padding: 0
+}
+
+.p-0\.5 {
+ padding: .125rem
+}
+
+.p-1 {
+ padding: .25rem
+}
+
+.p-1\.5 {
+ padding: .375rem
+}
+
+.p-10 {
+ padding: 2.5rem
+}
+
+.p-2 {
+ padding: .5rem
+}
+
+.p-2\.5 {
+ padding: .625rem
+}
+
+.p-3 {
+ padding: .75rem
+}
+
+.p-4 {
+ padding: 1rem
+}
+
+.p-5 {
+ padding: 1.25rem
+}
+
+.p-6 {
+ padding: 1.5rem
+}
+
+.p-7 {
+ padding: 1.75rem
+}
+
+.p-8 {
+ padding: 2rem
+}
+
+.p-\[1px\] {
+ padding: 1px
+}
+
+.p-\[6px\] {
+ padding: 6px
+}
+
+.p-px {
+ padding: 1px
+}
+
+.\!px-0 {
+ padding-left: 0!important;
+ padding-right: 0!important
+}
+
+.\!py-0 {
+ padding-top: 0!important;
+ padding-bottom: 0!important
+}
+
+.px-0 {
+ padding-left: 0;
+ padding-right: 0
+}
+
+.px-1 {
+ padding-left: .25rem;
+ padding-right: .25rem
+}
+
+.px-1\.5 {
+ padding-left: .375rem;
+ padding-right: .375rem
+}
+
+.px-12 {
+ padding-left: 3rem;
+ padding-right: 3rem
+}
+
+.px-2 {
+ padding-left: .5rem;
+ padding-right: .5rem
+}
+
+.px-3 {
+ padding-left: .75rem;
+ padding-right: .75rem
+}
+
+.px-3\.5 {
+ padding-left: .875rem;
+ padding-right: .875rem
+}
+
+.px-4 {
+ padding-left: 1rem;
+ padding-right: 1rem
+}
+
+.px-5 {
+ padding-left: 1.25rem;
+ padding-right: 1.25rem
+}
+
+.px-6 {
+ padding-left: 1.5rem;
+ padding-right: 1.5rem
+}
+
+.px-8 {
+ padding-left: 2rem;
+ padding-right: 2rem
+}
+
+.px-9 {
+ padding-left: 2.25rem;
+ padding-right: 2.25rem
+}
+
+.px-\[10px\] {
+ padding-left: 10px;
+ padding-right: 10px
+}
+
+.px-\[6px\] {
+ padding-left: 6px;
+ padding-right: 6px
+}
+
+.px-px {
+ padding-left: 1px;
+ padding-right: 1px
+}
+
+.py-0 {
+ padding-top: 0;
+ padding-bottom: 0
+}
+
+.py-0\.5 {
+ padding-top: .125rem;
+ padding-bottom: .125rem
+}
+
+.py-1 {
+ padding-top: .25rem;
+ padding-bottom: .25rem
+}
+
+.py-1\.5 {
+ padding-top: .375rem;
+ padding-bottom: .375rem
+}
+
+.py-10 {
+ padding-top: 2.5rem;
+ padding-bottom: 2.5rem
+}
+
+.py-12 {
+ padding-top: 3rem;
+ padding-bottom: 3rem
+}
+
+.py-14 {
+ padding-top: 3.5rem;
+ padding-bottom: 3.5rem
+}
+
+.py-2 {
+ padding-top: .5rem;
+ padding-bottom: .5rem
+}
+
+.py-2\.5 {
+ padding-top: .625rem;
+ padding-bottom: .625rem
+}
+
+.py-24 {
+ padding-top: 6rem;
+ padding-bottom: 6rem
+}
+
+.py-3 {
+ padding-top: .75rem;
+ padding-bottom: .75rem
+}
+
+.py-3\.5 {
+ padding-top: .875rem;
+ padding-bottom: .875rem
+}
+
+.py-4 {
+ padding-top: 1rem;
+ padding-bottom: 1rem
+}
+
+.py-5 {
+ padding-top: 1.25rem;
+ padding-bottom: 1.25rem
+}
+
+.py-6 {
+ padding-top: 1.5rem;
+ padding-bottom: 1.5rem
+}
+
+.py-8 {
+ padding-top: 2rem;
+ padding-bottom: 2rem
+}
+
+.py-\[10px\] {
+ padding-top: 10px;
+ padding-bottom: 10px
+}
+
+.py-\[10x\] {
+ padding-top: 10x;
+ padding-bottom: 10x
+}
+
+.py-\[19px\] {
+ padding-top: 19px;
+ padding-bottom: 19px
+}
+
+.py-\[2px\] {
+ padding-top: 2px;
+ padding-bottom: 2px
+}
+
+.py-\[5px\] {
+ padding-top: 5px;
+ padding-bottom: 5px
+}
+
+.py-\[6px\] {
+ padding-top: 6px;
+ padding-bottom: 6px
+}
+
+.py-px {
+ padding-top: 1px;
+ padding-bottom: 1px
+}
+
+.\!pb-0 {
+ padding-bottom: 0!important
+}
+
+.\!pl-0 {
+ padding-left: 0!important
+}
+
+.pb-0 {
+ padding-bottom: 0
+}
+
+.pb-1 {
+ padding-bottom: .25rem
+}
+
+.pb-10 {
+ padding-bottom: 2.5rem
+}
+
+.pb-11 {
+ padding-bottom: 2.75rem
+}
+
+.pb-12 {
+ padding-bottom: 3rem
+}
+
+.pb-2 {
+ padding-bottom: .5rem
+}
+
+.pb-2\.5 {
+ padding-bottom: .625rem
+}
+
+.pb-20 {
+ padding-bottom: 5rem
+}
+
+.pb-3 {
+ padding-bottom: .75rem
+}
+
+.pb-4 {
+ padding-bottom: 1rem
+}
+
+.pb-5 {
+ padding-bottom: 1.25rem
+}
+
+.pb-6 {
+ padding-bottom: 1.5rem
+}
+
+.pb-7 {
+ padding-bottom: 1.75rem
+}
+
+.pb-8 {
+ padding-bottom: 2rem
+}
+
+.pb-\[1\.5px\] {
+ padding-bottom: 1.5px
+}
+
+.pb-px {
+ padding-bottom: 1px
+}
+
+.pe-2 {
+ -webkit-padding-end: .5rem;
+ padding-inline-end:.5rem}
+
+.pe-3 {
+ -webkit-padding-end: .75rem;
+ padding-inline-end:.75rem}
+
+.pl-0 {
+ padding-left: 0
+}
+
+.pl-1 {
+ padding-left: .25rem
+}
+
+.pl-10 {
+ padding-left: 2.5rem
+}
+
+.pl-16 {
+ padding-left: 4rem
+}
+
+.pl-2 {
+ padding-left: .5rem
+}
+
+.pl-2\.5 {
+ padding-left: .625rem
+}
+
+.pl-3 {
+ padding-left: .75rem
+}
+
+.pl-4 {
+ padding-left: 1rem
+}
+
+.pl-5 {
+ padding-left: 1.25rem
+}
+
+.pl-6 {
+ padding-left: 1.5rem
+}
+
+.pl-px {
+ padding-left: 1px
+}
+
+.pr-0 {
+ padding-right: 0
+}
+
+.pr-1 {
+ padding-right: .25rem
+}
+
+.pr-10 {
+ padding-right: 2.5rem
+}
+
+.pr-12 {
+ padding-right: 3rem
+}
+
+.pr-2 {
+ padding-right: .5rem
+}
+
+.pr-2\.5 {
+ padding-right: .625rem
+}
+
+.pr-3 {
+ padding-right: .75rem
+}
+
+.pr-4 {
+ padding-right: 1rem
+}
+
+.pr-6 {
+ padding-right: 1.5rem
+}
+
+.pr-8 {
+ padding-right: 2rem
+}
+
+.pr-px {
+ padding-right: 1px
+}
+
+.ps-1 {
+ -webkit-padding-start: .25rem;
+ padding-inline-start:.25rem}
+
+.ps-6 {
+ -webkit-padding-start: 1.5rem;
+ padding-inline-start:1.5rem}
+
+.pt-0 {
+ padding-top: 0
+}
+
+.pt-0\.5 {
+ padding-top: .125rem
+}
+
+.pt-1 {
+ padding-top: .25rem
+}
+
+.pt-12 {
+ padding-top: 3rem
+}
+
+.pt-16 {
+ padding-top: 4rem
+}
+
+.pt-2 {
+ padding-top: .5rem
+}
+
+.pt-3 {
+ padding-top: .75rem
+}
+
+.pt-4 {
+ padding-top: 1rem
+}
+
+.pt-5 {
+ padding-top: 1.25rem
+}
+
+.pt-6 {
+ padding-top: 1.5rem
+}
+
+.pt-8 {
+ padding-top: 2rem
+}
+
+.pt-9 {
+ padding-top: 2.25rem
+}
+
+.pt-\[100\%\] {
+ padding-top: 100%
+}
+
+.pt-\[6px\] {
+ padding-top: 6px
+}
+
+.pt-px {
+ padding-top: 1px
+}
+
+.text-left {
+ text-align: left
+}
+
+.text-center {
+ text-align: center
+}
+
+.text-right {
+ text-align: right
+}
+
+.align-baseline {
+ vertical-align: baseline
+}
+
+.align-top {
+ vertical-align: top
+}
+
+.align-middle {
+ vertical-align: middle
+}
+
+.align-bottom {
+ vertical-align: bottom
+}
+
+.align-text-top {
+ vertical-align: text-top
+}
+
+.\!font-cal {
+ font-family: var(--font-cal),ui-serif,Georgia,Cambria,times new roman,Times,serif!important
+}
+
+.font-cal {
+ font-family: var(--font-cal),ui-serif,Georgia,Cambria,times new roman,Times,serif
+}
+
+.font-mono {
+ font-family: Roboto Mono,monospace
+}
+
+.font-sans {
+ font-family: var(--font-inter),ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji
+}
+
+.\!text-xl {
+ font-size: 1.25rem!important;
+ line-height: 1.75rem!important
+}
+
+.text-2xl {
+ font-size: 1.5rem;
+ line-height: 2rem
+}
+
+.text-3xl {
+ font-size: 1.875rem;
+ line-height: 2.25rem
+}
+
+.text-4xl {
+ font-size: 2.25rem;
+ line-height: 2.5rem
+}
+
+.text-6xl {
+ font-size: 3.75rem;
+ line-height: 1
+}
+
+.text-\[0\.5rem\] {
+ font-size: .5rem
+}
+
+.text-\[0px\] {
+ font-size: 0
+}
+
+.text-\[10px\] {
+ font-size: 10px
+}
+
+.text-\[12px\] {
+ font-size: 12px
+}
+
+.text-\[13px\] {
+ font-size: 13px
+}
+
+.text-\[14px\] {
+ font-size: 14px
+}
+
+.text-\[28px\] {
+ font-size: 28px
+}
+
+.text-base {
+ font-size: 1rem;
+ line-height: 1.5rem
+}
+
+.text-lg {
+ font-size: 1.125rem;
+ line-height: 1.75rem
+}
+
+.text-sm {
+ font-size: .875rem;
+ line-height: 1.25rem
+}
+
+.text-xl {
+ font-size: 1.25rem;
+ line-height: 1.75rem
+}
+
+.text-xs {
+ font-size: .75rem;
+ line-height: 1rem
+}
+
+.\!font-normal {
+ font-weight: 400!important
+}
+
+.font-bold {
+ font-weight: 700
+}
+
+.font-extrabold {
+ font-weight: 800
+}
+
+.font-extralight {
+ font-weight: 200
+}
+
+.font-light {
+ font-weight: 300
+}
+
+.font-medium {
+ font-weight: 500
+}
+
+.font-normal {
+ font-weight: 400
+}
+
+.font-semibold {
+ font-weight: 600
+}
+
+.uppercase {
+ text-transform: uppercase
+}
+
+.lowercase {
+ text-transform: lowercase
+}
+
+.capitalize {
+ text-transform: capitalize
+}
+
+.normal-case {
+ text-transform: none
+}
+
+.italic {
+ font-style: italic
+}
+
+.not-italic {
+ font-style: normal
+}
+
+.normal-nums {
+ font-variant-numeric: normal
+}
+
+.ordinal {
+ --tw-ordinal: ordinal
+}
+
+.ordinal,.slashed-zero {
+ font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)
+}
+
+.slashed-zero {
+ --tw-slashed-zero: slashed-zero
+}
+
+.lining-nums {
+ --tw-numeric-figure: lining-nums
+}
+
+.lining-nums,.oldstyle-nums {
+ font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)
+}
+
+.oldstyle-nums {
+ --tw-numeric-figure: oldstyle-nums
+}
+
+.proportional-nums {
+ --tw-numeric-spacing: proportional-nums
+}
+
+.proportional-nums,.tabular-nums {
+ font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)
+}
+
+.tabular-nums {
+ --tw-numeric-spacing: tabular-nums
+}
+
+.diagonal-fractions {
+ --tw-numeric-fraction: diagonal-fractions;
+ font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)
+}
+
+.leading-3 {
+ line-height: .75rem
+}
+
+.leading-4 {
+ line-height: 1rem
+}
+
+.leading-5 {
+ line-height: 1.25rem
+}
+
+.leading-6 {
+ line-height: 1.5rem
+}
+
+.leading-7 {
+ line-height: 1.75rem
+}
+
+.leading-\[0\] {
+ line-height: 0
+}
+
+.leading-\[18px\] {
+ line-height: 18px
+}
+
+.leading-none {
+ line-height: 1
+}
+
+.leading-normal {
+ line-height: 1.5
+}
+
+.leading-tight {
+ line-height: 1.25
+}
+
+.tracking-\[0\.01em\] {
+ letter-spacing: .01em
+}
+
+.tracking-normal {
+ letter-spacing: 0
+}
+
+.tracking-tight {
+ letter-spacing: -.025em
+}
+
+.tracking-wide {
+ letter-spacing: .025em
+}
+
+.tracking-wider {
+ letter-spacing: .05em
+}
+
+.tracking-widest {
+ letter-spacing: .1em
+}
+
+.\!text-default {
+ color: var(--cal-text,#374151)!important
+}
+
+.\!text-emphasis {
+ color: var(--cal-text-emphasis,#111827)!important
+}
+
+.\!text-green-700 {
+ --tw-text-opacity: 1!important;
+ color: rgb(21 128 61/var(--tw-text-opacity))!important
+}
+
+.\!text-orange-900 {
+ --tw-text-opacity: 1!important;
+ color: rgb(124 45 18/var(--tw-text-opacity))!important
+}
+
+.\!text-red-700 {
+ --tw-text-opacity: 1!important;
+ color: rgb(185 28 28/var(--tw-text-opacity))!important
+}
+
+.text-\[\#5B93F9\] {
+ --tw-text-opacity: 1;
+ color: rgb(91 147 249/var(--tw-text-opacity))
+}
+
+.text-\[--cal-brand\] {
+ color: var(--cal-brand)
+}
+
+.text-amber-100 {
+ --tw-text-opacity: 1;
+ color: rgb(254 243 199/var(--tw-text-opacity))
+}
+
+.text-amber-200 {
+ --tw-text-opacity: 1;
+ color: rgb(253 230 138/var(--tw-text-opacity))
+}
+
+.text-amber-300 {
+ --tw-text-opacity: 1;
+ color: rgb(252 211 77/var(--tw-text-opacity))
+}
+
+.text-amber-400 {
+ --tw-text-opacity: 1;
+ color: rgb(251 191 36/var(--tw-text-opacity))
+}
+
+.text-amber-50 {
+ --tw-text-opacity: 1;
+ color: rgb(255 251 235/var(--tw-text-opacity))
+}
+
+.text-amber-500 {
+ --tw-text-opacity: 1;
+ color: rgb(245 158 11/var(--tw-text-opacity))
+}
+
+.text-amber-600 {
+ --tw-text-opacity: 1;
+ color: rgb(217 119 6/var(--tw-text-opacity))
+}
+
+.text-amber-700 {
+ --tw-text-opacity: 1;
+ color: rgb(180 83 9/var(--tw-text-opacity))
+}
+
+.text-amber-800 {
+ --tw-text-opacity: 1;
+ color: rgb(146 64 14/var(--tw-text-opacity))
+}
+
+.text-amber-900 {
+ --tw-text-opacity: 1;
+ color: rgb(120 53 15/var(--tw-text-opacity))
+}
+
+.text-attention {
+ color: var(--cal-text-attention,#73321b)
+}
+
+.text-black {
+ --tw-text-opacity: 1;
+ color: rgb(17 17 17/var(--tw-text-opacity))
+}
+
+.text-blue-100 {
+ --tw-text-opacity: 1;
+ color: rgb(219 234 254/var(--tw-text-opacity))
+}
+
+.text-blue-200 {
+ --tw-text-opacity: 1;
+ color: rgb(191 219 254/var(--tw-text-opacity))
+}
+
+.text-blue-300 {
+ --tw-text-opacity: 1;
+ color: rgb(147 197 253/var(--tw-text-opacity))
+}
+
+.text-blue-400 {
+ --tw-text-opacity: 1;
+ color: rgb(96 165 250/var(--tw-text-opacity))
+}
+
+.text-blue-50 {
+ --tw-text-opacity: 1;
+ color: rgb(239 246 255/var(--tw-text-opacity))
+}
+
+.text-blue-500 {
+ --tw-text-opacity: 1;
+ color: rgb(59 130 246/var(--tw-text-opacity))
+}
+
+.text-blue-600 {
+ --tw-text-opacity: 1;
+ color: rgb(37 99 235/var(--tw-text-opacity))
+}
+
+.text-blue-700 {
+ --tw-text-opacity: 1;
+ color: rgb(29 78 216/var(--tw-text-opacity))
+}
+
+.text-blue-800 {
+ --tw-text-opacity: 1;
+ color: rgb(30 64 175/var(--tw-text-opacity))
+}
+
+.text-blue-900 {
+ --tw-text-opacity: 1;
+ color: rgb(30 58 138/var(--tw-text-opacity))
+}
+
+.text-brand {
+ color: var(--cal-brand-text,"white")
+}
+
+.text-current {
+ color: currentColor
+}
+
+.text-cyan-100 {
+ --tw-text-opacity: 1;
+ color: rgb(207 250 254/var(--tw-text-opacity))
+}
+
+.text-cyan-200 {
+ --tw-text-opacity: 1;
+ color: rgb(165 243 252/var(--tw-text-opacity))
+}
+
+.text-cyan-300 {
+ --tw-text-opacity: 1;
+ color: rgb(103 232 249/var(--tw-text-opacity))
+}
+
+.text-cyan-400 {
+ --tw-text-opacity: 1;
+ color: rgb(34 211 238/var(--tw-text-opacity))
+}
+
+.text-cyan-50 {
+ --tw-text-opacity: 1;
+ color: rgb(236 254 255/var(--tw-text-opacity))
+}
+
+.text-cyan-500 {
+ --tw-text-opacity: 1;
+ color: rgb(6 182 212/var(--tw-text-opacity))
+}
+
+.text-cyan-600 {
+ --tw-text-opacity: 1;
+ color: rgb(8 145 178/var(--tw-text-opacity))
+}
+
+.text-cyan-700 {
+ --tw-text-opacity: 1;
+ color: rgb(14 116 144/var(--tw-text-opacity))
+}
+
+.text-cyan-800 {
+ --tw-text-opacity: 1;
+ color: rgb(21 94 117/var(--tw-text-opacity))
+}
+
+.text-cyan-900 {
+ --tw-text-opacity: 1;
+ color: rgb(22 78 99/var(--tw-text-opacity))
+}
+
+.text-default {
+ color: var(--cal-text,#374151)
+}
+
+.text-emerald-100 {
+ --tw-text-opacity: 1;
+ color: rgb(209 250 229/var(--tw-text-opacity))
+}
+
+.text-emerald-200 {
+ --tw-text-opacity: 1;
+ color: rgb(167 243 208/var(--tw-text-opacity))
+}
+
+.text-emerald-300 {
+ --tw-text-opacity: 1;
+ color: rgb(110 231 183/var(--tw-text-opacity))
+}
+
+.text-emerald-400 {
+ --tw-text-opacity: 1;
+ color: rgb(52 211 153/var(--tw-text-opacity))
+}
+
+.text-emerald-50 {
+ --tw-text-opacity: 1;
+ color: rgb(236 253 245/var(--tw-text-opacity))
+}
+
+.text-emerald-500 {
+ --tw-text-opacity: 1;
+ color: rgb(16 185 129/var(--tw-text-opacity))
+}
+
+.text-emerald-600 {
+ --tw-text-opacity: 1;
+ color: rgb(5 150 105/var(--tw-text-opacity))
+}
+
+.text-emerald-700 {
+ --tw-text-opacity: 1;
+ color: rgb(4 120 87/var(--tw-text-opacity))
+}
+
+.text-emerald-800 {
+ --tw-text-opacity: 1;
+ color: rgb(6 95 70/var(--tw-text-opacity))
+}
+
+.text-emerald-900 {
+ --tw-text-opacity: 1;
+ color: rgb(6 78 59/var(--tw-text-opacity))
+}
+
+.text-emphasis {
+ color: var(--cal-text-emphasis,#111827)
+}
+
+.text-error {
+ color: var(--cal-text-error,#752522)
+}
+
+.text-fuchsia-100 {
+ --tw-text-opacity: 1;
+ color: rgb(250 232 255/var(--tw-text-opacity))
+}
+
+.text-fuchsia-200 {
+ --tw-text-opacity: 1;
+ color: rgb(245 208 254/var(--tw-text-opacity))
+}
+
+.text-fuchsia-300 {
+ --tw-text-opacity: 1;
+ color: rgb(240 171 252/var(--tw-text-opacity))
+}
+
+.text-fuchsia-400 {
+ --tw-text-opacity: 1;
+ color: rgb(232 121 249/var(--tw-text-opacity))
+}
+
+.text-fuchsia-50 {
+ --tw-text-opacity: 1;
+ color: rgb(253 244 255/var(--tw-text-opacity))
+}
+
+.text-fuchsia-500 {
+ --tw-text-opacity: 1;
+ color: rgb(217 70 239/var(--tw-text-opacity))
+}
+
+.text-fuchsia-600 {
+ --tw-text-opacity: 1;
+ color: rgb(192 38 211/var(--tw-text-opacity))
+}
+
+.text-fuchsia-700 {
+ --tw-text-opacity: 1;
+ color: rgb(162 28 175/var(--tw-text-opacity))
+}
+
+.text-fuchsia-800 {
+ --tw-text-opacity: 1;
+ color: rgb(134 25 143/var(--tw-text-opacity))
+}
+
+.text-fuchsia-900 {
+ --tw-text-opacity: 1;
+ color: rgb(112 26 117/var(--tw-text-opacity))
+}
+
+.text-gray-100 {
+ --tw-text-opacity: 1;
+ color: rgb(243 244 246/var(--tw-text-opacity))
+}
+
+.text-gray-200 {
+ --tw-text-opacity: 1;
+ color: rgb(229 231 235/var(--tw-text-opacity))
+}
+
+.text-gray-300 {
+ --tw-text-opacity: 1;
+ color: rgb(209 213 219/var(--tw-text-opacity))
+}
+
+.text-gray-400 {
+ --tw-text-opacity: 1;
+ color: rgb(156 163 175/var(--tw-text-opacity))
+}
+
+.text-gray-50 {
+ --tw-text-opacity: 1;
+ color: rgb(249 250 251/var(--tw-text-opacity))
+}
+
+.text-gray-500 {
+ --tw-text-opacity: 1;
+ color: rgb(107 114 128/var(--tw-text-opacity))
+}
+
+.text-gray-600 {
+ --tw-text-opacity: 1;
+ color: rgb(75 85 99/var(--tw-text-opacity))
+}
+
+.text-gray-700 {
+ --tw-text-opacity: 1;
+ color: rgb(55 65 81/var(--tw-text-opacity))
+}
+
+.text-gray-800 {
+ --tw-text-opacity: 1;
+ color: rgb(31 41 55/var(--tw-text-opacity))
+}
+
+.text-gray-900 {
+ --tw-text-opacity: 1;
+ color: rgb(17 24 39/var(--tw-text-opacity))
+}
+
+.text-green-100 {
+ --tw-text-opacity: 1;
+ color: rgb(220 252 231/var(--tw-text-opacity))
+}
+
+.text-green-200 {
+ --tw-text-opacity: 1;
+ color: rgb(187 247 208/var(--tw-text-opacity))
+}
+
+.text-green-300 {
+ --tw-text-opacity: 1;
+ color: rgb(134 239 172/var(--tw-text-opacity))
+}
+
+.text-green-400 {
+ --tw-text-opacity: 1;
+ color: rgb(74 222 128/var(--tw-text-opacity))
+}
+
+.text-green-50 {
+ --tw-text-opacity: 1;
+ color: rgb(240 253 244/var(--tw-text-opacity))
+}
+
+.text-green-500 {
+ --tw-text-opacity: 1;
+ color: rgb(34 197 94/var(--tw-text-opacity))
+}
+
+.text-green-600 {
+ --tw-text-opacity: 1;
+ color: rgb(22 163 74/var(--tw-text-opacity))
+}
+
+.text-green-700 {
+ --tw-text-opacity: 1;
+ color: rgb(21 128 61/var(--tw-text-opacity))
+}
+
+.text-green-800 {
+ --tw-text-opacity: 1;
+ color: rgb(22 101 52/var(--tw-text-opacity))
+}
+
+.text-green-900 {
+ --tw-text-opacity: 1;
+ color: rgb(20 83 45/var(--tw-text-opacity))
+}
+
+.text-indigo-100 {
+ --tw-text-opacity: 1;
+ color: rgb(224 231 255/var(--tw-text-opacity))
+}
+
+.text-indigo-200 {
+ --tw-text-opacity: 1;
+ color: rgb(199 210 254/var(--tw-text-opacity))
+}
+
+.text-indigo-300 {
+ --tw-text-opacity: 1;
+ color: rgb(165 180 252/var(--tw-text-opacity))
+}
+
+.text-indigo-400 {
+ --tw-text-opacity: 1;
+ color: rgb(129 140 248/var(--tw-text-opacity))
+}
+
+.text-indigo-50 {
+ --tw-text-opacity: 1;
+ color: rgb(238 242 255/var(--tw-text-opacity))
+}
+
+.text-indigo-500 {
+ --tw-text-opacity: 1;
+ color: rgb(99 102 241/var(--tw-text-opacity))
+}
+
+.text-indigo-600 {
+ --tw-text-opacity: 1;
+ color: rgb(79 70 229/var(--tw-text-opacity))
+}
+
+.text-indigo-700 {
+ --tw-text-opacity: 1;
+ color: rgb(67 56 202/var(--tw-text-opacity))
+}
+
+.text-indigo-800 {
+ --tw-text-opacity: 1;
+ color: rgb(55 48 163/var(--tw-text-opacity))
+}
+
+.text-indigo-900 {
+ --tw-text-opacity: 1;
+ color: rgb(49 46 129/var(--tw-text-opacity))
+}
+
+.text-info {
+ color: var(--cal-text-info,#253985)
+}
+
+.text-inherit {
+ color: inherit
+}
+
+.text-inverted {
+ color: var(--cal-text-inverted,#fff)
+}
+
+.text-lime-100 {
+ --tw-text-opacity: 1;
+ color: rgb(236 252 203/var(--tw-text-opacity))
+}
+
+.text-lime-200 {
+ --tw-text-opacity: 1;
+ color: rgb(217 249 157/var(--tw-text-opacity))
+}
+
+.text-lime-300 {
+ --tw-text-opacity: 1;
+ color: rgb(190 242 100/var(--tw-text-opacity))
+}
+
+.text-lime-400 {
+ --tw-text-opacity: 1;
+ color: rgb(163 230 53/var(--tw-text-opacity))
+}
+
+.text-lime-50 {
+ --tw-text-opacity: 1;
+ color: rgb(247 254 231/var(--tw-text-opacity))
+}
+
+.text-lime-500 {
+ --tw-text-opacity: 1;
+ color: rgb(132 204 22/var(--tw-text-opacity))
+}
+
+.text-lime-600 {
+ --tw-text-opacity: 1;
+ color: rgb(101 163 13/var(--tw-text-opacity))
+}
+
+.text-lime-700 {
+ --tw-text-opacity: 1;
+ color: rgb(77 124 15/var(--tw-text-opacity))
+}
+
+.text-lime-800 {
+ --tw-text-opacity: 1;
+ color: rgb(63 98 18/var(--tw-text-opacity))
+}
+
+.text-lime-900 {
+ --tw-text-opacity: 1;
+ color: rgb(54 83 20/var(--tw-text-opacity))
+}
+
+.text-muted {
+ color: var(--cal-text-muted,#9ca3af)
+}
+
+.text-neutral-100 {
+ --tw-text-opacity: 1;
+ color: rgb(245 245 245/var(--tw-text-opacity))
+}
+
+.text-neutral-200 {
+ --tw-text-opacity: 1;
+ color: rgb(229 229 229/var(--tw-text-opacity))
+}
+
+.text-neutral-300 {
+ --tw-text-opacity: 1;
+ color: rgb(212 212 212/var(--tw-text-opacity))
+}
+
+.text-neutral-400 {
+ --tw-text-opacity: 1;
+ color: rgb(163 163 163/var(--tw-text-opacity))
+}
+
+.text-neutral-50 {
+ --tw-text-opacity: 1;
+ color: rgb(250 250 250/var(--tw-text-opacity))
+}
+
+.text-neutral-500 {
+ --tw-text-opacity: 1;
+ color: rgb(115 115 115/var(--tw-text-opacity))
+}
+
+.text-neutral-600 {
+ --tw-text-opacity: 1;
+ color: rgb(82 82 82/var(--tw-text-opacity))
+}
+
+.text-neutral-700 {
+ --tw-text-opacity: 1;
+ color: rgb(64 64 64/var(--tw-text-opacity))
+}
+
+.text-neutral-800 {
+ --tw-text-opacity: 1;
+ color: rgb(38 38 38/var(--tw-text-opacity))
+}
+
+.text-neutral-900 {
+ --tw-text-opacity: 1;
+ color: rgb(23 23 23/var(--tw-text-opacity))
+}
+
+.text-orange-100 {
+ --tw-text-opacity: 1;
+ color: rgb(255 237 213/var(--tw-text-opacity))
+}
+
+.text-orange-200 {
+ --tw-text-opacity: 1;
+ color: rgb(254 215 170/var(--tw-text-opacity))
+}
+
+.text-orange-300 {
+ --tw-text-opacity: 1;
+ color: rgb(253 186 116/var(--tw-text-opacity))
+}
+
+.text-orange-400 {
+ --tw-text-opacity: 1;
+ color: rgb(251 146 60/var(--tw-text-opacity))
+}
+
+.text-orange-50 {
+ --tw-text-opacity: 1;
+ color: rgb(255 247 237/var(--tw-text-opacity))
+}
+
+.text-orange-500 {
+ --tw-text-opacity: 1;
+ color: rgb(249 115 22/var(--tw-text-opacity))
+}
+
+.text-orange-600 {
+ --tw-text-opacity: 1;
+ color: rgb(234 88 12/var(--tw-text-opacity))
+}
+
+.text-orange-700 {
+ --tw-text-opacity: 1;
+ color: rgb(194 65 12/var(--tw-text-opacity))
+}
+
+.text-orange-800 {
+ --tw-text-opacity: 1;
+ color: rgb(154 52 18/var(--tw-text-opacity))
+}
+
+.text-orange-900 {
+ --tw-text-opacity: 1;
+ color: rgb(124 45 18/var(--tw-text-opacity))
+}
+
+.text-pink-100 {
+ --tw-text-opacity: 1;
+ color: rgb(252 231 243/var(--tw-text-opacity))
+}
+
+.text-pink-200 {
+ --tw-text-opacity: 1;
+ color: rgb(251 207 232/var(--tw-text-opacity))
+}
+
+.text-pink-300 {
+ --tw-text-opacity: 1;
+ color: rgb(249 168 212/var(--tw-text-opacity))
+}
+
+.text-pink-400 {
+ --tw-text-opacity: 1;
+ color: rgb(244 114 182/var(--tw-text-opacity))
+}
+
+.text-pink-50 {
+ --tw-text-opacity: 1;
+ color: rgb(253 242 248/var(--tw-text-opacity))
+}
+
+.text-pink-500 {
+ --tw-text-opacity: 1;
+ color: rgb(236 72 153/var(--tw-text-opacity))
+}
+
+.text-pink-600 {
+ --tw-text-opacity: 1;
+ color: rgb(219 39 119/var(--tw-text-opacity))
+}
+
+.text-pink-700 {
+ --tw-text-opacity: 1;
+ color: rgb(190 24 93/var(--tw-text-opacity))
+}
+
+.text-pink-900 {
+ --tw-text-opacity: 1;
+ color: rgb(131 24 67/var(--tw-text-opacity))
+}
+
+.text-purple-100 {
+ --tw-text-opacity: 1;
+ color: rgb(243 232 255/var(--tw-text-opacity))
+}
+
+.text-purple-200 {
+ --tw-text-opacity: 1;
+ color: rgb(233 213 255/var(--tw-text-opacity))
+}
+
+.text-purple-300 {
+ --tw-text-opacity: 1;
+ color: rgb(216 180 254/var(--tw-text-opacity))
+}
+
+.text-purple-400 {
+ --tw-text-opacity: 1;
+ color: rgb(192 132 252/var(--tw-text-opacity))
+}
+
+.text-purple-50 {
+ --tw-text-opacity: 1;
+ color: rgb(250 245 255/var(--tw-text-opacity))
+}
+
+.text-purple-500 {
+ --tw-text-opacity: 1;
+ color: rgb(168 85 247/var(--tw-text-opacity))
+}
+
+.text-purple-600 {
+ --tw-text-opacity: 1;
+ color: rgb(147 51 234/var(--tw-text-opacity))
+}
+
+.text-purple-700 {
+ --tw-text-opacity: 1;
+ color: rgb(126 34 206/var(--tw-text-opacity))
+}
+
+.text-purple-800 {
+ --tw-text-opacity: 1;
+ color: rgb(107 33 168/var(--tw-text-opacity))
+}
+
+.text-purple-900 {
+ --tw-text-opacity: 1;
+ color: rgb(88 28 135/var(--tw-text-opacity))
+}
+
+.text-red-100 {
+ --tw-text-opacity: 1;
+ color: rgb(254 226 226/var(--tw-text-opacity))
+}
+
+.text-red-200 {
+ --tw-text-opacity: 1;
+ color: rgb(254 202 202/var(--tw-text-opacity))
+}
+
+.text-red-300 {
+ --tw-text-opacity: 1;
+ color: rgb(252 165 165/var(--tw-text-opacity))
+}
+
+.text-red-400 {
+ --tw-text-opacity: 1;
+ color: rgb(248 113 113/var(--tw-text-opacity))
+}
+
+.text-red-50 {
+ --tw-text-opacity: 1;
+ color: rgb(254 242 242/var(--tw-text-opacity))
+}
+
+.text-red-500 {
+ --tw-text-opacity: 1;
+ color: rgb(239 68 68/var(--tw-text-opacity))
+}
+
+.text-red-600 {
+ --tw-text-opacity: 1;
+ color: rgb(220 38 38/var(--tw-text-opacity))
+}
+
+.text-red-700 {
+ --tw-text-opacity: 1;
+ color: rgb(185 28 28/var(--tw-text-opacity))
+}
+
+.text-red-700\/30 {
+ color: rgba(185,28,28,.3)
+}
+
+.text-red-800 {
+ --tw-text-opacity: 1;
+ color: rgb(153 27 27/var(--tw-text-opacity))
+}
+
+.text-red-900 {
+ --tw-text-opacity: 1;
+ color: rgb(127 29 29/var(--tw-text-opacity))
+}
+
+.text-rose-100 {
+ --tw-text-opacity: 1;
+ color: rgb(255 228 230/var(--tw-text-opacity))
+}
+
+.text-rose-200 {
+ --tw-text-opacity: 1;
+ color: rgb(254 205 211/var(--tw-text-opacity))
+}
+
+.text-rose-300 {
+ --tw-text-opacity: 1;
+ color: rgb(253 164 175/var(--tw-text-opacity))
+}
+
+.text-rose-400 {
+ --tw-text-opacity: 1;
+ color: rgb(251 113 133/var(--tw-text-opacity))
+}
+
+.text-rose-50 {
+ --tw-text-opacity: 1;
+ color: rgb(255 241 242/var(--tw-text-opacity))
+}
+
+.text-rose-500 {
+ --tw-text-opacity: 1;
+ color: rgb(244 63 94/var(--tw-text-opacity))
+}
+
+.text-rose-600 {
+ --tw-text-opacity: 1;
+ color: rgb(225 29 72/var(--tw-text-opacity))
+}
+
+.text-rose-700 {
+ --tw-text-opacity: 1;
+ color: rgb(190 18 60/var(--tw-text-opacity))
+}
+
+.text-rose-800 {
+ --tw-text-opacity: 1;
+ color: rgb(159 18 57/var(--tw-text-opacity))
+}
+
+.text-rose-900 {
+ --tw-text-opacity: 1;
+ color: rgb(136 19 55/var(--tw-text-opacity))
+}
+
+.text-sky-100 {
+ --tw-text-opacity: 1;
+ color: rgb(224 242 254/var(--tw-text-opacity))
+}
+
+.text-sky-200 {
+ --tw-text-opacity: 1;
+ color: rgb(186 230 253/var(--tw-text-opacity))
+}
+
+.text-sky-300 {
+ --tw-text-opacity: 1;
+ color: rgb(125 211 252/var(--tw-text-opacity))
+}
+
+.text-sky-400 {
+ --tw-text-opacity: 1;
+ color: rgb(56 189 248/var(--tw-text-opacity))
+}
+
+.text-sky-50 {
+ --tw-text-opacity: 1;
+ color: rgb(240 249 255/var(--tw-text-opacity))
+}
+
+.text-sky-500 {
+ --tw-text-opacity: 1;
+ color: rgb(14 165 233/var(--tw-text-opacity))
+}
+
+.text-sky-600 {
+ --tw-text-opacity: 1;
+ color: rgb(2 132 199/var(--tw-text-opacity))
+}
+
+.text-sky-700 {
+ --tw-text-opacity: 1;
+ color: rgb(3 105 161/var(--tw-text-opacity))
+}
+
+.text-sky-800 {
+ --tw-text-opacity: 1;
+ color: rgb(7 89 133/var(--tw-text-opacity))
+}
+
+.text-sky-900 {
+ --tw-text-opacity: 1;
+ color: rgb(12 74 110/var(--tw-text-opacity))
+}
+
+.text-slate-100 {
+ --tw-text-opacity: 1;
+ color: rgb(241 245 249/var(--tw-text-opacity))
+}
+
+.text-slate-200 {
+ --tw-text-opacity: 1;
+ color: rgb(226 232 240/var(--tw-text-opacity))
+}
+
+.text-slate-300 {
+ --tw-text-opacity: 1;
+ color: rgb(203 213 225/var(--tw-text-opacity))
+}
+
+.text-slate-400 {
+ --tw-text-opacity: 1;
+ color: rgb(148 163 184/var(--tw-text-opacity))
+}
+
+.text-slate-50 {
+ --tw-text-opacity: 1;
+ color: rgb(248 250 252/var(--tw-text-opacity))
+}
+
+.text-slate-500 {
+ --tw-text-opacity: 1;
+ color: rgb(100 116 139/var(--tw-text-opacity))
+}
+
+.text-slate-600 {
+ --tw-text-opacity: 1;
+ color: rgb(71 85 105/var(--tw-text-opacity))
+}
+
+.text-slate-700 {
+ --tw-text-opacity: 1;
+ color: rgb(51 65 85/var(--tw-text-opacity))
+}
+
+.text-slate-800 {
+ --tw-text-opacity: 1;
+ color: rgb(30 41 59/var(--tw-text-opacity))
+}
+
+.text-slate-900 {
+ --tw-text-opacity: 1;
+ color: rgb(15 23 42/var(--tw-text-opacity))
+}
+
+.text-stone-100 {
+ --tw-text-opacity: 1;
+ color: rgb(245 245 244/var(--tw-text-opacity))
+}
+
+.text-stone-200 {
+ --tw-text-opacity: 1;
+ color: rgb(231 229 228/var(--tw-text-opacity))
+}
+
+.text-stone-300 {
+ --tw-text-opacity: 1;
+ color: rgb(214 211 209/var(--tw-text-opacity))
+}
+
+.text-stone-400 {
+ --tw-text-opacity: 1;
+ color: rgb(168 162 158/var(--tw-text-opacity))
+}
+
+.text-stone-50 {
+ --tw-text-opacity: 1;
+ color: rgb(250 250 249/var(--tw-text-opacity))
+}
+
+.text-stone-500 {
+ --tw-text-opacity: 1;
+ color: rgb(120 113 108/var(--tw-text-opacity))
+}
+
+.text-stone-600 {
+ --tw-text-opacity: 1;
+ color: rgb(87 83 78/var(--tw-text-opacity))
+}
+
+.text-stone-700 {
+ --tw-text-opacity: 1;
+ color: rgb(68 64 60/var(--tw-text-opacity))
+}
+
+.text-stone-800 {
+ --tw-text-opacity: 1;
+ color: rgb(41 37 36/var(--tw-text-opacity))
+}
+
+.text-stone-900 {
+ --tw-text-opacity: 1;
+ color: rgb(28 25 23/var(--tw-text-opacity))
+}
+
+.text-subtle {
+ color: var(--cal-text-subtle,#6b7280)
+}
+
+.text-success {
+ color: var(--cal-text-success,#285231)
+}
+
+.text-teal-100 {
+ --tw-text-opacity: 1;
+ color: rgb(204 251 241/var(--tw-text-opacity))
+}
+
+.text-teal-200 {
+ --tw-text-opacity: 1;
+ color: rgb(153 246 228/var(--tw-text-opacity))
+}
+
+.text-teal-300 {
+ --tw-text-opacity: 1;
+ color: rgb(94 234 212/var(--tw-text-opacity))
+}
+
+.text-teal-400 {
+ --tw-text-opacity: 1;
+ color: rgb(45 212 191/var(--tw-text-opacity))
+}
+
+.text-teal-50 {
+ --tw-text-opacity: 1;
+ color: rgb(240 253 250/var(--tw-text-opacity))
+}
+
+.text-teal-500 {
+ --tw-text-opacity: 1;
+ color: rgb(20 184 166/var(--tw-text-opacity))
+}
+
+.text-teal-600 {
+ --tw-text-opacity: 1;
+ color: rgb(13 148 136/var(--tw-text-opacity))
+}
+
+.text-teal-700 {
+ --tw-text-opacity: 1;
+ color: rgb(15 118 110/var(--tw-text-opacity))
+}
+
+.text-teal-800 {
+ --tw-text-opacity: 1;
+ color: rgb(17 94 89/var(--tw-text-opacity))
+}
+
+.text-teal-900 {
+ --tw-text-opacity: 1;
+ color: rgb(19 78 74/var(--tw-text-opacity))
+}
+
+.text-transparent {
+ color: transparent
+}
+
+.text-violet-100 {
+ --tw-text-opacity: 1;
+ color: rgb(237 233 254/var(--tw-text-opacity))
+}
+
+.text-violet-200 {
+ --tw-text-opacity: 1;
+ color: rgb(221 214 254/var(--tw-text-opacity))
+}
+
+.text-violet-300 {
+ --tw-text-opacity: 1;
+ color: rgb(196 181 253/var(--tw-text-opacity))
+}
+
+.text-violet-400 {
+ --tw-text-opacity: 1;
+ color: rgb(167 139 250/var(--tw-text-opacity))
+}
+
+.text-violet-50 {
+ --tw-text-opacity: 1;
+ color: rgb(245 243 255/var(--tw-text-opacity))
+}
+
+.text-violet-500 {
+ --tw-text-opacity: 1;
+ color: rgb(139 92 246/var(--tw-text-opacity))
+}
+
+.text-violet-600 {
+ --tw-text-opacity: 1;
+ color: rgb(124 58 237/var(--tw-text-opacity))
+}
+
+.text-violet-700 {
+ --tw-text-opacity: 1;
+ color: rgb(109 40 217/var(--tw-text-opacity))
+}
+
+.text-violet-800 {
+ --tw-text-opacity: 1;
+ color: rgb(91 33 182/var(--tw-text-opacity))
+}
+
+.text-violet-900 {
+ --tw-text-opacity: 1;
+ color: rgb(76 29 149/var(--tw-text-opacity))
+}
+
+.text-white {
+ --tw-text-opacity: 1;
+ color: rgb(255 255 255/var(--tw-text-opacity))
+}
+
+.text-yellow-100 {
+ --tw-text-opacity: 1;
+ color: rgb(254 249 195/var(--tw-text-opacity))
+}
+
+.text-yellow-200 {
+ --tw-text-opacity: 1;
+ color: rgb(254 240 138/var(--tw-text-opacity))
+}
+
+.text-yellow-300 {
+ --tw-text-opacity: 1;
+ color: rgb(253 224 71/var(--tw-text-opacity))
+}
+
+.text-yellow-400 {
+ --tw-text-opacity: 1;
+ color: rgb(250 204 21/var(--tw-text-opacity))
+}
+
+.text-yellow-50 {
+ --tw-text-opacity: 1;
+ color: rgb(254 252 232/var(--tw-text-opacity))
+}
+
+.text-yellow-500 {
+ --tw-text-opacity: 1;
+ color: rgb(234 179 8/var(--tw-text-opacity))
+}
+
+.text-yellow-600 {
+ --tw-text-opacity: 1;
+ color: rgb(202 138 4/var(--tw-text-opacity))
+}
+
+.text-yellow-700 {
+ --tw-text-opacity: 1;
+ color: rgb(161 98 7/var(--tw-text-opacity))
+}
+
+.text-yellow-800 {
+ --tw-text-opacity: 1;
+ color: rgb(133 77 14/var(--tw-text-opacity))
+}
+
+.text-yellow-900 {
+ --tw-text-opacity: 1;
+ color: rgb(113 63 18/var(--tw-text-opacity))
+}
+
+.text-zinc-100 {
+ --tw-text-opacity: 1;
+ color: rgb(244 244 245/var(--tw-text-opacity))
+}
+
+.text-zinc-200 {
+ --tw-text-opacity: 1;
+ color: rgb(228 228 231/var(--tw-text-opacity))
+}
+
+.text-zinc-300 {
+ --tw-text-opacity: 1;
+ color: rgb(212 212 216/var(--tw-text-opacity))
+}
+
+.text-zinc-400 {
+ --tw-text-opacity: 1;
+ color: rgb(161 161 170/var(--tw-text-opacity))
+}
+
+.text-zinc-50 {
+ --tw-text-opacity: 1;
+ color: rgb(250 250 250/var(--tw-text-opacity))
+}
+
+.text-zinc-500 {
+ --tw-text-opacity: 1;
+ color: rgb(113 113 122/var(--tw-text-opacity))
+}
+
+.text-zinc-600 {
+ --tw-text-opacity: 1;
+ color: rgb(82 82 91/var(--tw-text-opacity))
+}
+
+.text-zinc-700 {
+ --tw-text-opacity: 1;
+ color: rgb(63 63 70/var(--tw-text-opacity))
+}
+
+.text-zinc-800 {
+ --tw-text-opacity: 1;
+ color: rgb(39 39 42/var(--tw-text-opacity))
+}
+
+.text-zinc-900 {
+ --tw-text-opacity: 1;
+ color: rgb(24 24 27/var(--tw-text-opacity))
+}
+
+.underline {
+ text-decoration-line: underline
+}
+
+.overline {
+ text-decoration-line: overline
+}
+
+.line-through {
+ text-decoration-line: line-through
+}
+
+.no-underline {
+ text-decoration-line: none
+}
+
+.decoration-gray-400 {
+ text-decoration-color: #9ca3af
+}
+
+.decoration-dashed {
+ text-decoration-style: dashed
+}
+
+.underline-offset-2 {
+ text-underline-offset: 2px
+}
+
+.antialiased {
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale
+}
+
+.subpixel-antialiased {
+ -webkit-font-smoothing: auto;
+ -moz-osx-font-smoothing: auto
+}
+
+.\!opacity-30 {
+ opacity: .3!important
+}
+
+.opacity-0 {
+ opacity: 0
+}
+
+.opacity-100 {
+ opacity: 1
+}
+
+.opacity-20 {
+ opacity: .2
+}
+
+.opacity-25 {
+ opacity: .25
+}
+
+.opacity-30 {
+ opacity: .3
+}
+
+.opacity-40 {
+ opacity: .4
+}
+
+.opacity-50 {
+ opacity: .5
+}
+
+.opacity-60 {
+ opacity: .6
+}
+
+.opacity-70 {
+ opacity: .7
+}
+
+.opacity-75 {
+ opacity: .75
+}
+
+.opacity-80 {
+ opacity: .8
+}
+
+.\!shadow-none {
+ --tw-shadow: 0 0 #0000!important;
+ --tw-shadow-colored: 0 0 #0000!important;
+ box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)!important
+}
+
+.shadow {
+ --tw-shadow: 0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);
+ --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)
+}
+
+.shadow,.shadow-dropdown {
+ box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)
+}
+
+.shadow-dropdown {
+ --tw-shadow: 0px 2px 6px -1px rgba(0,0,0,.08);
+ --tw-shadow-colored: 0px 2px 6px -1px var(--tw-shadow-color)
+}
+
+.shadow-inner {
+ --tw-shadow: inset 0 2px 4px 0 rgba(0,0,0,.05);
+ --tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color)
+}
+
+.shadow-inner,.shadow-lg {
+ box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)
+}
+
+.shadow-lg {
+ --tw-shadow: 0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);
+ --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)
+}
+
+.shadow-md {
+ --tw-shadow: 0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);
+ --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)
+}
+
+.shadow-md,.shadow-none {
+ box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)
+}
+
+.shadow-none {
+ --tw-shadow: 0 0 #0000;
+ --tw-shadow-colored: 0 0 #0000
+}
+
+.shadow-sm {
+ --tw-shadow: 0 1px 2px 0 rgba(0,0,0,.05);
+ --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color)
+}
+
+.shadow-sm,.shadow-xl {
+ box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)
+}
+
+.shadow-xl {
+ --tw-shadow: 0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);
+ --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)
+}
+
+.outline-none {
+ outline: 2px solid transparent;
+ outline-offset: 2px
+}
+
+.outline {
+ outline-style: solid
+}
+
+.outline-amber-100 {
+ outline-color: #fef3c7
+}
+
+.outline-amber-200 {
+ outline-color: #fde68a
+}
+
+.outline-amber-300 {
+ outline-color: #fcd34d
+}
+
+.outline-amber-400 {
+ outline-color: #fbbf24
+}
+
+.outline-amber-50 {
+ outline-color: #fffbeb
+}
+
+.outline-amber-500 {
+ outline-color: #f59e0b
+}
+
+.outline-amber-600 {
+ outline-color: #d97706
+}
+
+.outline-amber-700 {
+ outline-color: #b45309
+}
+
+.outline-amber-800 {
+ outline-color: #92400e
+}
+
+.outline-amber-900 {
+ outline-color: #78350f
+}
+
+.outline-black {
+ outline-color: #111
+}
+
+.outline-blue-100 {
+ outline-color: #dbeafe
+}
+
+.outline-blue-200 {
+ outline-color: #bfdbfe
+}
+
+.outline-blue-300 {
+ outline-color: #93c5fd
+}
+
+.outline-blue-400 {
+ outline-color: #60a5fa
+}
+
+.outline-blue-50 {
+ outline-color: #eff6ff
+}
+
+.outline-blue-500 {
+ outline-color: #3b82f6
+}
+
+.outline-blue-600 {
+ outline-color: #2563eb
+}
+
+.outline-blue-700 {
+ outline-color: #1d4ed8
+}
+
+.outline-blue-800 {
+ outline-color: #1e40af
+}
+
+.outline-blue-900 {
+ outline-color: #1e3a8a
+}
+
+.outline-cyan-100 {
+ outline-color: #cffafe
+}
+
+.outline-cyan-200 {
+ outline-color: #a5f3fc
+}
+
+.outline-cyan-300 {
+ outline-color: #67e8f9
+}
+
+.outline-cyan-400 {
+ outline-color: #22d3ee
+}
+
+.outline-cyan-50 {
+ outline-color: #ecfeff
+}
+
+.outline-cyan-500 {
+ outline-color: #06b6d4
+}
+
+.outline-cyan-600 {
+ outline-color: #0891b2
+}
+
+.outline-cyan-700 {
+ outline-color: #0e7490
+}
+
+.outline-cyan-800 {
+ outline-color: #155e75
+}
+
+.outline-cyan-900 {
+ outline-color: #164e63
+}
+
+.outline-emerald-100 {
+ outline-color: #d1fae5
+}
+
+.outline-emerald-200 {
+ outline-color: #a7f3d0
+}
+
+.outline-emerald-300 {
+ outline-color: #6ee7b7
+}
+
+.outline-emerald-400 {
+ outline-color: #34d399
+}
+
+.outline-emerald-50 {
+ outline-color: #ecfdf5
+}
+
+.outline-emerald-500 {
+ outline-color: #10b981
+}
+
+.outline-emerald-600 {
+ outline-color: #059669
+}
+
+.outline-emerald-700 {
+ outline-color: #047857
+}
+
+.outline-emerald-800 {
+ outline-color: #065f46
+}
+
+.outline-emerald-900 {
+ outline-color: #064e3b
+}
+
+.outline-fuchsia-100 {
+ outline-color: #fae8ff
+}
+
+.outline-fuchsia-200 {
+ outline-color: #f5d0fe
+}
+
+.outline-fuchsia-300 {
+ outline-color: #f0abfc
+}
+
+.outline-fuchsia-400 {
+ outline-color: #e879f9
+}
+
+.outline-fuchsia-50 {
+ outline-color: #fdf4ff
+}
+
+.outline-fuchsia-500 {
+ outline-color: #d946ef
+}
+
+.outline-fuchsia-600 {
+ outline-color: #c026d3
+}
+
+.outline-fuchsia-700 {
+ outline-color: #a21caf
+}
+
+.outline-fuchsia-800 {
+ outline-color: #86198f
+}
+
+.outline-fuchsia-900 {
+ outline-color: #701a75
+}
+
+.outline-gray-100 {
+ outline-color: #f3f4f6
+}
+
+.outline-gray-200 {
+ outline-color: #e5e7eb
+}
+
+.outline-gray-300 {
+ outline-color: #d1d5db
+}
+
+.outline-gray-400 {
+ outline-color: #9ca3af
+}
+
+.outline-gray-50 {
+ outline-color: #f9fafb
+}
+
+.outline-gray-500 {
+ outline-color: #6b7280
+}
+
+.outline-gray-600 {
+ outline-color: #4b5563
+}
+
+.outline-gray-700 {
+ outline-color: #374151
+}
+
+.outline-gray-800 {
+ outline-color: #1f2937
+}
+
+.outline-gray-900 {
+ outline-color: #111827
+}
+
+.outline-green-100 {
+ outline-color: #dcfce7
+}
+
+.outline-green-200 {
+ outline-color: #bbf7d0
+}
+
+.outline-green-300 {
+ outline-color: #86efac
+}
+
+.outline-green-400 {
+ outline-color: #4ade80
+}
+
+.outline-green-50 {
+ outline-color: #f0fdf4
+}
+
+.outline-green-500 {
+ outline-color: #22c55e
+}
+
+.outline-green-600 {
+ outline-color: #16a34a
+}
+
+.outline-green-700 {
+ outline-color: #15803d
+}
+
+.outline-green-800 {
+ outline-color: #166534
+}
+
+.outline-green-900 {
+ outline-color: #14532d
+}
+
+.outline-indigo-100 {
+ outline-color: #e0e7ff
+}
+
+.outline-indigo-200 {
+ outline-color: #c7d2fe
+}
+
+.outline-indigo-300 {
+ outline-color: #a5b4fc
+}
+
+.outline-indigo-400 {
+ outline-color: #818cf8
+}
+
+.outline-indigo-50 {
+ outline-color: #eef2ff
+}
+
+.outline-indigo-500 {
+ outline-color: #6366f1
+}
+
+.outline-indigo-600 {
+ outline-color: #4f46e5
+}
+
+.outline-indigo-700 {
+ outline-color: #4338ca
+}
+
+.outline-indigo-800 {
+ outline-color: #3730a3
+}
+
+.outline-indigo-900 {
+ outline-color: #312e81
+}
+
+.outline-lime-100 {
+ outline-color: #ecfccb
+}
+
+.outline-lime-200 {
+ outline-color: #d9f99d
+}
+
+.outline-lime-300 {
+ outline-color: #bef264
+}
+
+.outline-lime-400 {
+ outline-color: #a3e635
+}
+
+.outline-lime-50 {
+ outline-color: #f7fee7
+}
+
+.outline-lime-500 {
+ outline-color: #84cc16
+}
+
+.outline-lime-600 {
+ outline-color: #65a30d
+}
+
+.outline-lime-700 {
+ outline-color: #4d7c0f
+}
+
+.outline-lime-800 {
+ outline-color: #3f6212
+}
+
+.outline-lime-900 {
+ outline-color: #365314
+}
+
+.outline-neutral-100 {
+ outline-color: #f5f5f5
+}
+
+.outline-neutral-200 {
+ outline-color: #e5e5e5
+}
+
+.outline-neutral-300 {
+ outline-color: #d4d4d4
+}
+
+.outline-neutral-400 {
+ outline-color: #a3a3a3
+}
+
+.outline-neutral-50 {
+ outline-color: #fafafa
+}
+
+.outline-neutral-500 {
+ outline-color: #737373
+}
+
+.outline-neutral-600 {
+ outline-color: #525252
+}
+
+.outline-neutral-700 {
+ outline-color: #404040
+}
+
+.outline-neutral-800 {
+ outline-color: #262626
+}
+
+.outline-neutral-900 {
+ outline-color: #171717
+}
+
+.outline-orange-100 {
+ outline-color: #ffedd5
+}
+
+.outline-orange-200 {
+ outline-color: #fed7aa
+}
+
+.outline-orange-300 {
+ outline-color: #fdba74
+}
+
+.outline-orange-400 {
+ outline-color: #fb923c
+}
+
+.outline-orange-50 {
+ outline-color: #fff7ed
+}
+
+.outline-orange-500 {
+ outline-color: #f97316
+}
+
+.outline-orange-600 {
+ outline-color: #ea580c
+}
+
+.outline-orange-700 {
+ outline-color: #c2410c
+}
+
+.outline-orange-800 {
+ outline-color: #9a3412
+}
+
+.outline-orange-900 {
+ outline-color: #7c2d12
+}
+
+.outline-pink-100 {
+ outline-color: #fce7f3
+}
+
+.outline-pink-200 {
+ outline-color: #fbcfe8
+}
+
+.outline-pink-300 {
+ outline-color: #f9a8d4
+}
+
+.outline-pink-400 {
+ outline-color: #f472b6
+}
+
+.outline-pink-50 {
+ outline-color: #fdf2f8
+}
+
+.outline-pink-500 {
+ outline-color: #ec4899
+}
+
+.outline-pink-600 {
+ outline-color: #db2777
+}
+
+.outline-pink-700 {
+ outline-color: #be185d
+}
+
+.outline-pink-900 {
+ outline-color: #831843
+}
+
+.outline-purple-100 {
+ outline-color: #f3e8ff
+}
+
+.outline-purple-200 {
+ outline-color: #e9d5ff
+}
+
+.outline-purple-300 {
+ outline-color: #d8b4fe
+}
+
+.outline-purple-400 {
+ outline-color: #c084fc
+}
+
+.outline-purple-50 {
+ outline-color: #faf5ff
+}
+
+.outline-purple-500 {
+ outline-color: #a855f7
+}
+
+.outline-purple-600 {
+ outline-color: #9333ea
+}
+
+.outline-purple-700 {
+ outline-color: #7e22ce
+}
+
+.outline-purple-800 {
+ outline-color: #6b21a8
+}
+
+.outline-purple-900 {
+ outline-color: #581c87
+}
+
+.outline-red-100 {
+ outline-color: #fee2e2
+}
+
+.outline-red-200 {
+ outline-color: #fecaca
+}
+
+.outline-red-300 {
+ outline-color: #fca5a5
+}
+
+.outline-red-400 {
+ outline-color: #f87171
+}
+
+.outline-red-50 {
+ outline-color: #fef2f2
+}
+
+.outline-red-500 {
+ outline-color: #ef4444
+}
+
+.outline-red-600 {
+ outline-color: #dc2626
+}
+
+.outline-red-700 {
+ outline-color: #b91c1c
+}
+
+.outline-red-800 {
+ outline-color: #991b1b
+}
+
+.outline-red-900 {
+ outline-color: #7f1d1d
+}
+
+.outline-rose-100 {
+ outline-color: #ffe4e6
+}
+
+.outline-rose-200 {
+ outline-color: #fecdd3
+}
+
+.outline-rose-300 {
+ outline-color: #fda4af
+}
+
+.outline-rose-400 {
+ outline-color: #fb7185
+}
+
+.outline-rose-50 {
+ outline-color: #fff1f2
+}
+
+.outline-rose-500 {
+ outline-color: #f43f5e
+}
+
+.outline-rose-600 {
+ outline-color: #e11d48
+}
+
+.outline-rose-700 {
+ outline-color: #be123c
+}
+
+.outline-rose-800 {
+ outline-color: #9f1239
+}
+
+.outline-rose-900 {
+ outline-color: #881337
+}
+
+.outline-sky-100 {
+ outline-color: #e0f2fe
+}
+
+.outline-sky-200 {
+ outline-color: #bae6fd
+}
+
+.outline-sky-300 {
+ outline-color: #7dd3fc
+}
+
+.outline-sky-400 {
+ outline-color: #38bdf8
+}
+
+.outline-sky-50 {
+ outline-color: #f0f9ff
+}
+
+.outline-sky-500 {
+ outline-color: #0ea5e9
+}
+
+.outline-sky-600 {
+ outline-color: #0284c7
+}
+
+.outline-sky-700 {
+ outline-color: #0369a1
+}
+
+.outline-sky-800 {
+ outline-color: #075985
+}
+
+.outline-sky-900 {
+ outline-color: #0c4a6e
+}
+
+.outline-slate-100 {
+ outline-color: #f1f5f9
+}
+
+.outline-slate-200 {
+ outline-color: #e2e8f0
+}
+
+.outline-slate-300 {
+ outline-color: #cbd5e1
+}
+
+.outline-slate-400 {
+ outline-color: #94a3b8
+}
+
+.outline-slate-50 {
+ outline-color: #f8fafc
+}
+
+.outline-slate-500 {
+ outline-color: #64748b
+}
+
+.outline-slate-600 {
+ outline-color: #475569
+}
+
+.outline-slate-700 {
+ outline-color: #334155
+}
+
+.outline-slate-800 {
+ outline-color: #1e293b
+}
+
+.outline-slate-900 {
+ outline-color: #0f172a
+}
+
+.outline-stone-100 {
+ outline-color: #f5f5f4
+}
+
+.outline-stone-200 {
+ outline-color: #e7e5e4
+}
+
+.outline-stone-300 {
+ outline-color: #d6d3d1
+}
+
+.outline-stone-400 {
+ outline-color: #a8a29e
+}
+
+.outline-stone-50 {
+ outline-color: #fafaf9
+}
+
+.outline-stone-500 {
+ outline-color: #78716c
+}
+
+.outline-stone-600 {
+ outline-color: #57534e
+}
+
+.outline-stone-700 {
+ outline-color: #44403c
+}
+
+.outline-stone-800 {
+ outline-color: #292524
+}
+
+.outline-stone-900 {
+ outline-color: #1c1917
+}
+
+.outline-teal-100 {
+ outline-color: #ccfbf1
+}
+
+.outline-teal-200 {
+ outline-color: #99f6e4
+}
+
+.outline-teal-300 {
+ outline-color: #5eead4
+}
+
+.outline-teal-400 {
+ outline-color: #2dd4bf
+}
+
+.outline-teal-50 {
+ outline-color: #f0fdfa
+}
+
+.outline-teal-500 {
+ outline-color: #14b8a6
+}
+
+.outline-teal-600 {
+ outline-color: #0d9488
+}
+
+.outline-teal-700 {
+ outline-color: #0f766e
+}
+
+.outline-teal-800 {
+ outline-color: #115e59
+}
+
+.outline-teal-900 {
+ outline-color: #134e4a
+}
+
+.outline-transparent {
+ outline-color: transparent
+}
+
+.outline-violet-100 {
+ outline-color: #ede9fe
+}
+
+.outline-violet-200 {
+ outline-color: #ddd6fe
+}
+
+.outline-violet-300 {
+ outline-color: #c4b5fd
+}
+
+.outline-violet-400 {
+ outline-color: #a78bfa
+}
+
+.outline-violet-50 {
+ outline-color: #f5f3ff
+}
+
+.outline-violet-500 {
+ outline-color: #8b5cf6
+}
+
+.outline-violet-600 {
+ outline-color: #7c3aed
+}
+
+.outline-violet-700 {
+ outline-color: #6d28d9
+}
+
+.outline-violet-800 {
+ outline-color: #5b21b6
+}
+
+.outline-violet-900 {
+ outline-color: #4c1d95
+}
+
+.outline-white {
+ outline-color: #fff
+}
+
+.outline-yellow-100 {
+ outline-color: #fef9c3
+}
+
+.outline-yellow-200 {
+ outline-color: #fef08a
+}
+
+.outline-yellow-300 {
+ outline-color: #fde047
+}
+
+.outline-yellow-400 {
+ outline-color: #facc15
+}
+
+.outline-yellow-50 {
+ outline-color: #fefce8
+}
+
+.outline-yellow-500 {
+ outline-color: #eab308
+}
+
+.outline-yellow-600 {
+ outline-color: #ca8a04
+}
+
+.outline-yellow-700 {
+ outline-color: #a16207
+}
+
+.outline-yellow-800 {
+ outline-color: #854d0e
+}
+
+.outline-yellow-900 {
+ outline-color: #713f12
+}
+
+.outline-zinc-100 {
+ outline-color: #f4f4f5
+}
+
+.outline-zinc-200 {
+ outline-color: #e4e4e7
+}
+
+.outline-zinc-300 {
+ outline-color: #d4d4d8
+}
+
+.outline-zinc-400 {
+ outline-color: #a1a1aa
+}
+
+.outline-zinc-50 {
+ outline-color: #fafafa
+}
+
+.outline-zinc-500 {
+ outline-color: #71717a
+}
+
+.outline-zinc-600 {
+ outline-color: #52525b
+}
+
+.outline-zinc-700 {
+ outline-color: #3f3f46
+}
+
+.outline-zinc-800 {
+ outline-color: #27272a
+}
+
+.outline-zinc-900 {
+ outline-color: #18181b
+}
+
+.\!ring-0 {
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color)!important;
+ box-shadow: var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important
+}
+
+.ring {
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)
+}
+
+.ring,.ring-0 {
+ box-shadow: var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)
+}
+
+.ring-0 {
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color)
+}
+
+.ring-1 {
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)
+}
+
+.ring-1,.ring-2 {
+ box-shadow: var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)
+}
+
+.ring-2 {
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)
+}
+
+.ring-inset {
+ --tw-ring-inset: inset
+}
+
+.ring-amber-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(254 243 199/var(--tw-ring-opacity))
+}
+
+.ring-amber-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(253 230 138/var(--tw-ring-opacity))
+}
+
+.ring-amber-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(252 211 77/var(--tw-ring-opacity))
+}
+
+.ring-amber-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(251 191 36/var(--tw-ring-opacity))
+}
+
+.ring-amber-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(255 251 235/var(--tw-ring-opacity))
+}
+
+.ring-amber-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(245 158 11/var(--tw-ring-opacity))
+}
+
+.ring-amber-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(217 119 6/var(--tw-ring-opacity))
+}
+
+.ring-amber-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(180 83 9/var(--tw-ring-opacity))
+}
+
+.ring-amber-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(146 64 14/var(--tw-ring-opacity))
+}
+
+.ring-amber-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(120 53 15/var(--tw-ring-opacity))
+}
+
+.ring-black {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(17 17 17/var(--tw-ring-opacity))
+}
+
+.ring-blue-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(219 234 254/var(--tw-ring-opacity))
+}
+
+.ring-blue-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(191 219 254/var(--tw-ring-opacity))
+}
+
+.ring-blue-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(147 197 253/var(--tw-ring-opacity))
+}
+
+.ring-blue-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(96 165 250/var(--tw-ring-opacity))
+}
+
+.ring-blue-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(239 246 255/var(--tw-ring-opacity))
+}
+
+.ring-blue-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(59 130 246/var(--tw-ring-opacity))
+}
+
+.ring-blue-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(37 99 235/var(--tw-ring-opacity))
+}
+
+.ring-blue-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(29 78 216/var(--tw-ring-opacity))
+}
+
+.ring-blue-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(30 64 175/var(--tw-ring-opacity))
+}
+
+.ring-blue-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(30 58 138/var(--tw-ring-opacity))
+}
+
+.ring-cyan-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(207 250 254/var(--tw-ring-opacity))
+}
+
+.ring-cyan-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(165 243 252/var(--tw-ring-opacity))
+}
+
+.ring-cyan-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(103 232 249/var(--tw-ring-opacity))
+}
+
+.ring-cyan-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(34 211 238/var(--tw-ring-opacity))
+}
+
+.ring-cyan-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(236 254 255/var(--tw-ring-opacity))
+}
+
+.ring-cyan-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(6 182 212/var(--tw-ring-opacity))
+}
+
+.ring-cyan-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(8 145 178/var(--tw-ring-opacity))
+}
+
+.ring-cyan-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(14 116 144/var(--tw-ring-opacity))
+}
+
+.ring-cyan-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(21 94 117/var(--tw-ring-opacity))
+}
+
+.ring-cyan-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(22 78 99/var(--tw-ring-opacity))
+}
+
+.ring-emerald-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(209 250 229/var(--tw-ring-opacity))
+}
+
+.ring-emerald-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(167 243 208/var(--tw-ring-opacity))
+}
+
+.ring-emerald-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(110 231 183/var(--tw-ring-opacity))
+}
+
+.ring-emerald-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(52 211 153/var(--tw-ring-opacity))
+}
+
+.ring-emerald-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(236 253 245/var(--tw-ring-opacity))
+}
+
+.ring-emerald-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(16 185 129/var(--tw-ring-opacity))
+}
+
+.ring-emerald-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(5 150 105/var(--tw-ring-opacity))
+}
+
+.ring-emerald-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(4 120 87/var(--tw-ring-opacity))
+}
+
+.ring-emerald-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(6 95 70/var(--tw-ring-opacity))
+}
+
+.ring-emerald-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(6 78 59/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(250 232 255/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(245 208 254/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(240 171 252/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(232 121 249/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(253 244 255/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(217 70 239/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(192 38 211/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(162 28 175/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(134 25 143/var(--tw-ring-opacity))
+}
+
+.ring-fuchsia-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(112 26 117/var(--tw-ring-opacity))
+}
+
+.ring-gray-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(243 244 246/var(--tw-ring-opacity))
+}
+
+.ring-gray-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(229 231 235/var(--tw-ring-opacity))
+}
+
+.ring-gray-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(209 213 219/var(--tw-ring-opacity))
+}
+
+.ring-gray-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(156 163 175/var(--tw-ring-opacity))
+}
+
+.ring-gray-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(249 250 251/var(--tw-ring-opacity))
+}
+
+.ring-gray-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(107 114 128/var(--tw-ring-opacity))
+}
+
+.ring-gray-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(75 85 99/var(--tw-ring-opacity))
+}
+
+.ring-gray-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(55 65 81/var(--tw-ring-opacity))
+}
+
+.ring-gray-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(31 41 55/var(--tw-ring-opacity))
+}
+
+.ring-gray-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(17 24 39/var(--tw-ring-opacity))
+}
+
+.ring-green-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(220 252 231/var(--tw-ring-opacity))
+}
+
+.ring-green-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(187 247 208/var(--tw-ring-opacity))
+}
+
+.ring-green-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(134 239 172/var(--tw-ring-opacity))
+}
+
+.ring-green-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(74 222 128/var(--tw-ring-opacity))
+}
+
+.ring-green-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(240 253 244/var(--tw-ring-opacity))
+}
+
+.ring-green-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(34 197 94/var(--tw-ring-opacity))
+}
+
+.ring-green-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(22 163 74/var(--tw-ring-opacity))
+}
+
+.ring-green-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(21 128 61/var(--tw-ring-opacity))
+}
+
+.ring-green-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(22 101 52/var(--tw-ring-opacity))
+}
+
+.ring-green-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(20 83 45/var(--tw-ring-opacity))
+}
+
+.ring-indigo-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(224 231 255/var(--tw-ring-opacity))
+}
+
+.ring-indigo-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(199 210 254/var(--tw-ring-opacity))
+}
+
+.ring-indigo-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(165 180 252/var(--tw-ring-opacity))
+}
+
+.ring-indigo-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(129 140 248/var(--tw-ring-opacity))
+}
+
+.ring-indigo-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(238 242 255/var(--tw-ring-opacity))
+}
+
+.ring-indigo-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(99 102 241/var(--tw-ring-opacity))
+}
+
+.ring-indigo-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(79 70 229/var(--tw-ring-opacity))
+}
+
+.ring-indigo-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(67 56 202/var(--tw-ring-opacity))
+}
+
+.ring-indigo-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(55 48 163/var(--tw-ring-opacity))
+}
+
+.ring-indigo-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(49 46 129/var(--tw-ring-opacity))
+}
+
+.ring-inverted {
+ --tw-ring-color: var(--cal-bg-inverted)
+}
+
+.ring-lime-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(236 252 203/var(--tw-ring-opacity))
+}
+
+.ring-lime-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(217 249 157/var(--tw-ring-opacity))
+}
+
+.ring-lime-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(190 242 100/var(--tw-ring-opacity))
+}
+
+.ring-lime-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(163 230 53/var(--tw-ring-opacity))
+}
+
+.ring-lime-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(247 254 231/var(--tw-ring-opacity))
+}
+
+.ring-lime-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(132 204 22/var(--tw-ring-opacity))
+}
+
+.ring-lime-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(101 163 13/var(--tw-ring-opacity))
+}
+
+.ring-lime-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(77 124 15/var(--tw-ring-opacity))
+}
+
+.ring-lime-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(63 98 18/var(--tw-ring-opacity))
+}
+
+.ring-lime-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(54 83 20/var(--tw-ring-opacity))
+}
+
+.ring-muted {
+ --tw-ring-color: var(--cal-bg-muted)
+}
+
+.ring-neutral-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(245 245 245/var(--tw-ring-opacity))
+}
+
+.ring-neutral-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(229 229 229/var(--tw-ring-opacity))
+}
+
+.ring-neutral-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(212 212 212/var(--tw-ring-opacity))
+}
+
+.ring-neutral-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(163 163 163/var(--tw-ring-opacity))
+}
+
+.ring-neutral-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(250 250 250/var(--tw-ring-opacity))
+}
+
+.ring-neutral-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(115 115 115/var(--tw-ring-opacity))
+}
+
+.ring-neutral-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(82 82 82/var(--tw-ring-opacity))
+}
+
+.ring-neutral-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(64 64 64/var(--tw-ring-opacity))
+}
+
+.ring-neutral-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(38 38 38/var(--tw-ring-opacity))
+}
+
+.ring-neutral-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(23 23 23/var(--tw-ring-opacity))
+}
+
+.ring-orange-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(255 237 213/var(--tw-ring-opacity))
+}
+
+.ring-orange-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(254 215 170/var(--tw-ring-opacity))
+}
+
+.ring-orange-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(253 186 116/var(--tw-ring-opacity))
+}
+
+.ring-orange-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(251 146 60/var(--tw-ring-opacity))
+}
+
+.ring-orange-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(255 247 237/var(--tw-ring-opacity))
+}
+
+.ring-orange-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(249 115 22/var(--tw-ring-opacity))
+}
+
+.ring-orange-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(234 88 12/var(--tw-ring-opacity))
+}
+
+.ring-orange-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(194 65 12/var(--tw-ring-opacity))
+}
+
+.ring-orange-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(154 52 18/var(--tw-ring-opacity))
+}
+
+.ring-orange-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(124 45 18/var(--tw-ring-opacity))
+}
+
+.ring-pink-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(252 231 243/var(--tw-ring-opacity))
+}
+
+.ring-pink-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(251 207 232/var(--tw-ring-opacity))
+}
+
+.ring-pink-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(249 168 212/var(--tw-ring-opacity))
+}
+
+.ring-pink-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(244 114 182/var(--tw-ring-opacity))
+}
+
+.ring-pink-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(253 242 248/var(--tw-ring-opacity))
+}
+
+.ring-pink-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(236 72 153/var(--tw-ring-opacity))
+}
+
+.ring-pink-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(219 39 119/var(--tw-ring-opacity))
+}
+
+.ring-pink-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(190 24 93/var(--tw-ring-opacity))
+}
+
+.ring-pink-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(131 24 67/var(--tw-ring-opacity))
+}
+
+.ring-purple-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(243 232 255/var(--tw-ring-opacity))
+}
+
+.ring-purple-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(233 213 255/var(--tw-ring-opacity))
+}
+
+.ring-purple-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(216 180 254/var(--tw-ring-opacity))
+}
+
+.ring-purple-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(192 132 252/var(--tw-ring-opacity))
+}
+
+.ring-purple-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(250 245 255/var(--tw-ring-opacity))
+}
+
+.ring-purple-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(168 85 247/var(--tw-ring-opacity))
+}
+
+.ring-purple-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(147 51 234/var(--tw-ring-opacity))
+}
+
+.ring-purple-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(126 34 206/var(--tw-ring-opacity))
+}
+
+.ring-purple-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(107 33 168/var(--tw-ring-opacity))
+}
+
+.ring-purple-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(88 28 135/var(--tw-ring-opacity))
+}
+
+.ring-red-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(254 226 226/var(--tw-ring-opacity))
+}
+
+.ring-red-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(254 202 202/var(--tw-ring-opacity))
+}
+
+.ring-red-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(252 165 165/var(--tw-ring-opacity))
+}
+
+.ring-red-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(248 113 113/var(--tw-ring-opacity))
+}
+
+.ring-red-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(254 242 242/var(--tw-ring-opacity))
+}
+
+.ring-red-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(239 68 68/var(--tw-ring-opacity))
+}
+
+.ring-red-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(220 38 38/var(--tw-ring-opacity))
+}
+
+.ring-red-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(185 28 28/var(--tw-ring-opacity))
+}
+
+.ring-red-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(153 27 27/var(--tw-ring-opacity))
+}
+
+.ring-red-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(127 29 29/var(--tw-ring-opacity))
+}
+
+.ring-rose-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(255 228 230/var(--tw-ring-opacity))
+}
+
+.ring-rose-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(254 205 211/var(--tw-ring-opacity))
+}
+
+.ring-rose-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(253 164 175/var(--tw-ring-opacity))
+}
+
+.ring-rose-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(251 113 133/var(--tw-ring-opacity))
+}
+
+.ring-rose-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(255 241 242/var(--tw-ring-opacity))
+}
+
+.ring-rose-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(244 63 94/var(--tw-ring-opacity))
+}
+
+.ring-rose-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(225 29 72/var(--tw-ring-opacity))
+}
+
+.ring-rose-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(190 18 60/var(--tw-ring-opacity))
+}
+
+.ring-rose-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(159 18 57/var(--tw-ring-opacity))
+}
+
+.ring-rose-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(136 19 55/var(--tw-ring-opacity))
+}
+
+.ring-sky-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(224 242 254/var(--tw-ring-opacity))
+}
+
+.ring-sky-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(186 230 253/var(--tw-ring-opacity))
+}
+
+.ring-sky-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(125 211 252/var(--tw-ring-opacity))
+}
+
+.ring-sky-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(56 189 248/var(--tw-ring-opacity))
+}
+
+.ring-sky-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(240 249 255/var(--tw-ring-opacity))
+}
+
+.ring-sky-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(14 165 233/var(--tw-ring-opacity))
+}
+
+.ring-sky-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(2 132 199/var(--tw-ring-opacity))
+}
+
+.ring-sky-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(3 105 161/var(--tw-ring-opacity))
+}
+
+.ring-sky-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(7 89 133/var(--tw-ring-opacity))
+}
+
+.ring-sky-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(12 74 110/var(--tw-ring-opacity))
+}
+
+.ring-slate-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(241 245 249/var(--tw-ring-opacity))
+}
+
+.ring-slate-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(226 232 240/var(--tw-ring-opacity))
+}
+
+.ring-slate-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(203 213 225/var(--tw-ring-opacity))
+}
+
+.ring-slate-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(148 163 184/var(--tw-ring-opacity))
+}
+
+.ring-slate-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(248 250 252/var(--tw-ring-opacity))
+}
+
+.ring-slate-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(100 116 139/var(--tw-ring-opacity))
+}
+
+.ring-slate-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(71 85 105/var(--tw-ring-opacity))
+}
+
+.ring-slate-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(51 65 85/var(--tw-ring-opacity))
+}
+
+.ring-slate-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(30 41 59/var(--tw-ring-opacity))
+}
+
+.ring-slate-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(15 23 42/var(--tw-ring-opacity))
+}
+
+.ring-stone-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(245 245 244/var(--tw-ring-opacity))
+}
+
+.ring-stone-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(231 229 228/var(--tw-ring-opacity))
+}
+
+.ring-stone-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(214 211 209/var(--tw-ring-opacity))
+}
+
+.ring-stone-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(168 162 158/var(--tw-ring-opacity))
+}
+
+.ring-stone-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(250 250 249/var(--tw-ring-opacity))
+}
+
+.ring-stone-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(120 113 108/var(--tw-ring-opacity))
+}
+
+.ring-stone-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(87 83 78/var(--tw-ring-opacity))
+}
+
+.ring-stone-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(68 64 60/var(--tw-ring-opacity))
+}
+
+.ring-stone-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(41 37 36/var(--tw-ring-opacity))
+}
+
+.ring-stone-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(28 25 23/var(--tw-ring-opacity))
+}
+
+.ring-subtle {
+ --tw-ring-color: var(--cal-bg-subtle)
+}
+
+.ring-teal-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(204 251 241/var(--tw-ring-opacity))
+}
+
+.ring-teal-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(153 246 228/var(--tw-ring-opacity))
+}
+
+.ring-teal-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(94 234 212/var(--tw-ring-opacity))
+}
+
+.ring-teal-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(45 212 191/var(--tw-ring-opacity))
+}
+
+.ring-teal-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(240 253 250/var(--tw-ring-opacity))
+}
+
+.ring-teal-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(20 184 166/var(--tw-ring-opacity))
+}
+
+.ring-teal-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(13 148 136/var(--tw-ring-opacity))
+}
+
+.ring-teal-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(15 118 110/var(--tw-ring-opacity))
+}
+
+.ring-teal-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(17 94 89/var(--tw-ring-opacity))
+}
+
+.ring-teal-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(19 78 74/var(--tw-ring-opacity))
+}
+
+.ring-transparent {
+ --tw-ring-color: transparent
+}
+
+.ring-violet-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(237 233 254/var(--tw-ring-opacity))
+}
+
+.ring-violet-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(221 214 254/var(--tw-ring-opacity))
+}
+
+.ring-violet-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(196 181 253/var(--tw-ring-opacity))
+}
+
+.ring-violet-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(167 139 250/var(--tw-ring-opacity))
+}
+
+.ring-violet-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(245 243 255/var(--tw-ring-opacity))
+}
+
+.ring-violet-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(139 92 246/var(--tw-ring-opacity))
+}
+
+.ring-violet-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(124 58 237/var(--tw-ring-opacity))
+}
+
+.ring-violet-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(109 40 217/var(--tw-ring-opacity))
+}
+
+.ring-violet-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(91 33 182/var(--tw-ring-opacity))
+}
+
+.ring-violet-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(76 29 149/var(--tw-ring-opacity))
+}
+
+.ring-white {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(255 255 255/var(--tw-ring-opacity))
+}
+
+.ring-yellow-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(254 249 195/var(--tw-ring-opacity))
+}
+
+.ring-yellow-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(254 240 138/var(--tw-ring-opacity))
+}
+
+.ring-yellow-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(253 224 71/var(--tw-ring-opacity))
+}
+
+.ring-yellow-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(250 204 21/var(--tw-ring-opacity))
+}
+
+.ring-yellow-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(254 252 232/var(--tw-ring-opacity))
+}
+
+.ring-yellow-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(234 179 8/var(--tw-ring-opacity))
+}
+
+.ring-yellow-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(202 138 4/var(--tw-ring-opacity))
+}
+
+.ring-yellow-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(161 98 7/var(--tw-ring-opacity))
+}
+
+.ring-yellow-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(133 77 14/var(--tw-ring-opacity))
+}
+
+.ring-yellow-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(113 63 18/var(--tw-ring-opacity))
+}
+
+.ring-zinc-100 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(244 244 245/var(--tw-ring-opacity))
+}
+
+.ring-zinc-200 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(228 228 231/var(--tw-ring-opacity))
+}
+
+.ring-zinc-300 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(212 212 216/var(--tw-ring-opacity))
+}
+
+.ring-zinc-400 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(161 161 170/var(--tw-ring-opacity))
+}
+
+.ring-zinc-50 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(250 250 250/var(--tw-ring-opacity))
+}
+
+.ring-zinc-500 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(113 113 122/var(--tw-ring-opacity))
+}
+
+.ring-zinc-600 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(82 82 91/var(--tw-ring-opacity))
+}
+
+.ring-zinc-700 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(63 63 70/var(--tw-ring-opacity))
+}
+
+.ring-zinc-800 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(39 39 42/var(--tw-ring-opacity))
+}
+
+.ring-zinc-900 {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(24 24 27/var(--tw-ring-opacity))
+}
+
+.ring-offset-1 {
+ --tw-ring-offset-width: 1px
+}
+
+.ring-offset-2 {
+ --tw-ring-offset-width: 2px
+}
+
+.blur {
+ --tw-blur: blur(8px)
+}
+
+.blur,.blur-\[3px\] {
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
+}
+
+.blur-\[3px\] {
+ --tw-blur: blur(3px)
+}
+
+.drop-shadow {
+ --tw-drop-shadow: drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))
+}
+
+.drop-shadow,.grayscale {
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
+}
+
+.grayscale {
+ --tw-grayscale: grayscale(100%)
+}
+
+.grayscale-0 {
+ --tw-grayscale: grayscale(0)
+}
+
+.grayscale-0,.invert {
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
+}
+
+.invert {
+ --tw-invert: invert(100%)
+}
+
+.sepia {
+ --tw-sepia: sepia(100%);
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
+}
+
+.\!filter {
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important
+}
+
+.filter {
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
+}
+
+.backdrop-blur {
+ --tw-backdrop-blur: blur(8px)
+}
+
+.backdrop-blur,.backdrop-blur-\[1px\] {
+ -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
+ backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)
+}
+
+.backdrop-blur-\[1px\] {
+ --tw-backdrop-blur: blur(1px)
+}
+
+.backdrop-blur-lg {
+ --tw-backdrop-blur: blur(16px)
+}
+
+.backdrop-blur-lg,.backdrop-blur-md {
+ -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
+ backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)
+}
+
+.backdrop-blur-md {
+ --tw-backdrop-blur: blur(12px)
+}
+
+.backdrop-grayscale {
+ --tw-backdrop-grayscale: grayscale(100%)
+}
+
+.backdrop-grayscale,.backdrop-invert {
+ -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
+ backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)
+}
+
+.backdrop-invert {
+ --tw-backdrop-invert: invert(100%)
+}
+
+.backdrop-sepia {
+ --tw-backdrop-sepia: sepia(100%)
+}
+
+.backdrop-filter,.backdrop-sepia {
+ -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
+ backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)
+}
+
+.transition {
+ transition-property: color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;
+ transition-property: color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;
+ transition-property: color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ transition-duration: .15s
+}
+
+.transition-all {
+ transition-property: all;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ transition-duration: .15s
+}
+
+.transition-colors {
+ transition-property: color,background-color,border-color,text-decoration-color,fill,stroke;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ transition-duration: .15s
+}
+
+.transition-none {
+ transition-property: none
+}
+
+.transition-opacity {
+ transition-property: opacity;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ transition-duration: .15s
+}
+
+.transition-transform {
+ transition-property: transform;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ transition-duration: .15s
+}
+
+.delay-1000 {
+ transition-delay: 1s
+}
+
+.duration-100 {
+ transition-duration: .1s
+}
+
+.duration-150 {
+ transition-duration: .15s
+}
+
+.duration-200 {
+ transition-duration: .2s
+}
+
+.duration-300 {
+ transition-duration: .3s
+}
+
+.ease-in {
+ transition-timing-function: cubic-bezier(.4,0,1,1)
+}
+
+.ease-in-out {
+ transition-timing-function: cubic-bezier(.4,0,.2,1)
+}
+
+.ease-out {
+ transition-timing-function: cubic-bezier(0,0,.2,1)
+}
+
+.will-change-transform {
+ will-change: transform
+}
+
+.scrollbar {
+ --scrollbar-track: initial;
+ --scrollbar-thumb: initial;
+ --scrollbar-corner: initial;
+ --scrollbar-track-hover: var(--scrollbar-track);
+ --scrollbar-thumb-hover: var(--scrollbar-thumb);
+ --scrollbar-corner-hover: var(--scrollbar-corner);
+ --scrollbar-track-active: var(--scrollbar-track-hover);
+ --scrollbar-thumb-active: var(--scrollbar-thumb-hover);
+ --scrollbar-corner-active: var(--scrollbar-corner-hover);
+ scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
+ overflow: overlay
+}
+
+.scrollbar.overflow-x-hidden {
+ overflow-x: hidden
+}
+
+.scrollbar.overflow-y-hidden {
+ overflow-y: hidden
+}
+
+.scrollbar::-webkit-scrollbar-track {
+ background-color: var(--scrollbar-track)
+}
+
+.scrollbar::-webkit-scrollbar-thumb {
+ background-color: var(--scrollbar-thumb)
+}
+
+.scrollbar::-webkit-scrollbar-corner {
+ background-color: var(--scrollbar-corner)
+}
+
+.scrollbar::-webkit-scrollbar-track:hover {
+ background-color: var(--scrollbar-track-hover)
+}
+
+.scrollbar::-webkit-scrollbar-thumb:hover {
+ background-color: var(--scrollbar-thumb-hover)
+}
+
+.scrollbar::-webkit-scrollbar-corner:hover {
+ background-color: var(--scrollbar-corner-hover)
+}
+
+.scrollbar::-webkit-scrollbar-track:active {
+ background-color: var(--scrollbar-track-active)
+}
+
+.scrollbar::-webkit-scrollbar-thumb:active {
+ background-color: var(--scrollbar-thumb-active)
+}
+
+.scrollbar::-webkit-scrollbar-corner:active {
+ background-color: var(--scrollbar-corner-active)
+}
+
+.scrollbar {
+ scrollbar-width: auto
+}
+
+.scrollbar::-webkit-scrollbar {
+ width: 16px;
+ height: 16px
+}
+
+.scrollbar-thin.overflow-x-hidden {
+ overflow-x: hidden
+}
+
+.scrollbar-thin.overflow-y-hidden {
+ overflow-y: hidden
+}
+
+@keyframes enter {
+ 0% {
+ opacity: var(--tw-enter-opacity,1);
+ transform: translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0) scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1)) rotate(var(--tw-enter-rotate,0))
+ }
+}
+
+@keyframes exit {
+ to {
+ opacity: var(--tw-exit-opacity,1);
+ transform: translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0) scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1)) rotate(var(--tw-exit-rotate,0))
+ }
+}
+
+.animate-in {
+ animation-name: enter;
+ animation-duration: .15s;
+ --tw-enter-opacity: initial;
+ --tw-enter-scale: initial;
+ --tw-enter-rotate: initial;
+ --tw-enter-translate-x: initial;
+ --tw-enter-translate-y: initial
+}
+
+.zoom-in {
+ --tw-enter-scale: 0
+}
+
+.zoom-out {
+ --tw-exit-scale: 0
+}
+
+.slide-in-from-bottom {
+ --tw-enter-translate-y: 100%
+}
+
+.slide-in-from-left {
+ --tw-enter-translate-x: -100%
+}
+
+.slide-in-from-right {
+ --tw-enter-translate-x: 100%
+}
+
+.slide-in-from-top {
+ --tw-enter-translate-y: -100%
+}
+
+.duration-100 {
+ animation-duration: .1s
+}
+
+.duration-150 {
+ animation-duration: .15s
+}
+
+.duration-200 {
+ animation-duration: .2s
+}
+
+.duration-300 {
+ animation-duration: .3s
+}
+
+.delay-1000 {
+ animation-delay: 1s
+}
+
+.ease-in {
+ animation-timing-function: cubic-bezier(.4,0,1,1)
+}
+
+.ease-in-out {
+ animation-timing-function: cubic-bezier(.4,0,.2,1)
+}
+
+.ease-out {
+ animation-timing-function: cubic-bezier(0,0,.2,1)
+}
+
+.running {
+ animation-play-state: running
+}
+
+.no-scrollbar::-webkit-scrollbar {
+ display: none
+}
+
+.no-scrollbar {
+ -ms-overflow-style: none;
+ scrollbar-width: none
+}
+
+.\[--booker-main-width\: 420px\] {
+ --booker-main-width:420px
+}
+
+.\[--booker-main-width\: 480px\] {
+ --booker-main-width:480px;
+}
+
+.\[--booker-meta-width\: 240px\] {
+ --booker-meta-width:240px
+}
+
+.\[--booker-meta-width\: 340px\] {
+ --booker-meta-width:340px
+}
+
+.\[--booker-timeslots-width\: 240px\] {
+ --booker-timeslots-width:240px
+}
+
+.\[--cal-brand-accent\: \#fafafa\] {
+ --cal-brand-accent:#fafafa
+}
+
+.\[--cal-brand-emphasis\: \#101010\] {
+ --cal-brand-emphasis:#101010
+}
+
+.\[--cal-brand-subtle\: \#9CA3AF\] {
+ --cal-brand-subtle:#9ca3af
+}
+
+.\[--cal-brand-text\: \#FFFFFF\],.\[--cal-brand-text\:white\] {
+ --cal-brand-text:#fff
+}
+
+.\[--cal-brand\: \#111827\] {
+ --cal-brand:#111827
+}
+
+.\[--calendar-dates-sticky-offset\: 66px\] {
+ --calendar-dates-sticky-offset:66px
+}
+
+.\[--disabled-gradient-background\: \#E5E7EB\] {
+ --disabled-gradient-background:#e5e7eb
+}
+
+.\[--disabled-gradient-background\: \#F8F9FB\] {
+ --disabled-gradient-background:#f8f9fb
+}
+
+.\[--disabled-gradient-foreground\: \#D1D5DB\] {
+ --disabled-gradient-foreground:#d1d5db
+}
+
+.\[--disabled-gradient-foreground\: \#E6E7EB\] {
+ --disabled-gradient-foreground:#e6e7eb
+}
+
+.\[--troublehooster-meta-width\: 0px\] {
+ --troublehooster-meta-width:0px
+}
+
+.\[--troublehooster-meta-width\: 250px\] {
+ --troublehooster-meta-width:250px
+}
+
+.\[-webkit-app-region\:no-drag\] {
+ -webkit-app-region: no-drag
+}
+
+.\[appearance\:textfield\] {
+ -webkit-appearance: textfield;
+ -moz-appearance: textfield;
+ appearance: textfield
+}
+
+.\[filter\:invert\(0\.5\)_brightness\(0\.5\)\] {
+ filter: invert(.5) brightness(.5)
+}
+
+.\[grid-area\:calendar\] {
+ grid-area: calendar
+}
+
+.\[grid-area\:header\] {
+ grid-area: header
+}
+
+.\[grid-area\:main\] {
+ grid-area: main
+}
+
+.\[grid-area\:meta\] {
+ grid-area: meta
+}
+
+.\[grid-area\:timeslots\] {
+ grid-area: timeslots
+}
+
+:root {
+ --cal-bg-emphasis: #e5e7eb;
+ --cal-bg: #fff;
+ --cal-bg-subtle: #f3f4f6;
+ --cal-bg-muted: #f9fafb;
+ --cal-bg-inverted: #0f0f0f;
+ --cal-bg-info: #f6f9fe;
+ --cal-bg-success: #e4fbe9;
+ --cal-bg-attention: #fcefd9;
+ --cal-bg-error: hsla(3,66,93,1);
+ --cal-bg-dark-error: #772522;
+ --cal-border-emphasis: #9ca3b0;
+ --cal-border: #d1d5db;
+ --cal-border-subtle: #e5e7eb;
+ --cal-border-booker: #e5e7eb;
+ --cal-border-muted: #f3f4f6;
+ --cal-border-error: #aa2f27;
+ --cal-text-emphasis: #384252;
+ --cal-text: #384252;
+ --cal-text-subtle: #6b7280;
+ --cal-text-muted: #9ca3b0;
+ --cal-text-inverted: #fff;
+ --cal-text-info: #253883;
+ --cal-text-success: #285231;
+ --cal-text-attention: #74331b;
+ --cal-text-error: #772522;
+ --cal-brand: #111827;
+ --cal-brand-emphasis: #0f0f0f;
+ --cal-brand-text: #fff
+}
+
+.dark {
+ --cal-bg-emphasis: #404040;
+ --cal-bg: #1a1a1a;
+ --cal-bg-subtle: #2e2e2e;
+ --cal-bg-muted: #1f1f1f;
+ --cal-bg-inverted: #f3f4f6;
+ --cal-bg-info: #253883;
+ --cal-bg-success: #285231;
+ --cal-bg-attention: #74331b;
+ --cal-bg-error: #772522;
+ --cal-bg-dark-error: #772522;
+ --cal-border-emphasis: #757575;
+ --cal-border: #575757;
+ --cal-border-subtle: #383838;
+ --cal-border-booker: #383838;
+ --cal-border-muted: #2e2e2e;
+ --cal-border-error: #aa2f27;
+ --cal-text-emphasis: #fcfcfd;
+ --cal-text: #d6d6d6;
+ --cal-text-subtle: #a6a6a6;
+ --cal-text-muted: #575757;
+ --cal-text-inverted: #1a1a1a;
+ --cal-text-info: #dee9fc;
+ --cal-text-success: #e4fbe9;
+ --cal-text-attention: #fcefd9;
+ --cal-text-error: #f9e3e1;
+ --cal-brand: #fff;
+ --cal-brand-emphasis: #9ca3b0;
+ --cal-brand-text: #000
+}
+
+::-moz-selection {
+ color: var(--cal-brand-text);
+ background: var(--cal-brand)
+}
+
+::selection {
+ color: var(--cal-brand-text);
+ background: var(--cal-brand)
+}
+
+body {
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased
+}
+
+html.todesktop a,html.todesktop button,html.todesktop header,html.todesktop img {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ cursor: default!important
+}
+
+html.todesktop,html.todesktop div {
+ cursor: default!important
+}
+
+html.todesktop header {
+ -webkit-app-region: drag
+}
+
+html.todesktop header a,html.todesktop header button {
+ -webkit-app-region: no-drag
+}
+
+html.todesktop-platform-darwin.dark main.bg-default {
+ background: rgba(0,0,0,.6)!important
+}
+
+html.todesktop-platform-darwin.light main.bg-default {
+ background: hsla(0,0%,100%,.8)!important
+}
+
+html.todesktop.light {
+ --cal-bg-emphasis: rgba(28,28,28,.1)
+}
+
+html.todesktop.dark {
+ --cal-bg-emphasis: rgba(65,66,68,.3)
+}
+
+[multiple]:focus,[type=checkbox]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=radio]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus {
+ --tw-ring-color: var(--brand-color);
+ border-color: var(--brand-color)
+}
+
+@media only screen and (max-width: 768px) {
+ #launcher,div[role=presentation]>iframe {
+ display:none!important
+ }
+
+ .BeaconFabButtonFrame {
+ margin-left: -30px;
+ left: 50%;
+ bottom: 28px!important;
+ z-index: 1058!important
+ }
+}
+
+.DateRangePickerInput__withBorder {
+ border: 0!important
+}
+
+.DateInput_input {
+ border: 1px solid #d1d5db!important;
+ border-radius: 2px!important;
+ font-size: inherit!important;
+ font-weight: inherit!important;
+ color: #000;
+ padding: 11px â11px 9px!important;
+ line-height: 16px!important
+}
+
+.DateInput_input__focused {
+ border: 2px solid #000!important;
+ border-radius: 2px!important;
+ box-shadow: none!important;
+ padding: 10px â10px 9px!important
+}
+
+.DateRangePickerInput_arrow {
+ padding: 0 10px
+}
+
+.loader {
+ display: block;
+ width: 30px;
+ height: 30px;
+ margin: 60px auto;
+ position: relative;
+ border-width: 4px;
+ border-style: solid;
+ animation: loader 2s ease infinite
+}
+
+.loader-inner {
+ vertical-align: top;
+ display: inline-block;
+ width: 100%;
+ animation: loader-inner 2s ease-in infinite
+}
+
+.no-ring-inset {
+ --tw-ring-inset: unset
+}
+
+@keyframes loader {
+ 0% {
+ transform: rotate(0deg)
+ }
+
+ 25% {
+ transform: rotate(180deg)
+ }
+
+ 50% {
+ transform: rotate(180deg)
+ }
+
+ 75% {
+ transform: rotate(1turn)
+ }
+
+ to {
+ transform: rotate(1turn)
+ }
+}
+
+@keyframes loader-inner {
+ 0% {
+ height: 0%
+ }
+
+ 25% {
+ height: 0%
+ }
+
+ 50% {
+ height: 100%
+ }
+
+ 75% {
+ height: 100%
+ }
+
+ to {
+ height: 0%
+ }
+}
+
+.text-inverted-important {
+ color: #fff!important
+}
+
+#timeZone input:focus {
+ box-shadow: none
+}
+
+.react-date-picker__wrapper {
+ border: none!important
+}
+
+.react-date-picker__inputGroup__input {
+ padding-top: 0;
+ padding-bottom: 0
+}
+
+.slideInBottom {
+ animation-duration: .3s;
+ animation-fill-mode: both;
+ animation-name: slideInBottom
+}
+
+@keyframes slideInBottom {
+ 0% {
+ opacity: 0;
+ transform: translateY(30%);
+ pointer-events: none
+ }
+
+ to {
+ opacity: 1;
+ pointer-events: auto
+ }
+}
+
+.slideInTop {
+ animation-duration: .3s;
+ animation-fill-mode: both;
+ animation-name: slideInTop
+}
+
+@keyframes slideInTop {
+ 0% {
+ opacity: 0;
+ transform: translateY(-20%);
+ pointer-events: none
+ }
+
+ to {
+ opacity: 1;
+ pointer-events: auto;
+ transform: translateY(0)
+ }
+}
+
+.fadeIn {
+ animation-duration: .3s;
+ animation-fill-mode: both;
+ animation-name: fadeIn;
+ animation-timing-function: ease-in-out
+}
+
+@keyframes fadeIn {
+ 0% {
+ opacity: 0
+ }
+
+ to {
+ opacity: 1
+ }
+}
+
+@supports(-webkit-touch-callout:none) {
+ .h-screen {
+ height: -webkit-fill-available
+ }
+}
+
+::-webkit-search-cancel-button {
+ -webkit-appearance: none
+}
+
+.react-tel-input .country-list .country.highlight,.react-tel-input .country-list .country:hover {
+ background-color: var(--cal-bg-emphasis)!important
+}
+
+.react-tel-input .flag-dropdown .selected-flag,.react-tel-input .flag-dropdown.open .selected-flag {
+ background-color: var(--cal-bg,#fff)!important
+}
+
+.react-tel-input .flag-dropdown {
+ left: .125rem;
+ border-top-width: 0!important;
+ border-bottom-width: 0!important;
+ border-left-width: 0!important;
+ border-right-color: var(--cal-border,#d1d5db)!important
+}
+
+.first-letter\:text-xs:first-letter {
+ font-size: .75rem;
+ line-height: 1rem
+}
+
+.selection\:bg-subtle ::-moz-selection {
+ background-color: var(--cal-bg-subtle)
+}
+
+.selection\:bg-subtle ::selection {
+ background-color: var(--cal-bg-subtle)
+}
+
+.selection\:bg-subtle::-moz-selection {
+ background-color: var(--cal-bg-subtle)
+}
+
+.selection\:bg-subtle::selection {
+ background-color: var(--cal-bg-subtle)
+}
+
+.placeholder\:text-sm::-moz-placeholder {
+ font-size: .875rem;
+ line-height: 1.25rem
+}
+
+.placeholder\:text-sm::placeholder {
+ font-size: .875rem;
+ line-height: 1.25rem
+}
+
+.placeholder\:font-normal::-moz-placeholder {
+ font-weight: 400
+}
+
+.placeholder\:font-normal::placeholder {
+ font-weight: 400
+}
+
+.placeholder\:text-gray-500::-moz-placeholder {
+ --tw-text-opacity: 1;
+ color: rgb(107 114 128/var(--tw-text-opacity))
+}
+
+.placeholder\:text-gray-500::placeholder {
+ --tw-text-opacity: 1;
+ color: rgb(107 114 128/var(--tw-text-opacity))
+}
+
+.placeholder\:text-muted::-moz-placeholder {
+ color: var(--cal-text-muted,#9ca3af)
+}
+
+.placeholder\:text-muted::placeholder {
+ color: var(--cal-text-muted,#9ca3af)
+}
+
+.placeholder\:text-subtle::-moz-placeholder {
+ color: var(--cal-text-subtle,#6b7280)
+}
+
+.placeholder\:text-subtle::placeholder {
+ color: var(--cal-text-subtle,#6b7280)
+}
+
+.before\:pointer-events-none:before {
+ content: var(--tw-content);
+ pointer-events: none
+}
+
+.before\:absolute:before {
+ content: var(--tw-content);
+ position: absolute
+}
+
+.before\:inset-0:before {
+ content: var(--tw-content);
+ inset: 0
+}
+
+.before\:bottom-\[-3px\]:before {
+ content: var(--tw-content);
+ bottom: -3px
+}
+
+.before\:bottom-\[-5px\]:before {
+ content: var(--tw-content);
+ bottom: -5px
+}
+
+.before\:left-\[-30px\]:before {
+ content: var(--tw-content);
+ left: -30px
+}
+
+.before\:top-\[-3px\]:before {
+ content: var(--tw-content);
+ top: -3px
+}
+
+.before\:top-\[-5px\]:before {
+ content: var(--tw-content);
+ top: -5px
+}
+
+.before\:w-\[calc\(100\%_\+_35px\)\]:before {
+ content: var(--tw-content);
+ width: calc(100% + 35px)
+}
+
+.before\:rounded-md:before {
+ content: var(--tw-content);
+ border-radius: .375rem
+}
+
+.before\:border-0:before {
+ content: var(--tw-content);
+ border-width: 0
+}
+
+.before\:bg-default:before {
+ content: var(--tw-content);
+ background-color: var(--cal-bg,#fff)
+}
+
+.before\:bg-subtle:before {
+ content: var(--tw-content);
+ background-color: var(--cal-bg-subtle)
+}
+
+.before\:py-3:before {
+ content: var(--tw-content);
+ padding-top: .75rem;
+ padding-bottom: .75rem
+}
+
+.before\:opacity-0:before {
+ content: var(--tw-content);
+ opacity: 0
+}
+
+.before\:transition-opacity:before {
+ content: var(--tw-content);
+ transition-property: opacity;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ transition-duration: .15s
+}
+
+.after\:block:after {
+ content: var(--tw-content);
+ display: block
+}
+
+.after\:h-2:after {
+ content: var(--tw-content);
+ height: .5rem
+}
+
+.after\:h-\[6px\]:after {
+ content: var(--tw-content);
+ height: 6px
+}
+
+.after\:w-2:after {
+ content: var(--tw-content);
+ width: .5rem
+}
+
+.after\:w-\[6px\]:after {
+ content: var(--tw-content);
+ width: 6px
+}
+
+.after\:rounded-full:after {
+ content: var(--tw-content);
+ border-radius: 9999px
+}
+
+.after\:bg-default:after {
+ content: var(--tw-content);
+ background-color: var(--cal-bg,#fff)
+}
+
+.after\:bg-inverted:after {
+ content: var(--tw-content);
+ background-color: var(--cal-bg-inverted)
+}
+
+.after\:bg-muted:after {
+ content: var(--tw-content);
+ background-color: var(--cal-bg-muted)
+}
+
+
+*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-inter),ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:Roboto Mono,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af}input::placeholder,textarea::placeholder{color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}select{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9J25vbmUnIHZpZXdCb3g9JzAgMCAyMCAyMCc+PHBhdGggc3Ryb2tlPScjNkI3MjgwJyBzdHJva2UtbGluZWNhcD0ncm91bmQnIHN0cm9rZS1saW5lam9pbj0ncm91bmQnIHN0cm9rZS13aWR0aD0nMS41JyBkPSdNNiA4bDQgNCA0LTQnLz48L3N2Zz4=);background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMTYgMTYnIGZpbGw9J3doaXRlJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxwYXRoIGQ9J00xMi4yMDcgNC43OTNhMSAxIDAgMDEwIDEuNDE0bC01IDVhMSAxIDAgMDEtMS40MTQgMGwtMi0yYTEgMSAwIDAxMS40MTQtMS40MTRMNi41IDkuMDg2bDQuMjkzLTQuMjkzYTEgMSAwIDAxMS40MTQgMHonLz48L3N2Zz4=)}[type=radio]:checked{background-image:url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMTYgMTYnIGZpbGw9J3doaXRlJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxjaXJjbGUgY3g9JzgnIGN5PSc4JyByPSczJy8+PC9zdmc+)}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9J25vbmUnIHZpZXdCb3g9JzAgMCAxNiAxNic+PHBhdGggc3Ryb2tlPSd3aGl0ZScgc3Ryb2tlLWxpbmVjYXA9J3JvdW5kJyBzdHJva2UtbGluZWpvaW49J3JvdW5kJyBzdHJva2Utd2lkdGg9JzInIGQ9J000IDhoOCcvPjwvc3ZnPg==);border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*{scrollbar-color:auto;scrollbar-width:auto}*{border-color:var(--cal-border,#d1d5db)}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where([class~=lead]):not(:where([class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-left:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=As]):not(:where([class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=as]):not(:where([class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=Is]):not(:where([class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=is]):not(:where([class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-left:1.625em}.prose :where(ol>li):not(:where([class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(hr):not(:where([class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-left-width:.25rem;border-left-color:var(--tw-prose-quote-borders);quotes:"\201C""\201D""\2018""\2019";margin-top:1.6em;margin-bottom:1.6em;padding-left:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(figure>*):not(:where([class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose :where(code):not(:where([class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding:.8571429em 1.1428571em}.prose :where(pre code):not(:where([class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose] *)){width:100%;table-layout:auto;text-align:left;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-right:.5714286em;padding-bottom:.5714286em;padding-left:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose] *)){vertical-align:top}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:rgba(0,0,0,.5);--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:1rem;line-height:1.75}.prose :where(p):not(:where([class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(video):not(:where([class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(figure):not(:where([class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(li):not(:where([class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose] *)){padding-left:.375em}.prose :where(ul>li):not(:where([class~=not-prose] *)){padding-left:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(hr+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose] *)){padding-left:0}.prose :where(thead th:last-child):not(:where([class~=not-prose] *)){padding-right:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose] *)){padding:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose] *)){padding-left:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose] *)){padding-right:0}.prose :where(.prose>:first-child):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-left:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(video):not(:where([class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure):not(:where([class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(code):not(:where([class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding:.6666667em 1em}.prose-sm :where(ol):not(:where([class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-left:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-left:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose] *)){padding-left:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose] *)){padding-left:.4285714em}.prose-sm :where(.prose>ul>li p):not(:where([class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose] *)){padding-right:1em;padding-bottom:.6666667em;padding-left:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose] *)){padding-left:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose] *)){padding-right:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose] *)){padding:.6666667em 1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose] *)){padding-left:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose] *)){padding-right:0}.prose-sm :where(.prose>:first-child):not(:where([class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose>:last-child):not(:where([class~=not-prose] *)){margin-bottom:0}.prose-base :where(.prose>ul>li p):not(:where([class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose-base :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.25em}.prose-base :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.25em}.prose-base :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.25em}.prose-base :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.25em}.prose-base :where(.prose>:first-child):not(:where([class~=not-prose] *)){margin-top:0}.prose-base :where(.prose>:last-child):not(:where([class~=not-prose] *)){margin-bottom:0}.prose-lg :where(.prose>ul>li p):not(:where([class~=not-prose] *)){margin-top:.8888889em;margin-bottom:.8888889em}.prose-lg :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.3333333em}.prose-lg :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.3333333em}.prose-lg :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.3333333em}.prose-lg :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.3333333em}.prose-lg :where(.prose>:first-child):not(:where([class~=not-prose] *)){margin-top:0}.prose-lg :where(.prose>:last-child):not(:where([class~=not-prose] *)){margin-bottom:0}.prose-xl :where(.prose>ul>li p):not(:where([class~=not-prose] *)){margin-top:.8em;margin-bottom:.8em}.prose-xl :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.2em}.prose-xl :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.2em}.prose-xl :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.2em}.prose-xl :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.2em}.prose-xl :where(.prose>:first-child):not(:where([class~=not-prose] *)){margin-top:0}.prose-xl :where(.prose>:last-child):not(:where([class~=not-prose] *)){margin-bottom:0}.prose-2xl :where(.prose>ul>li p):not(:where([class~=not-prose] *)){margin-top:.8333333em;margin-bottom:.8333333em}.prose-2xl :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.3333333em}.prose-2xl :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.3333333em}.prose-2xl :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.3333333em}.prose-2xl :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.3333333em}.prose-2xl :where(.prose>:first-child):not(:where([class~=not-prose] *)){margin-top:0}.prose-2xl :where(.prose>:last-child):not(:where([class~=not-prose] *)){margin-bottom:0}.prose-invert{--tw-prose-body:var(--tw-prose-invert-body);--tw-prose-headings:var(--tw-prose-invert-headings);--tw-prose-lead:var(--tw-prose-invert-lead);--tw-prose-links:var(--tw-prose-invert-links);--tw-prose-bold:var(--tw-prose-invert-bold);--tw-prose-counters:var(--tw-prose-invert-counters);--tw-prose-bullets:var(--tw-prose-invert-bullets);--tw-prose-hr:var(--tw-prose-invert-hr);--tw-prose-quotes:var(--tw-prose-invert-quotes);--tw-prose-quote-borders:var(--tw-prose-invert-quote-borders);--tw-prose-captions:var(--tw-prose-invert-captions);--tw-prose-code:var(--tw-prose-invert-code);--tw-prose-pre-code:var(--tw-prose-invert-pre-code);--tw-prose-pre-bg:var(--tw-prose-invert-pre-bg);--tw-prose-th-borders:var(--tw-prose-invert-th-borders);--tw-prose-td-borders:var(--tw-prose-invert-td-borders)}.scroll-bar{--scrollbar-track:initial;--scrollbar-thumb:initial;--scrollbar-corner:initial;--scrollbar-track-hover:var(--scrollbar-track);--scrollbar-thumb-hover:var(--scrollbar-thumb);--scrollbar-corner-hover:var(--scrollbar-corner);--scrollbar-track-active:var(--scrollbar-track-hover);--scrollbar-thumb-active:var(--scrollbar-thumb-hover);--scrollbar-corner-active:var(--scrollbar-corner-hover);scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track);overflow:overlay}.scroll-bar.overflow-x-hidden{overflow-x:hidden}.scroll-bar.overflow-y-hidden{overflow-y:hidden}.scroll-bar::-webkit-scrollbar-track{background-color:var(--scrollbar-track)}.scroll-bar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb)}.scroll-bar::-webkit-scrollbar-corner{background-color:var(--scrollbar-corner)}.scroll-bar::-webkit-scrollbar-track:hover{background-color:var(--scrollbar-track-hover)}.scroll-bar::-webkit-scrollbar-thumb:hover{background-color:var(--scrollbar-thumb-hover)}.scroll-bar::-webkit-scrollbar-corner:hover{background-color:var(--scrollbar-corner-hover)}.scroll-bar::-webkit-scrollbar-track:active{background-color:var(--scrollbar-track-active)}.scroll-bar::-webkit-scrollbar-thumb:active{background-color:var(--scrollbar-thumb-active)}.scroll-bar::-webkit-scrollbar-corner:active{background-color:var(--scrollbar-corner-active)}.scroll-bar{scrollbar-width:thin}.scroll-bar::-webkit-scrollbar{width:8px;height:8px}.scroll-bar{--scrollbar-track:transparent;--scrollbar-thumb:#d1d5db}.scroll-bar::-webkit-scrollbar-thumb{border-radius:.375rem}:is(.dark .scroll-bar){--scrollbar-thumb:#444}.slider{position:relative;display:flex;height:1rem;width:10rem;-webkit-user-select:none;-moz-user-select:none;user-select:none;align-items:center}.slider>.slider-track{position:relative;height:.25rem;flex-grow:1;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.slider .slider-range{position:absolute;height:100%}.slider .slider-range,.slider .slider-thumb{border-radius:9999px;--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.slider .slider-thumb{display:block;height:.75rem;width:.75rem;cursor:pointer;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.slider .slider-thumb:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.slider .slider-thumb:focus{box-shadow:0 0 0 4px rgba(0,0,0,.2)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.not-sr-only{position:static;width:auto;height:auto;padding:0;margin:0;overflow:visible;clip:auto;white-space:normal}.pointer-events-none{pointer-events:none}.\!visible{visibility:visible!important}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.\!static{position:static!important}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-inset-1{inset:-.25rem}.inset-0{inset:0}.inset-x-0{left:0;right:0}.inset-x-1{left:.25rem;right:.25rem}.inset-y-0{top:0;bottom:0}.-bottom-0{bottom:0}.-bottom-0\.5{bottom:-.125rem}.-bottom-1{bottom:-.25rem}.-left-\[12px\]{left:-12px}.-right-0{right:0}.-right-0\.5{right:-.125rem}.-right-1{right:-.25rem}.-top-\[2px\]{top:-2px}.-top-px{top:-1px}.bottom-0{bottom:0}.bottom-1{bottom:.25rem}.bottom-2{bottom:.5rem}.bottom-20{bottom:5rem}.bottom-3{bottom:.75rem}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.bottom-7{bottom:1.75rem}.bottom-\[6px\]{bottom:6px}.bottom-px{bottom:1px}.left-0{left:0}.left-0\.5{left:.125rem}.left-1{left:.25rem}.left-1\/2{left:50%}.left-1\/4{left:25%}.left-2{left:.5rem}.left-3{left:.75rem}.left-4{left:1rem}.left-6{left:1.5rem}.left-8{left:2rem}.left-\[calc\(50\%\+calc\(var\(--booker-meta-width\2c 0px\)\/2\)\)\]{left:calc(50% + calc(var(--booker-meta-width,0px)/2))}.left-px{left:1px}.right-0{right:0}.right-1{right:.25rem}.right-1\/2{right:50%}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.right-6{right:1.5rem}.right-8{right:2rem}.right-\[2px\]{right:2px}.right-px{right:1px}.top-0{top:0}.top-1{top:.25rem}.top-1\/2{top:50%}.top-2{top:.5rem}.top-2\.5{top:.625rem}.top-3{top:.75rem}.top-4{top:1rem}.top-40{top:10rem}.top-6{top:1.5rem}.top-8{top:2rem}.top-\[0\.9rem\]{top:.9rem}.top-\[var\(--calendar-dates-sticky-offset\2c 0px\)\]{top:var(--calendar-dates-sticky-offset,0)}.top-px{top:1px}.isolate{isolation:isolate}.isolation-auto{isolation:auto}.-z-10{z-index:-10}.z-0{z-index:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[2\]{z-index:2}.z-\[60\]{z-index:60}.z-\[80\]{z-index:80}.order-2{order:2}.order-none{order:0}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-11{grid-column:span 11/span 11}.col-span-12{grid-column:span 12/span 12}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.col-span-5{grid-column:span 5/span 5}.col-span-6{grid-column:span 6/span 6}.col-span-7{grid-column:span 7/span 7}.col-span-8{grid-column:span 8/span 8}.col-span-9{grid-column:span 9/span 9}.col-start-1{grid-column-start:1}.col-start-2{grid-column-start:2}.col-start-3{grid-column-start:3}.col-start-4{grid-column-start:4}.col-start-5{grid-column-start:5}.col-start-6{grid-column-start:6}.col-start-7{grid-column-start:7}.col-end-1{grid-column-end:1}.col-end-2{grid-column-end:2}.row-span-full{grid-row:1/-1}.row-start-1{grid-row-start:1}.row-end-1{grid-row-end:1}.float-right{float:right}.float-left{float:left}.\!m-0{margin:0!important}.m-0{margin:0}.m-1{margin:.25rem}.m-10{margin:2.5rem}.m-2{margin:.5rem}.m-4{margin:1rem}.m-8{margin:2rem}.m-auto{margin:auto}.\!my-0{margin-top:0!important;margin-bottom:0!important}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.-mx-2{margin-left:-.5rem;margin-right:-.5rem}.-mx-4{margin-left:-1rem;margin-right:-1rem}.-mx-8{margin-left:-2rem;margin-right:-2rem}.-mx-9{margin-left:-2.25rem;margin-right:-2.25rem}.-my-1{margin-top:-.25rem;margin-bottom:-.25rem}.mx-0{margin-left:0;margin-right:0}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-3{margin-left:.75rem;margin-right:.75rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.mx-6{margin-left:1.5rem;margin-right:1.5rem}.mx-8{margin-left:2rem;margin-right:2rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-0\.5{margin-top:.125rem;margin-bottom:.125rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-1\.5{margin-top:.375rem;margin-bottom:.375rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-2\.5{margin-top:.625rem;margin-bottom:.625rem}.my-24{margin-top:6rem;margin-bottom:6rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.my-7{margin-top:1.75rem;margin-bottom:1.75rem}.my-8{margin-top:2rem;margin-bottom:2rem}.my-auto{margin-top:auto;margin-bottom:auto}.\!mb-0{margin-bottom:0!important}.\!mb-3{margin-bottom:.75rem!important}.\!mt-0{margin-top:0!important}.\!mt-2{margin-top:.5rem!important}.\!mt-4{margin-top:1rem!important}.-mb-0{margin-bottom:0}.-mb-1{margin-bottom:-.25rem}.-mb-2{margin-bottom:-.5rem}.-mb-20{margin-bottom:-5rem}.-mb-3{margin-bottom:-.75rem}.-mb-4{margin-bottom:-1rem}.-mb-6{margin-bottom:-1.5rem}.-mb-px{margin-bottom:-1px}.-ml-0{margin-left:0}.-ml-0\.5{margin-left:-.125rem}.-ml-1{margin-left:-.25rem}.-ml-14{margin-left:-3.5rem}.-ml-2{margin-left:-.5rem}.-ml-3{margin-left:-.75rem}.-ml-4{margin-left:-1rem}.-ml-6{margin-left:-1.5rem}.-ml-8{margin-left:-2rem}.-ml-\[13px\]{margin-left:-13px}.-ml-\[3px\]{margin-left:-3px}.-ml-px{margin-left:-1px}.-mr-0{margin-right:0}.-mr-1{margin-right:-.25rem}.-mr-2{margin-right:-.5rem}.-mr-3{margin-right:-.75rem}.-mr-4{margin-right:-1rem}.-mr-6{margin-right:-1.5rem}.-mr-px{margin-right:-1px}.-mt-0{margin-top:0}.-mt-0\.5{margin-top:-.125rem}.-mt-1{margin-top:-.25rem}.-mt-1\.5{margin-top:-.375rem}.-mt-10{margin-top:-2.5rem}.-mt-11{margin-top:-2.75rem}.-mt-2{margin-top:-.5rem}.-mt-2\.5{margin-top:-.625rem}.-mt-3{margin-top:-.75rem}.-mt-4{margin-top:-1rem}.-mt-6{margin-top:-1.5rem}.-mt-7{margin-top:-1.75rem}.-mt-\[2px\]{margin-top:-2px}.-mt-px{margin-top:-1px}.mb-0{margin-bottom:0}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-10{margin-bottom:2.5rem}.mb-16{margin-bottom:4rem}.mb-2{margin-bottom:.5rem}.mb-2\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mb-\[-1px\]{margin-bottom:-1px}.mb-\[9px\]{margin-bottom:9px}.mb-auto{margin-bottom:auto}.mb-px{margin-bottom:1px}.me-1{-webkit-margin-end:.25rem;margin-inline-end:.25rem}.me-1\.5{-webkit-margin-end:.375rem;margin-inline-end:.375rem}.me-2{-webkit-margin-end:.5rem;margin-inline-end:.5rem}.me-3{-webkit-margin-end:.75rem;margin-inline-end:.75rem}.me-4{-webkit-margin-end:1rem;margin-inline-end:1rem}.me-5{-webkit-margin-end:1.25rem;margin-inline-end:1.25rem}.me-\[10px\]{-webkit-margin-end:10px;margin-inline-end:10px}.ml-0{margin-left:0}.ml-1{margin-left:.25rem}.ml-1\.5{margin-left:.375rem}.ml-11{margin-left:2.75rem}.ml-16{margin-left:4rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-5{margin-left:1.25rem}.ml-6{margin-left:1.5rem}.ml-7{margin-left:1.75rem}.ml-8{margin-left:2rem}.ml-\[-1px\]{margin-left:-1px}.ml-auto{margin-left:auto}.ml-px{margin-left:1px}.mr-0{margin-right:0}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-12{margin-right:3rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mr-5{margin-right:1.25rem}.mr-6{margin-right:1.5rem}.mr-7{margin-right:1.75rem}.mr-8{margin-right:2rem}.mr-\[11px\]{margin-right:11px}.mr-\[9px\]{margin-right:9px}.mr-auto{margin-right:auto}.mr-px{margin-right:1px}.ms-1{-webkit-margin-start:.25rem;margin-inline-start:.25rem}.ms-2{-webkit-margin-start:.5rem;margin-inline-start:.5rem}.ms-3{-webkit-margin-start:.75rem;margin-inline-start:.75rem}.ms-4{-webkit-margin-start:1rem;margin-inline-start:1rem}.ms-auto{-webkit-margin-start:auto;margin-inline-start:auto}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-2\.5{margin-top:.625rem}.mt-24{margin-top:6rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-7{margin-top:1.75rem}.mt-8{margin-top:2rem}.mt-9{margin-top:2.25rem}.mt-\[22px\]{margin-top:22px}.mt-\[2px\]{margin-top:2px}.mt-\[3px\]{margin-top:3px}.mt-\[9px\]{margin-top:9px}.mt-auto{margin-top:auto}.mt-px{margin-top:1px}.box-border{box-sizing:border-box}.line-clamp-1{-webkit-line-clamp:1}.line-clamp-1,.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical}.line-clamp-2{-webkit-line-clamp:2}.line-clamp-3{-webkit-line-clamp:3}.line-clamp-3,.line-clamp-4{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical}.line-clamp-4{-webkit-line-clamp:4}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.\!flex{display:flex!important}.flex{display:flex}.inline-flex{display:inline-flex}.\!table{display:table!important}.table{display:table}.inline-table{display:inline-table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-column{display:table-column}.table-column-group{display:table-column-group}.table-footer-group{display:table-footer-group}.table-header-group{display:table-header-group}.table-row-group{display:table-row-group}.table-row{display:table-row}.flow-root{display:flow-root}.grid{display:grid}.inline-grid{display:inline-grid}.contents{display:contents}.list-item{display:list-item}.hidden{display:none}.aspect-square{aspect-ratio:1/1}.aspect-video{aspect-ratio:16/9}.\!h-32{height:8rem!important}.h-0{height:0}.h-0\.5{height:.125rem}.h-1{height:.25rem}.h-1\/2{height:50%}.h-1\/3{height:33.333333%}.h-1\/4{height:25%}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-20{height:5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-40{height:10rem}.h-44{height:11rem}.h-5{height:1.25rem}.h-5\/6{height:83.333333%}.h-6{height:1.5rem}.h-60{height:15rem}.h-64{height:16rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-80{height:20rem}.h-9{height:2.25rem}.h-\[--calendar-offset-top\]{height:var(--calendar-offset-top)}.h-\[100vh\]{height:100vh}.h-\[10px\]{height:10px}.h-\[110px\]{height:110px}.h-\[14px\]{height:14px}.h-\[16px\]{height:16px}.h-\[1px\]{height:1px}.h-\[200px\]{height:200px}.h-\[20px\]{height:20px}.h-\[212px\]{height:212px}.h-\[26px\]{height:26px}.h-\[28px\]{height:28px}.h-\[34px\]{height:34px}.h-\[36px\]{height:36px}.h-\[38px\]{height:38px}.h-\[400px\]{height:400px}.h-\[40px\]{height:40px}.h-\[414px\]{height:414px}.h-\[54px\]{height:54px}.h-\[55vh\]{height:55vh}.h-\[5px\]{height:5px}.h-\[70px\]{height:70px}.h-\[72px\]{height:72px}.h-\[90px\]{height:90px}.h-\[90vh\]{height:90vh}.h-\[calc\(100\%-50px\)\]{height:calc(100% - 50px)}.h-\[calc\(100vh-2rem\)\]{height:calc(100vh - 2rem)}.h-auto{height:auto}.h-fit{height:-moz-fit-content;height:fit-content}.h-full{height:100%}.h-min{height:-moz-min-content;height:min-content}.h-px{height:1px}.h-screen{height:100vh}.max-h-20{max-height:5rem}.max-h-24{max-height:6rem}.max-h-4{max-height:1rem}.max-h-40{max-height:10rem}.max-h-48{max-height:12rem}.max-h-6{max-height:1.5rem}.max-h-64{max-height:16rem}.max-h-72{max-height:18rem}.max-h-80{max-height:20rem}.max-h-9{max-height:2.25rem}.max-h-\[180px\]{max-height:180px}.max-h-\[300px\]{max-height:300px}.max-h-\[390px\]{max-height:390px}.max-h-\[400px\]{max-height:400px}.max-h-\[45vh\]{max-height:45vh}.max-h-\[500px\]{max-height:500px}.max-h-\[80vh\]{max-height:80vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[95vh\]{max-height:95vh}.max-h-\[calc\(100vh-2rem\)\]{max-height:calc(100vh - 2rem)}.max-h-fit{max-height:-moz-fit-content;max-height:fit-content}.max-h-full{max-height:100%}.max-h-none{max-height:none}.max-h-screen{max-height:100vh}.\!min-h-0{min-height:0!important}.\!min-h-9{min-height:2.25rem!important}.\!min-h-\[36px\]{min-height:36px!important}.min-h-10{min-height:2.5rem}.min-h-16{min-height:4rem}.min-h-24{min-height:6rem}.min-h-3{min-height:.75rem}.min-h-3\.5{min-height:.875rem}.min-h-36{min-height:9rem}.min-h-4{min-height:1rem}.min-h-48{min-height:12rem}.min-h-5{min-height:1.25rem}.min-h-52{min-height:13rem}.min-h-6{min-height:1.5rem}.min-h-8{min-height:2rem}.min-h-80{min-height:20rem}.min-h-9{min-height:2.25rem}.min-h-\[100dvh\]{min-height:100dvh}.min-h-\[180px\]{min-height:180px}.min-h-\[200px\]{min-height:200px}.min-h-\[295px\]{min-height:295px}.min-h-\[315px\]{min-height:315px}.min-h-\[36px\]{min-height:36px}.min-h-\[450px\]{min-height:450px}.min-h-\[calc\(100dvh\)\]{min-height:calc(100dvh)}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.\!w-32{width:8rem!important}.\!w-48{width:12rem!important}.\!w-64{width:16rem!important}.\!w-auto{width:auto!important}.w-0{width:0}.w-1{width:.25rem}.w-1\/12{width:8.333333%}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-1\/4{width:25%}.w-1\/6{width:16.666667%}.w-10{width:2.5rem}.w-10\/12{width:83.333333%}.w-11{width:2.75rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-2\/3{width:66.666667%}.w-20{width:5rem}.w-24{width:6rem}.w-28{width:7rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-36{width:9rem}.w-4{width:1rem}.w-4\/5{width:80%}.w-40{width:10rem}.w-44{width:11rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-5\/6{width:83.333333%}.w-52{width:13rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-60{width:15rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-80{width:20rem}.w-9{width:2.25rem}.w-\[100px\]{width:100px}.w-\[106px\]{width:106px}.w-\[120px\]{width:120px}.w-\[130px\]{width:130px}.w-\[14px\]{width:14px}.w-\[16px\]{width:16px}.w-\[20\%\]{width:20%}.w-\[200px\]{width:200px}.w-\[20px\]{width:20px}.w-\[276px\]{width:276px}.w-\[30rem\]{width:30rem}.w-\[34px\]{width:34px}.w-\[36px\]{width:36px}.w-\[42px\]{width:42px}.w-\[46px\]{width:46px}.w-\[49px\]{width:49px}.w-\[5px\]{width:5px}.w-\[70px\]{width:70px}.w-\[72px\]{width:72px}.w-\[90px\]{width:90px}.w-\[calc\(100\%\+16px\)\]{width:calc(100% + 16px)}.w-\[calc\(100\%-1px\)\]{width:calc(100% - 1px)}.w-\[calc\(100\%-2rem\)\]{width:calc(100% - 2rem)}.w-\[var\(--booker-timeslots-width\)\]{width:var(--booker-timeslots-width)}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.w-screen{width:100vw}.min-w-0{min-width:0}.min-w-10{min-width:2.5rem}.min-w-16{min-width:4rem}.min-w-24{min-width:6rem}.min-w-3{min-width:.75rem}.min-w-3\.5{min-width:.875rem}.min-w-32{min-width:8rem}.min-w-4{min-width:1rem}.min-w-40{min-width:10rem}.min-w-48{min-width:12rem}.min-w-5{min-width:1.25rem}.min-w-56{min-width:14rem}.min-w-6{min-width:1.5rem}.min-w-60{min-width:15rem}.min-w-8{min-width:2rem}.min-w-80{min-width:20rem}.min-w-9{min-width:2.25rem}.min-w-\[10rem\]{min-width:10rem}.min-w-\[150px\]{min-width:150px}.min-w-\[34px\]{min-width:34px}.min-w-\[36px\]{min-width:36px}.min-w-\[40px\]{min-width:40px}.min-w-\[42px\]{min-width:42px}.min-w-\[88px\]{min-width:88px}.min-w-\[90\%\]{min-width:90%}.min-w-fit{min-width:-moz-fit-content;min-width:fit-content}.min-w-full{min-width:100%}.min-w-max{min-width:-moz-max-content;min-width:max-content}.min-w-min{min-width:-moz-min-content;min-width:min-content}.max-w-28{max-width:7rem}.max-w-2xl{max-width:42rem}.max-w-36{max-width:9rem}.max-w-3xl{max-width:48rem}.max-w-44{max-width:11rem}.max-w-4xl{max-width:56rem}.max-w-52{max-width:13rem}.max-w-56{max-width:14rem}.max-w-5xl{max-width:64rem}.max-w-60{max-width:15rem}.max-w-64{max-width:16rem}.max-w-7xl{max-width:80rem}.max-w-80{max-width:20rem}.max-w-96{max-width:24rem}.max-w-\[1440px\]{max-width:1440px}.max-w-\[2000px\]{max-width:2000px}.max-w-\[22rem\]{max-width:22rem}.max-w-\[250px\]{max-width:250px}.max-w-\[280px\]{max-width:280px}.max-w-\[420px\]{max-width:420px}.max-w-\[43em\]{max-width:43em}.max-w-\[500px\]{max-width:500px}.max-w-\[50ch\]{max-width:50ch}.max-w-\[600px\]{max-width:600px}.max-w-\[90\%\]{max-width:90%}.max-w-\[90vw\]{max-width:90vw}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-screen{max-width:100vw}.max-w-screen-lg{max-width:1024px}.max-w-screen-sm{max-width:640px}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-auto{flex:1 1 auto}.flex-none{flex:none}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.flex-grow-0{flex-grow:0}.grow{flex-grow:1}.basis-1\/5{flex-basis:20%}.basis-2\/5{flex-basis:40%}.basis-3\/5{flex-basis:60%}.table-fixed{table-layout:fixed}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.border-separate{border-collapse:separate}.border-spacing-0{--tw-border-spacing-x:0px;--tw-border-spacing-y:0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.origin-center{transform-origin:center}.origin-top-right{transform-origin:top right}.-translate-x-0{--tw-translate-x:-0px}.-translate-x-0,.-translate-x-0\.5{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-0\.5{--tw-translate-x:-0.125rem}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-x-\[228px\]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-\[228px\]{--tw-translate-x:-228px}.-translate-x-\[232px\]{--tw-translate-x:-232px}.-translate-x-\[232px\],.-translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-full{--tw-translate-x:-100%}.-translate-y-1\/2{--tw-translate-y:-50%}.-translate-y-1\/2,.translate-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x:0px}.translate-x-1\/2{--tw-translate-x:50%}.translate-x-1\/2,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x:100%}.translate-y-\[2px\]{--tw-translate-y:2px}.translate-y-\[2px\],.translate-y-\[8px\]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[8px\]{--tw-translate-y:8px}.-rotate-180{--tw-rotate:-180deg}.-rotate-180,.rotate-180{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate:180deg}.rotate-45{--tw-rotate:45deg}.rotate-45,.scale-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-0{--tw-scale-x:0;--tw-scale-y:0}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.scale-100,.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes fade-in-up{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}.animate-fade-in-up{animation:fade-in-up .6s var(--animation-delay,0ms) cubic-bezier(.21,1.02,.73,1) forwards}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}@keyframes spinning{to{transform:rotate(1turn)}}.animate-spinning{animation:spinning .75s linear infinite}.\!cursor-pointer{cursor:pointer!important}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.cursor-wait{cursor:wait}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.snap-x{scroll-snap-type:x var(--tw-scroll-snap-strictness)}.snap-mandatory{--tw-scroll-snap-strictness:mandatory}.snap-center{scroll-snap-align:center}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.auto-cols-auto{grid-auto-columns:auto}.auto-cols-fr{grid-auto-columns:minmax(0,1fr)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.grid-cols-11{grid-template-columns:repeat(11,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.grid-cols-9{grid-template-columns:repeat(9,minmax(0,1fr))}.grid-cols-none{grid-template-columns:none}.grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.content-center{align-content:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.justify-evenly{justify-content:space-evenly}.justify-items-center{justify-items:center}.gap-0{gap:0}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.gap-x-1{-moz-column-gap:.25rem;column-gap:.25rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-y-1{row-gap:.25rem}.gap-y-2{row-gap:.5rem}.gap-y-3{row-gap:.75rem}.gap-y-4{row-gap:1rem}.gap-y-6{row-gap:1.5rem}.gap-y-8{row-gap:2rem}.space-x-0>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(0px * var(--tw-space-x-reverse));margin-left:calc(0px * calc(1 - var(--tw-space-x-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-11>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2.75rem * var(--tw-space-x-reverse));margin-left:calc(2.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.625rem * var(--tw-space-x-reverse));margin-left:calc(.625rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.875rem * var(--tw-space-x-reverse));margin-left:calc(.875rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.25rem * var(--tw-space-x-reverse));margin-left:calc(1.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.5rem * var(--tw-space-x-reverse));margin-left:calc(1.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-px>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1px * var(--tw-space-x-reverse));margin-left:calc(1px * calc(1 - var(--tw-space-x-reverse)))}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px * var(--tw-space-y-reverse))}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-reverse>:not([hidden])~:not([hidden]){--tw-space-y-reverse:1}.space-x-reverse>:not([hidden])~:not([hidden]){--tw-space-x-reverse:1}.divide-x>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;border-right-width:calc(1px * var(--tw-divide-x-reverse));border-left-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-y-2>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(2px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(2px * var(--tw-divide-y-reverse))}.divide-y-reverse>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:1}.divide-x-reverse>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:1}.divide-amber-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(254 243 199/var(--tw-divide-opacity))}.divide-amber-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(253 230 138/var(--tw-divide-opacity))}.divide-amber-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(252 211 77/var(--tw-divide-opacity))}.divide-amber-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(251 191 36/var(--tw-divide-opacity))}.divide-amber-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(255 251 235/var(--tw-divide-opacity))}.divide-amber-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(245 158 11/var(--tw-divide-opacity))}.divide-amber-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(217 119 6/var(--tw-divide-opacity))}.divide-amber-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(180 83 9/var(--tw-divide-opacity))}.divide-amber-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(146 64 14/var(--tw-divide-opacity))}.divide-amber-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(120 53 15/var(--tw-divide-opacity))}.divide-black>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(17 17 17/var(--tw-divide-opacity))}.divide-blue-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(219 234 254/var(--tw-divide-opacity))}.divide-blue-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(191 219 254/var(--tw-divide-opacity))}.divide-blue-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(147 197 253/var(--tw-divide-opacity))}.divide-blue-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(96 165 250/var(--tw-divide-opacity))}.divide-blue-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(239 246 255/var(--tw-divide-opacity))}.divide-blue-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(59 130 246/var(--tw-divide-opacity))}.divide-blue-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(37 99 235/var(--tw-divide-opacity))}.divide-blue-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(29 78 216/var(--tw-divide-opacity))}.divide-blue-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(30 64 175/var(--tw-divide-opacity))}.divide-blue-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(30 58 138/var(--tw-divide-opacity))}.divide-cyan-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(207 250 254/var(--tw-divide-opacity))}.divide-cyan-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(165 243 252/var(--tw-divide-opacity))}.divide-cyan-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(103 232 249/var(--tw-divide-opacity))}.divide-cyan-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(34 211 238/var(--tw-divide-opacity))}.divide-cyan-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(236 254 255/var(--tw-divide-opacity))}.divide-cyan-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(6 182 212/var(--tw-divide-opacity))}.divide-cyan-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(8 145 178/var(--tw-divide-opacity))}.divide-cyan-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(14 116 144/var(--tw-divide-opacity))}.divide-cyan-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(21 94 117/var(--tw-divide-opacity))}.divide-cyan-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(22 78 99/var(--tw-divide-opacity))}.divide-default>:not([hidden])~:not([hidden]){border-color:var(--cal-border,#d1d5db)}.divide-emerald-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(209 250 229/var(--tw-divide-opacity))}.divide-emerald-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(167 243 208/var(--tw-divide-opacity))}.divide-emerald-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(110 231 183/var(--tw-divide-opacity))}.divide-emerald-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(52 211 153/var(--tw-divide-opacity))}.divide-emerald-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(236 253 245/var(--tw-divide-opacity))}.divide-emerald-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(16 185 129/var(--tw-divide-opacity))}.divide-emerald-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(5 150 105/var(--tw-divide-opacity))}.divide-emerald-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(4 120 87/var(--tw-divide-opacity))}.divide-emerald-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(6 95 70/var(--tw-divide-opacity))}.divide-emerald-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(6 78 59/var(--tw-divide-opacity))}.divide-fuchsia-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(250 232 255/var(--tw-divide-opacity))}.divide-fuchsia-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(245 208 254/var(--tw-divide-opacity))}.divide-fuchsia-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(240 171 252/var(--tw-divide-opacity))}.divide-fuchsia-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(232 121 249/var(--tw-divide-opacity))}.divide-fuchsia-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(253 244 255/var(--tw-divide-opacity))}.divide-fuchsia-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(217 70 239/var(--tw-divide-opacity))}.divide-fuchsia-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(192 38 211/var(--tw-divide-opacity))}.divide-fuchsia-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(162 28 175/var(--tw-divide-opacity))}.divide-fuchsia-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(134 25 143/var(--tw-divide-opacity))}.divide-fuchsia-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(112 26 117/var(--tw-divide-opacity))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.divide-gray-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(209 213 219/var(--tw-divide-opacity))}.divide-gray-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(156 163 175/var(--tw-divide-opacity))}.divide-gray-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(249 250 251/var(--tw-divide-opacity))}.divide-gray-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(107 114 128/var(--tw-divide-opacity))}.divide-gray-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(75 85 99/var(--tw-divide-opacity))}.divide-gray-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 65 81/var(--tw-divide-opacity))}.divide-gray-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(31 41 55/var(--tw-divide-opacity))}.divide-gray-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(17 24 39/var(--tw-divide-opacity))}.divide-green-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(220 252 231/var(--tw-divide-opacity))}.divide-green-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(187 247 208/var(--tw-divide-opacity))}.divide-green-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(134 239 172/var(--tw-divide-opacity))}.divide-green-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(74 222 128/var(--tw-divide-opacity))}.divide-green-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(240 253 244/var(--tw-divide-opacity))}.divide-green-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(34 197 94/var(--tw-divide-opacity))}.divide-green-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(22 163 74/var(--tw-divide-opacity))}.divide-green-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(21 128 61/var(--tw-divide-opacity))}.divide-green-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(22 101 52/var(--tw-divide-opacity))}.divide-green-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(20 83 45/var(--tw-divide-opacity))}.divide-indigo-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(224 231 255/var(--tw-divide-opacity))}.divide-indigo-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(199 210 254/var(--tw-divide-opacity))}.divide-indigo-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(165 180 252/var(--tw-divide-opacity))}.divide-indigo-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(129 140 248/var(--tw-divide-opacity))}.divide-indigo-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(238 242 255/var(--tw-divide-opacity))}.divide-indigo-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(99 102 241/var(--tw-divide-opacity))}.divide-indigo-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(79 70 229/var(--tw-divide-opacity))}.divide-indigo-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(67 56 202/var(--tw-divide-opacity))}.divide-indigo-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 48 163/var(--tw-divide-opacity))}.divide-indigo-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(49 46 129/var(--tw-divide-opacity))}.divide-lime-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(236 252 203/var(--tw-divide-opacity))}.divide-lime-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(217 249 157/var(--tw-divide-opacity))}.divide-lime-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(190 242 100/var(--tw-divide-opacity))}.divide-lime-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(163 230 53/var(--tw-divide-opacity))}.divide-lime-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(247 254 231/var(--tw-divide-opacity))}.divide-lime-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(132 204 22/var(--tw-divide-opacity))}.divide-lime-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(101 163 13/var(--tw-divide-opacity))}.divide-lime-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(77 124 15/var(--tw-divide-opacity))}.divide-lime-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(63 98 18/var(--tw-divide-opacity))}.divide-lime-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(54 83 20/var(--tw-divide-opacity))}.divide-neutral-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(245 245 245/var(--tw-divide-opacity))}.divide-neutral-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 229 229/var(--tw-divide-opacity))}.divide-neutral-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(212 212 212/var(--tw-divide-opacity))}.divide-neutral-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(163 163 163/var(--tw-divide-opacity))}.divide-neutral-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(250 250 250/var(--tw-divide-opacity))}.divide-neutral-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(115 115 115/var(--tw-divide-opacity))}.divide-neutral-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(82 82 82/var(--tw-divide-opacity))}.divide-neutral-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(64 64 64/var(--tw-divide-opacity))}.divide-neutral-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(38 38 38/var(--tw-divide-opacity))}.divide-neutral-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(23 23 23/var(--tw-divide-opacity))}.divide-orange-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(255 237 213/var(--tw-divide-opacity))}.divide-orange-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(254 215 170/var(--tw-divide-opacity))}.divide-orange-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(253 186 116/var(--tw-divide-opacity))}.divide-orange-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(251 146 60/var(--tw-divide-opacity))}.divide-orange-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(255 247 237/var(--tw-divide-opacity))}.divide-orange-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(249 115 22/var(--tw-divide-opacity))}.divide-orange-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(234 88 12/var(--tw-divide-opacity))}.divide-orange-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(194 65 12/var(--tw-divide-opacity))}.divide-orange-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(154 52 18/var(--tw-divide-opacity))}.divide-orange-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(124 45 18/var(--tw-divide-opacity))}.divide-pink-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(252 231 243/var(--tw-divide-opacity))}.divide-pink-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(251 207 232/var(--tw-divide-opacity))}.divide-pink-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(249 168 212/var(--tw-divide-opacity))}.divide-pink-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(244 114 182/var(--tw-divide-opacity))}.divide-pink-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(253 242 248/var(--tw-divide-opacity))}.divide-pink-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(236 72 153/var(--tw-divide-opacity))}.divide-pink-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(219 39 119/var(--tw-divide-opacity))}.divide-pink-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(190 24 93/var(--tw-divide-opacity))}.divide-pink-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(131 24 67/var(--tw-divide-opacity))}.divide-purple-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 232 255/var(--tw-divide-opacity))}.divide-purple-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(233 213 255/var(--tw-divide-opacity))}.divide-purple-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(216 180 254/var(--tw-divide-opacity))}.divide-purple-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(192 132 252/var(--tw-divide-opacity))}.divide-purple-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(250 245 255/var(--tw-divide-opacity))}.divide-purple-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(168 85 247/var(--tw-divide-opacity))}.divide-purple-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(147 51 234/var(--tw-divide-opacity))}.divide-purple-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(126 34 206/var(--tw-divide-opacity))}.divide-purple-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(107 33 168/var(--tw-divide-opacity))}.divide-purple-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(88 28 135/var(--tw-divide-opacity))}.divide-red-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(254 226 226/var(--tw-divide-opacity))}.divide-red-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(254 202 202/var(--tw-divide-opacity))}.divide-red-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(252 165 165/var(--tw-divide-opacity))}.divide-red-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(248 113 113/var(--tw-divide-opacity))}.divide-red-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(254 242 242/var(--tw-divide-opacity))}.divide-red-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(239 68 68/var(--tw-divide-opacity))}.divide-red-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(220 38 38/var(--tw-divide-opacity))}.divide-red-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(185 28 28/var(--tw-divide-opacity))}.divide-red-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(153 27 27/var(--tw-divide-opacity))}.divide-red-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(127 29 29/var(--tw-divide-opacity))}.divide-rose-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(255 228 230/var(--tw-divide-opacity))}.divide-rose-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(254 205 211/var(--tw-divide-opacity))}.divide-rose-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(253 164 175/var(--tw-divide-opacity))}.divide-rose-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(251 113 133/var(--tw-divide-opacity))}.divide-rose-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(255 241 242/var(--tw-divide-opacity))}.divide-rose-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(244 63 94/var(--tw-divide-opacity))}.divide-rose-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(225 29 72/var(--tw-divide-opacity))}.divide-rose-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(190 18 60/var(--tw-divide-opacity))}.divide-rose-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(159 18 57/var(--tw-divide-opacity))}.divide-rose-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(136 19 55/var(--tw-divide-opacity))}.divide-sky-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(224 242 254/var(--tw-divide-opacity))}.divide-sky-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(186 230 253/var(--tw-divide-opacity))}.divide-sky-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(125 211 252/var(--tw-divide-opacity))}.divide-sky-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(56 189 248/var(--tw-divide-opacity))}.divide-sky-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(240 249 255/var(--tw-divide-opacity))}.divide-sky-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(14 165 233/var(--tw-divide-opacity))}.divide-sky-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(2 132 199/var(--tw-divide-opacity))}.divide-sky-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(3 105 161/var(--tw-divide-opacity))}.divide-sky-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(7 89 133/var(--tw-divide-opacity))}.divide-sky-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(12 74 110/var(--tw-divide-opacity))}.divide-slate-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(241 245 249/var(--tw-divide-opacity))}.divide-slate-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(226 232 240/var(--tw-divide-opacity))}.divide-slate-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(203 213 225/var(--tw-divide-opacity))}.divide-slate-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(148 163 184/var(--tw-divide-opacity))}.divide-slate-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(248 250 252/var(--tw-divide-opacity))}.divide-slate-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(100 116 139/var(--tw-divide-opacity))}.divide-slate-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(71 85 105/var(--tw-divide-opacity))}.divide-slate-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(51 65 85/var(--tw-divide-opacity))}.divide-slate-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(30 41 59/var(--tw-divide-opacity))}.divide-slate-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(15 23 42/var(--tw-divide-opacity))}.divide-stone-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(245 245 244/var(--tw-divide-opacity))}.divide-stone-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(231 229 228/var(--tw-divide-opacity))}.divide-stone-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(214 211 209/var(--tw-divide-opacity))}.divide-stone-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(168 162 158/var(--tw-divide-opacity))}.divide-stone-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(250 250 249/var(--tw-divide-opacity))}.divide-stone-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(120 113 108/var(--tw-divide-opacity))}.divide-stone-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(87 83 78/var(--tw-divide-opacity))}.divide-stone-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(68 64 60/var(--tw-divide-opacity))}.divide-stone-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(41 37 36/var(--tw-divide-opacity))}.divide-stone-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(28 25 23/var(--tw-divide-opacity))}.divide-subtle>:not([hidden])~:not([hidden]){border-color:var(--cal-border-subtle,#e5e7eb)}.divide-teal-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(204 251 241/var(--tw-divide-opacity))}.divide-teal-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(153 246 228/var(--tw-divide-opacity))}.divide-teal-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(94 234 212/var(--tw-divide-opacity))}.divide-teal-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(45 212 191/var(--tw-divide-opacity))}.divide-teal-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(240 253 250/var(--tw-divide-opacity))}.divide-teal-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(20 184 166/var(--tw-divide-opacity))}.divide-teal-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(13 148 136/var(--tw-divide-opacity))}.divide-teal-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(15 118 110/var(--tw-divide-opacity))}.divide-teal-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(17 94 89/var(--tw-divide-opacity))}.divide-teal-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(19 78 74/var(--tw-divide-opacity))}.divide-transparent>:not([hidden])~:not([hidden]){border-color:transparent}.divide-violet-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(237 233 254/var(--tw-divide-opacity))}.divide-violet-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(221 214 254/var(--tw-divide-opacity))}.divide-violet-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(196 181 253/var(--tw-divide-opacity))}.divide-violet-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(167 139 250/var(--tw-divide-opacity))}.divide-violet-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(245 243 255/var(--tw-divide-opacity))}.divide-violet-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(139 92 246/var(--tw-divide-opacity))}.divide-violet-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(124 58 237/var(--tw-divide-opacity))}.divide-violet-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(109 40 217/var(--tw-divide-opacity))}.divide-violet-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(91 33 182/var(--tw-divide-opacity))}.divide-violet-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(76 29 149/var(--tw-divide-opacity))}.divide-white>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(255 255 255/var(--tw-divide-opacity))}.divide-yellow-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(254 249 195/var(--tw-divide-opacity))}.divide-yellow-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(254 240 138/var(--tw-divide-opacity))}.divide-yellow-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(253 224 71/var(--tw-divide-opacity))}.divide-yellow-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(250 204 21/var(--tw-divide-opacity))}.divide-yellow-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(254 252 232/var(--tw-divide-opacity))}.divide-yellow-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(234 179 8/var(--tw-divide-opacity))}.divide-yellow-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(202 138 4/var(--tw-divide-opacity))}.divide-yellow-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(161 98 7/var(--tw-divide-opacity))}.divide-yellow-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(133 77 14/var(--tw-divide-opacity))}.divide-yellow-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(113 63 18/var(--tw-divide-opacity))}.divide-zinc-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(244 244 245/var(--tw-divide-opacity))}.divide-zinc-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(228 228 231/var(--tw-divide-opacity))}.divide-zinc-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(212 212 216/var(--tw-divide-opacity))}.divide-zinc-400>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(161 161 170/var(--tw-divide-opacity))}.divide-zinc-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(250 250 250/var(--tw-divide-opacity))}.divide-zinc-500>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(113 113 122/var(--tw-divide-opacity))}.divide-zinc-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(82 82 91/var(--tw-divide-opacity))}.divide-zinc-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(63 63 70/var(--tw-divide-opacity))}.divide-zinc-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(39 39 42/var(--tw-divide-opacity))}.divide-zinc-900>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(24 24 27/var(--tw-divide-opacity))}.place-self-center{place-self:center}.self-start{align-self:flex-start}.self-end{align-self:flex-end}.self-center{align-self:center}.self-stretch{align-self:stretch}.justify-self-end{justify-self:end}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-clip{overflow:clip}.overflow-visible{overflow:visible}.overflow-scroll{overflow:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-hidden{overflow-y:hidden}.overflow-x-clip{overflow-x:clip}.overflow-x-scroll{overflow-x:scroll}.\!overflow-y-scroll{overflow-y:scroll!important}.overflow-y-scroll{overflow-y:scroll}.scroll-auto{scroll-behavior:auto}.truncate{overflow:hidden;white-space:nowrap}.overflow-ellipsis,.text-ellipsis,.truncate{text-overflow:ellipsis}.text-clip{text-overflow:clip}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-\[1px\]{border-radius:1px}.rounded-\[32px\]{border-radius:32px}.rounded-\[4px\]{border-radius:4px}.rounded-\[5px\]{border-radius:5px}.rounded-\[6px\]{border-radius:6px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-b-full{border-bottom-right-radius:9999px;border-bottom-left-radius:9999px}.rounded-b-lg{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.rounded-b-md{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-b-none{border-bottom-right-radius:0;border-bottom-left-radius:0}.rounded-b-xl{border-bottom-right-radius:.75rem;border-bottom-left-radius:.75rem}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-l-2xl{border-top-left-radius:1rem;border-bottom-left-radius:1rem}.rounded-l-full{border-top-left-radius:9999px;border-bottom-left-radius:9999px}.rounded-l-lg{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}.rounded-l-md{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-l-none{border-top-left-radius:0;border-bottom-left-radius:0}.rounded-l-sm{border-top-left-radius:.125rem;border-bottom-left-radius:.125rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-r-full{border-top-right-radius:9999px;border-bottom-right-radius:9999px}.rounded-r-lg{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-r-none{border-top-right-radius:0;border-bottom-right-radius:0}.rounded-r-sm{border-top-right-radius:.125rem;border-bottom-right-radius:.125rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-full{border-top-left-radius:9999px;border-top-right-radius:9999px}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.rounded-t-none{border-top-left-radius:0;border-top-right-radius:0}.rounded-t-xl{border-top-left-radius:.75rem;border-top-right-radius:.75rem}.rounded-bl{border-bottom-left-radius:.25rem}.rounded-bl-2xl{border-bottom-left-radius:1rem}.rounded-bl-md{border-bottom-left-radius:.375rem}.rounded-br{border-bottom-right-radius:.25rem}.rounded-br-md{border-bottom-right-radius:.375rem}.rounded-br-none{border-bottom-right-radius:0}.rounded-tl{border-top-left-radius:.25rem}.rounded-tl-2xl{border-top-left-radius:1rem}.rounded-tl-lg{border-top-left-radius:.5rem}.rounded-tr{border-top-right-radius:.25rem}.rounded-tr-lg{border-top-right-radius:.5rem}.\!border-0{border-width:0!important}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-4{border-width:4px}.border-\[1\.5px\]{border-width:1.5px}.border-\[1px\]{border-width:1px}.border-booker-width{border-width:var(--cal-border-booker-width,1px)}.border-x{border-left-width:1px;border-right-width:1px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-y-0{border-top-width:0;border-bottom-width:0}.border-b{border-bottom-width:1px}.border-b-0{border-bottom-width:0}.border-b-2{border-bottom-width:2px}.border-b-4{border-bottom-width:4px}.border-l{border-left-width:1px}.border-l-0{border-left-width:0}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-r-0{border-right-width:0}.border-r-2{border-right-width:2px}.border-r-4{border-right-width:4px}.border-t{border-top-width:1px}.border-t-0{border-top-width:0}.border-t-2{border-top-width:2px}.border-t-4{border-top-width:4px}.border-solid{border-style:solid}.border-dashed{border-style:dashed}.border-none{border-style:none}.\!border-muted{border-color:var(--cal-border-muted,#f3f4f6)!important}.\!border-red-700{--tw-border-opacity:1!important;border-color:rgb(185 28 28/var(--tw-border-opacity))!important}.border-amber-100{--tw-border-opacity:1;border-color:rgb(254 243 199/var(--tw-border-opacity))}.border-amber-200{--tw-border-opacity:1;border-color:rgb(253 230 138/var(--tw-border-opacity))}.border-amber-300{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity))}.border-amber-400{--tw-border-opacity:1;border-color:rgb(251 191 36/var(--tw-border-opacity))}.border-amber-50{--tw-border-opacity:1;border-color:rgb(255 251 235/var(--tw-border-opacity))}.border-amber-500{--tw-border-opacity:1;border-color:rgb(245 158 11/var(--tw-border-opacity))}.border-amber-600{--tw-border-opacity:1;border-color:rgb(217 119 6/var(--tw-border-opacity))}.border-amber-700{--tw-border-opacity:1;border-color:rgb(180 83 9/var(--tw-border-opacity))}.border-amber-800{--tw-border-opacity:1;border-color:rgb(146 64 14/var(--tw-border-opacity))}.border-amber-900{--tw-border-opacity:1;border-color:rgb(120 53 15/var(--tw-border-opacity))}.border-black{--tw-border-opacity:1;border-color:rgb(17 17 17/var(--tw-border-opacity))}.border-blue-100{--tw-border-opacity:1;border-color:rgb(219 234 254/var(--tw-border-opacity))}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity))}.border-blue-300{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity))}.border-blue-400{--tw-border-opacity:1;border-color:rgb(96 165 250/var(--tw-border-opacity))}.border-blue-50{--tw-border-opacity:1;border-color:rgb(239 246 255/var(--tw-border-opacity))}.border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.border-blue-600{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity))}.border-blue-700{--tw-border-opacity:1;border-color:rgb(29 78 216/var(--tw-border-opacity))}.border-blue-800{--tw-border-opacity:1;border-color:rgb(30 64 175/var(--tw-border-opacity))}.border-blue-900{--tw-border-opacity:1;border-color:rgb(30 58 138/var(--tw-border-opacity))}.border-booker{border-color:var(--cal-border-booker,#e5e7eb)}.border-brand-default{border-color:var(--cal-brand,#111827)}.border-cyan-100{--tw-border-opacity:1;border-color:rgb(207 250 254/var(--tw-border-opacity))}.border-cyan-200{--tw-border-opacity:1;border-color:rgb(165 243 252/var(--tw-border-opacity))}.border-cyan-300{--tw-border-opacity:1;border-color:rgb(103 232 249/var(--tw-border-opacity))}.border-cyan-400{--tw-border-opacity:1;border-color:rgb(34 211 238/var(--tw-border-opacity))}.border-cyan-50{--tw-border-opacity:1;border-color:rgb(236 254 255/var(--tw-border-opacity))}.border-cyan-500{--tw-border-opacity:1;border-color:rgb(6 182 212/var(--tw-border-opacity))}.border-cyan-600{--tw-border-opacity:1;border-color:rgb(8 145 178/var(--tw-border-opacity))}.border-cyan-700{--tw-border-opacity:1;border-color:rgb(14 116 144/var(--tw-border-opacity))}.border-cyan-800{--tw-border-opacity:1;border-color:rgb(21 94 117/var(--tw-border-opacity))}.border-cyan-900{--tw-border-opacity:1;border-color:rgb(22 78 99/var(--tw-border-opacity))}.border-default{border-color:var(--cal-border,#d1d5db)}.border-emerald-100{--tw-border-opacity:1;border-color:rgb(209 250 229/var(--tw-border-opacity))}.border-emerald-200{--tw-border-opacity:1;border-color:rgb(167 243 208/var(--tw-border-opacity))}.border-emerald-300{--tw-border-opacity:1;border-color:rgb(110 231 183/var(--tw-border-opacity))}.border-emerald-400{--tw-border-opacity:1;border-color:rgb(52 211 153/var(--tw-border-opacity))}.border-emerald-50{--tw-border-opacity:1;border-color:rgb(236 253 245/var(--tw-border-opacity))}.border-emerald-500{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity))}.border-emerald-600{--tw-border-opacity:1;border-color:rgb(5 150 105/var(--tw-border-opacity))}.border-emerald-700{--tw-border-opacity:1;border-color:rgb(4 120 87/var(--tw-border-opacity))}.border-emerald-800{--tw-border-opacity:1;border-color:rgb(6 95 70/var(--tw-border-opacity))}.border-emerald-900{--tw-border-opacity:1;border-color:rgb(6 78 59/var(--tw-border-opacity))}.border-emphasis{border-color:var(--cal-border-emphasis,#9ca3af)}.border-error{border-color:var(--cal-border-error,#aa2e26)}.border-fuchsia-100{--tw-border-opacity:1;border-color:rgb(250 232 255/var(--tw-border-opacity))}.border-fuchsia-200{--tw-border-opacity:1;border-color:rgb(245 208 254/var(--tw-border-opacity))}.border-fuchsia-300{--tw-border-opacity:1;border-color:rgb(240 171 252/var(--tw-border-opacity))}.border-fuchsia-400{--tw-border-opacity:1;border-color:rgb(232 121 249/var(--tw-border-opacity))}.border-fuchsia-50{--tw-border-opacity:1;border-color:rgb(253 244 255/var(--tw-border-opacity))}.border-fuchsia-500{--tw-border-opacity:1;border-color:rgb(217 70 239/var(--tw-border-opacity))}.border-fuchsia-600{--tw-border-opacity:1;border-color:rgb(192 38 211/var(--tw-border-opacity))}.border-fuchsia-700{--tw-border-opacity:1;border-color:rgb(162 28 175/var(--tw-border-opacity))}.border-fuchsia-800{--tw-border-opacity:1;border-color:rgb(134 25 143/var(--tw-border-opacity))}.border-fuchsia-900{--tw-border-opacity:1;border-color:rgb(112 26 117/var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-gray-300\/20{border-color:rgba(209,213,219,.2)}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.border-gray-50{--tw-border-opacity:1;border-color:rgb(249 250 251/var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}.border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}.border-gray-800{--tw-border-opacity:1;border-color:rgb(31 41 55/var(--tw-border-opacity))}.border-gray-900{--tw-border-opacity:1;border-color:rgb(17 24 39/var(--tw-border-opacity))}.border-green-100{--tw-border-opacity:1;border-color:rgb(220 252 231/var(--tw-border-opacity))}.border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity))}.border-green-300{--tw-border-opacity:1;border-color:rgb(134 239 172/var(--tw-border-opacity))}.border-green-400{--tw-border-opacity:1;border-color:rgb(74 222 128/var(--tw-border-opacity))}.border-green-50{--tw-border-opacity:1;border-color:rgb(240 253 244/var(--tw-border-opacity))}.border-green-500{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity))}.border-green-600{--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity))}.border-green-700{--tw-border-opacity:1;border-color:rgb(21 128 61/var(--tw-border-opacity))}.border-green-800{--tw-border-opacity:1;border-color:rgb(22 101 52/var(--tw-border-opacity))}.border-green-900{--tw-border-opacity:1;border-color:rgb(20 83 45/var(--tw-border-opacity))}.border-indigo-100{--tw-border-opacity:1;border-color:rgb(224 231 255/var(--tw-border-opacity))}.border-indigo-200{--tw-border-opacity:1;border-color:rgb(199 210 254/var(--tw-border-opacity))}.border-indigo-300{--tw-border-opacity:1;border-color:rgb(165 180 252/var(--tw-border-opacity))}.border-indigo-400{--tw-border-opacity:1;border-color:rgb(129 140 248/var(--tw-border-opacity))}.border-indigo-50{--tw-border-opacity:1;border-color:rgb(238 242 255/var(--tw-border-opacity))}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.border-indigo-600{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity))}.border-indigo-700{--tw-border-opacity:1;border-color:rgb(67 56 202/var(--tw-border-opacity))}.border-indigo-800{--tw-border-opacity:1;border-color:rgb(55 48 163/var(--tw-border-opacity))}.border-indigo-900{--tw-border-opacity:1;border-color:rgb(49 46 129/var(--tw-border-opacity))}.border-inverted{border-color:var(--cal-bg-inverted)}.border-lime-100{--tw-border-opacity:1;border-color:rgb(236 252 203/var(--tw-border-opacity))}.border-lime-200{--tw-border-opacity:1;border-color:rgb(217 249 157/var(--tw-border-opacity))}.border-lime-300{--tw-border-opacity:1;border-color:rgb(190 242 100/var(--tw-border-opacity))}.border-lime-400{--tw-border-opacity:1;border-color:rgb(163 230 53/var(--tw-border-opacity))}.border-lime-50{--tw-border-opacity:1;border-color:rgb(247 254 231/var(--tw-border-opacity))}.border-lime-500{--tw-border-opacity:1;border-color:rgb(132 204 22/var(--tw-border-opacity))}.border-lime-600{--tw-border-opacity:1;border-color:rgb(101 163 13/var(--tw-border-opacity))}.border-lime-700{--tw-border-opacity:1;border-color:rgb(77 124 15/var(--tw-border-opacity))}.border-lime-800{--tw-border-opacity:1;border-color:rgb(63 98 18/var(--tw-border-opacity))}.border-lime-900{--tw-border-opacity:1;border-color:rgb(54 83 20/var(--tw-border-opacity))}.border-muted{border-color:var(--cal-border-muted,#f3f4f6)}.border-neutral-100{--tw-border-opacity:1;border-color:rgb(245 245 245/var(--tw-border-opacity))}.border-neutral-200{--tw-border-opacity:1;border-color:rgb(229 229 229/var(--tw-border-opacity))}.border-neutral-300{--tw-border-opacity:1;border-color:rgb(212 212 212/var(--tw-border-opacity))}.border-neutral-400{--tw-border-opacity:1;border-color:rgb(163 163 163/var(--tw-border-opacity))}.border-neutral-50{--tw-border-opacity:1;border-color:rgb(250 250 250/var(--tw-border-opacity))}.border-neutral-500{--tw-border-opacity:1;border-color:rgb(115 115 115/var(--tw-border-opacity))}.border-neutral-600{--tw-border-opacity:1;border-color:rgb(82 82 82/var(--tw-border-opacity))}.border-neutral-700{--tw-border-opacity:1;border-color:rgb(64 64 64/var(--tw-border-opacity))}.border-neutral-800{--tw-border-opacity:1;border-color:rgb(38 38 38/var(--tw-border-opacity))}.border-neutral-900{--tw-border-opacity:1;border-color:rgb(23 23 23/var(--tw-border-opacity))}.border-orange-100{--tw-border-opacity:1;border-color:rgb(255 237 213/var(--tw-border-opacity))}.border-orange-200{--tw-border-opacity:1;border-color:rgb(254 215 170/var(--tw-border-opacity))}.border-orange-300{--tw-border-opacity:1;border-color:rgb(253 186 116/var(--tw-border-opacity))}.border-orange-400{--tw-border-opacity:1;border-color:rgb(251 146 60/var(--tw-border-opacity))}.border-orange-50{--tw-border-opacity:1;border-color:rgb(255 247 237/var(--tw-border-opacity))}.border-orange-500{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity))}.border-orange-600{--tw-border-opacity:1;border-color:rgb(234 88 12/var(--tw-border-opacity))}.border-orange-700{--tw-border-opacity:1;border-color:rgb(194 65 12/var(--tw-border-opacity))}.border-orange-800{--tw-border-opacity:1;border-color:rgb(154 52 18/var(--tw-border-opacity))}.border-orange-900{--tw-border-opacity:1;border-color:rgb(124 45 18/var(--tw-border-opacity))}.border-pink-100{--tw-border-opacity:1;border-color:rgb(252 231 243/var(--tw-border-opacity))}.border-pink-200{--tw-border-opacity:1;border-color:rgb(251 207 232/var(--tw-border-opacity))}.border-pink-300{--tw-border-opacity:1;border-color:rgb(249 168 212/var(--tw-border-opacity))}.border-pink-400{--tw-border-opacity:1;border-color:rgb(244 114 182/var(--tw-border-opacity))}.border-pink-50{--tw-border-opacity:1;border-color:rgb(253 242 248/var(--tw-border-opacity))}.border-pink-500{--tw-border-opacity:1;border-color:rgb(236 72 153/var(--tw-border-opacity))}.border-pink-600{--tw-border-opacity:1;border-color:rgb(219 39 119/var(--tw-border-opacity))}.border-pink-700{--tw-border-opacity:1;border-color:rgb(190 24 93/var(--tw-border-opacity))}.border-pink-900{--tw-border-opacity:1;border-color:rgb(131 24 67/var(--tw-border-opacity))}.border-purple-100{--tw-border-opacity:1;border-color:rgb(243 232 255/var(--tw-border-opacity))}.border-purple-200{--tw-border-opacity:1;border-color:rgb(233 213 255/var(--tw-border-opacity))}.border-purple-300{--tw-border-opacity:1;border-color:rgb(216 180 254/var(--tw-border-opacity))}.border-purple-400{--tw-border-opacity:1;border-color:rgb(192 132 252/var(--tw-border-opacity))}.border-purple-50{--tw-border-opacity:1;border-color:rgb(250 245 255/var(--tw-border-opacity))}.border-purple-500{--tw-border-opacity:1;border-color:rgb(168 85 247/var(--tw-border-opacity))}.border-purple-600{--tw-border-opacity:1;border-color:rgb(147 51 234/var(--tw-border-opacity))}.border-purple-700{--tw-border-opacity:1;border-color:rgb(126 34 206/var(--tw-border-opacity))}.border-purple-800{--tw-border-opacity:1;border-color:rgb(107 33 168/var(--tw-border-opacity))}.border-purple-900{--tw-border-opacity:1;border-color:rgb(88 28 135/var(--tw-border-opacity))}.border-red-100{--tw-border-opacity:1;border-color:rgb(254 226 226/var(--tw-border-opacity))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity))}.border-red-300{--tw-border-opacity:1;border-color:rgb(252 165 165/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.border-red-50{--tw-border-opacity:1;border-color:rgb(254 242 242/var(--tw-border-opacity))}.border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity))}.border-red-600{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity))}.border-red-700{--tw-border-opacity:1;border-color:rgb(185 28 28/var(--tw-border-opacity))}.border-red-800{--tw-border-opacity:1;border-color:rgb(153 27 27/var(--tw-border-opacity))}.border-red-900{--tw-border-opacity:1;border-color:rgb(127 29 29/var(--tw-border-opacity))}.border-rose-100{--tw-border-opacity:1;border-color:rgb(255 228 230/var(--tw-border-opacity))}.border-rose-200{--tw-border-opacity:1;border-color:rgb(254 205 211/var(--tw-border-opacity))}.border-rose-300{--tw-border-opacity:1;border-color:rgb(253 164 175/var(--tw-border-opacity))}.border-rose-400{--tw-border-opacity:1;border-color:rgb(251 113 133/var(--tw-border-opacity))}.border-rose-50{--tw-border-opacity:1;border-color:rgb(255 241 242/var(--tw-border-opacity))}.border-rose-500{--tw-border-opacity:1;border-color:rgb(244 63 94/var(--tw-border-opacity))}.border-rose-600{--tw-border-opacity:1;border-color:rgb(225 29 72/var(--tw-border-opacity))}.border-rose-700{--tw-border-opacity:1;border-color:rgb(190 18 60/var(--tw-border-opacity))}.border-rose-800{--tw-border-opacity:1;border-color:rgb(159 18 57/var(--tw-border-opacity))}.border-rose-900{--tw-border-opacity:1;border-color:rgb(136 19 55/var(--tw-border-opacity))}.border-sky-100{--tw-border-opacity:1;border-color:rgb(224 242 254/var(--tw-border-opacity))}.border-sky-200{--tw-border-opacity:1;border-color:rgb(186 230 253/var(--tw-border-opacity))}.border-sky-300{--tw-border-opacity:1;border-color:rgb(125 211 252/var(--tw-border-opacity))}.border-sky-400{--tw-border-opacity:1;border-color:rgb(56 189 248/var(--tw-border-opacity))}.border-sky-50{--tw-border-opacity:1;border-color:rgb(240 249 255/var(--tw-border-opacity))}.border-sky-500{--tw-border-opacity:1;border-color:rgb(14 165 233/var(--tw-border-opacity))}.border-sky-600{--tw-border-opacity:1;border-color:rgb(2 132 199/var(--tw-border-opacity))}.border-sky-700{--tw-border-opacity:1;border-color:rgb(3 105 161/var(--tw-border-opacity))}.border-sky-800{--tw-border-opacity:1;border-color:rgb(7 89 133/var(--tw-border-opacity))}.border-sky-900{--tw-border-opacity:1;border-color:rgb(12 74 110/var(--tw-border-opacity))}.border-slate-100{--tw-border-opacity:1;border-color:rgb(241 245 249/var(--tw-border-opacity))}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity))}.border-slate-300{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity))}.border-slate-400{--tw-border-opacity:1;border-color:rgb(148 163 184/var(--tw-border-opacity))}.border-slate-50{--tw-border-opacity:1;border-color:rgb(248 250 252/var(--tw-border-opacity))}.border-slate-500{--tw-border-opacity:1;border-color:rgb(100 116 139/var(--tw-border-opacity))}.border-slate-600{--tw-border-opacity:1;border-color:rgb(71 85 105/var(--tw-border-opacity))}.border-slate-700{--tw-border-opacity:1;border-color:rgb(51 65 85/var(--tw-border-opacity))}.border-slate-800{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity))}.border-slate-900{--tw-border-opacity:1;border-color:rgb(15 23 42/var(--tw-border-opacity))}.border-stone-100{--tw-border-opacity:1;border-color:rgb(245 245 244/var(--tw-border-opacity))}.border-stone-200{--tw-border-opacity:1;border-color:rgb(231 229 228/var(--tw-border-opacity))}.border-stone-300{--tw-border-opacity:1;border-color:rgb(214 211 209/var(--tw-border-opacity))}.border-stone-400{--tw-border-opacity:1;border-color:rgb(168 162 158/var(--tw-border-opacity))}.border-stone-50{--tw-border-opacity:1;border-color:rgb(250 250 249/var(--tw-border-opacity))}.border-stone-500{--tw-border-opacity:1;border-color:rgb(120 113 108/var(--tw-border-opacity))}.border-stone-600{--tw-border-opacity:1;border-color:rgb(87 83 78/var(--tw-border-opacity))}.border-stone-700{--tw-border-opacity:1;border-color:rgb(68 64 60/var(--tw-border-opacity))}.border-stone-800{--tw-border-opacity:1;border-color:rgb(41 37 36/var(--tw-border-opacity))}.border-stone-900{--tw-border-opacity:1;border-color:rgb(28 25 23/var(--tw-border-opacity))}.border-subtle{border-color:var(--cal-border-subtle,#e5e7eb)}.border-teal-100{--tw-border-opacity:1;border-color:rgb(204 251 241/var(--tw-border-opacity))}.border-teal-200{--tw-border-opacity:1;border-color:rgb(153 246 228/var(--tw-border-opacity))}.border-teal-300{--tw-border-opacity:1;border-color:rgb(94 234 212/var(--tw-border-opacity))}.border-teal-400{--tw-border-opacity:1;border-color:rgb(45 212 191/var(--tw-border-opacity))}.border-teal-50{--tw-border-opacity:1;border-color:rgb(240 253 250/var(--tw-border-opacity))}.border-teal-500{--tw-border-opacity:1;border-color:rgb(20 184 166/var(--tw-border-opacity))}.border-teal-600{--tw-border-opacity:1;border-color:rgb(13 148 136/var(--tw-border-opacity))}.border-teal-700{--tw-border-opacity:1;border-color:rgb(15 118 110/var(--tw-border-opacity))}.border-teal-800{--tw-border-opacity:1;border-color:rgb(17 94 89/var(--tw-border-opacity))}.border-teal-900{--tw-border-opacity:1;border-color:rgb(19 78 74/var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-violet-100{--tw-border-opacity:1;border-color:rgb(237 233 254/var(--tw-border-opacity))}.border-violet-200{--tw-border-opacity:1;border-color:rgb(221 214 254/var(--tw-border-opacity))}.border-violet-300{--tw-border-opacity:1;border-color:rgb(196 181 253/var(--tw-border-opacity))}.border-violet-400{--tw-border-opacity:1;border-color:rgb(167 139 250/var(--tw-border-opacity))}.border-violet-50{--tw-border-opacity:1;border-color:rgb(245 243 255/var(--tw-border-opacity))}.border-violet-500{--tw-border-opacity:1;border-color:rgb(139 92 246/var(--tw-border-opacity))}.border-violet-600{--tw-border-opacity:1;border-color:rgb(124 58 237/var(--tw-border-opacity))}.border-violet-700{--tw-border-opacity:1;border-color:rgb(109 40 217/var(--tw-border-opacity))}.border-violet-800{--tw-border-opacity:1;border-color:rgb(91 33 182/var(--tw-border-opacity))}.border-violet-900{--tw-border-opacity:1;border-color:rgb(76 29 149/var(--tw-border-opacity))}.border-white{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity))}.border-yellow-100{--tw-border-opacity:1;border-color:rgb(254 249 195/var(--tw-border-opacity))}.border-yellow-200{--tw-border-opacity:1;border-color:rgb(254 240 138/var(--tw-border-opacity))}.border-yellow-300{--tw-border-opacity:1;border-color:rgb(253 224 71/var(--tw-border-opacity))}.border-yellow-400{--tw-border-opacity:1;border-color:rgb(250 204 21/var(--tw-border-opacity))}.border-yellow-50{--tw-border-opacity:1;border-color:rgb(254 252 232/var(--tw-border-opacity))}.border-yellow-500{--tw-border-opacity:1;border-color:rgb(234 179 8/var(--tw-border-opacity))}.border-yellow-600{--tw-border-opacity:1;border-color:rgb(202 138 4/var(--tw-border-opacity))}.border-yellow-700{--tw-border-opacity:1;border-color:rgb(161 98 7/var(--tw-border-opacity))}.border-yellow-800{--tw-border-opacity:1;border-color:rgb(133 77 14/var(--tw-border-opacity))}.border-yellow-900{--tw-border-opacity:1;border-color:rgb(113 63 18/var(--tw-border-opacity))}.border-zinc-100{--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity))}.border-zinc-200{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity))}.border-zinc-300{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity))}.border-zinc-400{--tw-border-opacity:1;border-color:rgb(161 161 170/var(--tw-border-opacity))}.border-zinc-50{--tw-border-opacity:1;border-color:rgb(250 250 250/var(--tw-border-opacity))}.border-zinc-500{--tw-border-opacity:1;border-color:rgb(113 113 122/var(--tw-border-opacity))}.border-zinc-600{--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity))}.border-zinc-700{--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity))}.border-zinc-800{--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity))}.border-zinc-900{--tw-border-opacity:1;border-color:rgb(24 24 27/var(--tw-border-opacity))}.border-b-black{--tw-border-opacity:1;border-bottom-color:rgb(17 17 17/var(--tw-border-opacity))}.border-b-subtle{border-bottom-color:var(--cal-border-subtle,#e5e7eb)}.border-l-default{border-left-color:var(--cal-border,#d1d5db)}.border-r-transparent{border-right-color:transparent}.\!bg-default{background-color:var(--cal-bg,#fff)!important}.\!bg-orange-100{--tw-bg-opacity:1!important;background-color:rgb(255 237 213/var(--tw-bg-opacity))!important}.\!bg-subtle{background-color:var(--cal-bg-subtle)!important}.\!bg-success{background-color:var(--cal-bg-success)!important}.\!bg-transparent{background-color:transparent!important}.bg-\[\#FAFAFA\]{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity))}.bg-\[\#ffc439\]{--tw-bg-opacity:1;background-color:rgb(255 196 57/var(--tw-bg-opacity))}.bg-amber-100{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity))}.bg-amber-200{--tw-bg-opacity:1;background-color:rgb(253 230 138/var(--tw-bg-opacity))}.bg-amber-300{--tw-bg-opacity:1;background-color:rgb(252 211 77/var(--tw-bg-opacity))}.bg-amber-400{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity))}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity))}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity))}.bg-amber-600{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity))}.bg-amber-700{--tw-bg-opacity:1;background-color:rgb(180 83 9/var(--tw-bg-opacity))}.bg-amber-800{--tw-bg-opacity:1;background-color:rgb(146 64 14/var(--tw-bg-opacity))}.bg-amber-900{--tw-bg-opacity:1;background-color:rgb(120 53 15/var(--tw-bg-opacity))}.bg-attention{background-color:var(--cal-bg-attention)}.bg-black{--tw-bg-opacity:1;background-color:rgb(17 17 17/var(--tw-bg-opacity))}.bg-black\/50{background-color:hsla(0,0%,7%,.5)}.bg-black\/60{background-color:hsla(0,0%,7%,.6)}.bg-black\/80{background-color:hsla(0,0%,7%,.8)}.bg-black\/\[3\%\]{background-color:hsla(0,0%,7%,.03)}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity))}.bg-blue-200{--tw-bg-opacity:1;background-color:rgb(191 219 254/var(--tw-bg-opacity))}.bg-blue-300{--tw-bg-opacity:1;background-color:rgb(147 197 253/var(--tw-bg-opacity))}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.bg-blue-700{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity))}.bg-blue-800{--tw-bg-opacity:1;background-color:rgb(30 64 175/var(--tw-bg-opacity))}.bg-blue-900{--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity))}.bg-brand-accent{background-color:var(--cal-brand-accent,#fff)}.bg-brand-default{background-color:var(--cal-brand,#111827)}.bg-brand-subtle{background-color:var(--cal-brand-subtle,#9ca3af)}.bg-cyan-100{--tw-bg-opacity:1;background-color:rgb(207 250 254/var(--tw-bg-opacity))}.bg-cyan-200{--tw-bg-opacity:1;background-color:rgb(165 243 252/var(--tw-bg-opacity))}.bg-cyan-300{--tw-bg-opacity:1;background-color:rgb(103 232 249/var(--tw-bg-opacity))}.bg-cyan-400{--tw-bg-opacity:1;background-color:rgb(34 211 238/var(--tw-bg-opacity))}.bg-cyan-50{--tw-bg-opacity:1;background-color:rgb(236 254 255/var(--tw-bg-opacity))}.bg-cyan-500{--tw-bg-opacity:1;background-color:rgb(6 182 212/var(--tw-bg-opacity))}.bg-cyan-600{--tw-bg-opacity:1;background-color:rgb(8 145 178/var(--tw-bg-opacity))}.bg-cyan-700{--tw-bg-opacity:1;background-color:rgb(14 116 144/var(--tw-bg-opacity))}.bg-cyan-800{--tw-bg-opacity:1;background-color:rgb(21 94 117/var(--tw-bg-opacity))}.bg-cyan-900{--tw-bg-opacity:1;background-color:rgb(22 78 99/var(--tw-bg-opacity))}.bg-default{background-color:var(--cal-bg,#fff)}.bg-emerald-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity))}.bg-emerald-200{--tw-bg-opacity:1;background-color:rgb(167 243 208/var(--tw-bg-opacity))}.bg-emerald-300{--tw-bg-opacity:1;background-color:rgb(110 231 183/var(--tw-bg-opacity))}.bg-emerald-400{--tw-bg-opacity:1;background-color:rgb(52 211 153/var(--tw-bg-opacity))}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity))}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity))}.bg-emerald-700{--tw-bg-opacity:1;background-color:rgb(4 120 87/var(--tw-bg-opacity))}.bg-emerald-800{--tw-bg-opacity:1;background-color:rgb(6 95 70/var(--tw-bg-opacity))}.bg-emerald-900{--tw-bg-opacity:1;background-color:rgb(6 78 59/var(--tw-bg-opacity))}.bg-emphasis{background-color:var(--cal-bg-emphasis)}.bg-error{background-color:var(--cal-bg-error)}.bg-fuchsia-100{--tw-bg-opacity:1;background-color:rgb(250 232 255/var(--tw-bg-opacity))}.bg-fuchsia-200{--tw-bg-opacity:1;background-color:rgb(245 208 254/var(--tw-bg-opacity))}.bg-fuchsia-300{--tw-bg-opacity:1;background-color:rgb(240 171 252/var(--tw-bg-opacity))}.bg-fuchsia-400{--tw-bg-opacity:1;background-color:rgb(232 121 249/var(--tw-bg-opacity))}.bg-fuchsia-50{--tw-bg-opacity:1;background-color:rgb(253 244 255/var(--tw-bg-opacity))}.bg-fuchsia-500{--tw-bg-opacity:1;background-color:rgb(217 70 239/var(--tw-bg-opacity))}.bg-fuchsia-600{--tw-bg-opacity:1;background-color:rgb(192 38 211/var(--tw-bg-opacity))}.bg-fuchsia-700{--tw-bg-opacity:1;background-color:rgb(162 28 175/var(--tw-bg-opacity))}.bg-fuchsia-800{--tw-bg-opacity:1;background-color:rgb(134 25 143/var(--tw-bg-opacity))}.bg-fuchsia-900{--tw-bg-opacity:1;background-color:rgb(112 26 117/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.bg-gray-500\/10{background-color:hsla(220,9%,46%,.1)}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity:1;background-color:rgb(220 252 231/var(--tw-bg-opacity))}.bg-green-200{--tw-bg-opacity:1;background-color:rgb(187 247 208/var(--tw-bg-opacity))}.bg-green-300{--tw-bg-opacity:1;background-color:rgb(134 239 172/var(--tw-bg-opacity))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity))}.bg-green-700{--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity))}.bg-green-800{--tw-bg-opacity:1;background-color:rgb(22 101 52/var(--tw-bg-opacity))}.bg-green-900{--tw-bg-opacity:1;background-color:rgb(20 83 45/var(--tw-bg-opacity))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity))}.bg-indigo-200{--tw-bg-opacity:1;background-color:rgb(199 210 254/var(--tw-bg-opacity))}.bg-indigo-300{--tw-bg-opacity:1;background-color:rgb(165 180 252/var(--tw-bg-opacity))}.bg-indigo-400{--tw-bg-opacity:1;background-color:rgb(129 140 248/var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity:1;background-color:rgb(238 242 255/var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-indigo-700{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity))}.bg-indigo-800{--tw-bg-opacity:1;background-color:rgb(55 48 163/var(--tw-bg-opacity))}.bg-indigo-900{--tw-bg-opacity:1;background-color:rgb(49 46 129/var(--tw-bg-opacity))}.bg-info{background-color:var(--cal-bg-info)}.bg-inherit{background-color:inherit}.bg-inverted{background-color:var(--cal-bg-inverted)}.bg-lime-100{--tw-bg-opacity:1;background-color:rgb(236 252 203/var(--tw-bg-opacity))}.bg-lime-200{--tw-bg-opacity:1;background-color:rgb(217 249 157/var(--tw-bg-opacity))}.bg-lime-300{--tw-bg-opacity:1;background-color:rgb(190 242 100/var(--tw-bg-opacity))}.bg-lime-400{--tw-bg-opacity:1;background-color:rgb(163 230 53/var(--tw-bg-opacity))}.bg-lime-50{--tw-bg-opacity:1;background-color:rgb(247 254 231/var(--tw-bg-opacity))}.bg-lime-500{--tw-bg-opacity:1;background-color:rgb(132 204 22/var(--tw-bg-opacity))}.bg-lime-600{--tw-bg-opacity:1;background-color:rgb(101 163 13/var(--tw-bg-opacity))}.bg-lime-700{--tw-bg-opacity:1;background-color:rgb(77 124 15/var(--tw-bg-opacity))}.bg-lime-800{--tw-bg-opacity:1;background-color:rgb(63 98 18/var(--tw-bg-opacity))}.bg-lime-900{--tw-bg-opacity:1;background-color:rgb(54 83 20/var(--tw-bg-opacity))}.bg-muted{background-color:var(--cal-bg-muted)}.bg-neutral-100{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity))}.bg-neutral-200{--tw-bg-opacity:1;background-color:rgb(229 229 229/var(--tw-bg-opacity))}.bg-neutral-300{--tw-bg-opacity:1;background-color:rgb(212 212 212/var(--tw-bg-opacity))}.bg-neutral-400{--tw-bg-opacity:1;background-color:rgb(163 163 163/var(--tw-bg-opacity))}.bg-neutral-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity))}.bg-neutral-500{--tw-bg-opacity:1;background-color:rgb(115 115 115/var(--tw-bg-opacity))}.bg-neutral-600{--tw-bg-opacity:1;background-color:rgb(82 82 82/var(--tw-bg-opacity))}.bg-neutral-700{--tw-bg-opacity:1;background-color:rgb(64 64 64/var(--tw-bg-opacity))}.bg-neutral-800{--tw-bg-opacity:1;background-color:rgb(38 38 38/var(--tw-bg-opacity))}.bg-neutral-900{--tw-bg-opacity:1;background-color:rgb(23 23 23/var(--tw-bg-opacity))}.bg-orange-100{--tw-bg-opacity:1;background-color:rgb(255 237 213/var(--tw-bg-opacity))}.bg-orange-200{--tw-bg-opacity:1;background-color:rgb(254 215 170/var(--tw-bg-opacity))}.bg-orange-300{--tw-bg-opacity:1;background-color:rgb(253 186 116/var(--tw-bg-opacity))}.bg-orange-400{--tw-bg-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity))}.bg-orange-50{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity))}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity))}.bg-orange-600{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity))}.bg-orange-700{--tw-bg-opacity:1;background-color:rgb(194 65 12/var(--tw-bg-opacity))}.bg-orange-800{--tw-bg-opacity:1;background-color:rgb(154 52 18/var(--tw-bg-opacity))}.bg-orange-900{--tw-bg-opacity:1;background-color:rgb(124 45 18/var(--tw-bg-opacity))}.bg-pink-100{--tw-bg-opacity:1;background-color:rgb(252 231 243/var(--tw-bg-opacity))}.bg-pink-200{--tw-bg-opacity:1;background-color:rgb(251 207 232/var(--tw-bg-opacity))}.bg-pink-300{--tw-bg-opacity:1;background-color:rgb(249 168 212/var(--tw-bg-opacity))}.bg-pink-400{--tw-bg-opacity:1;background-color:rgb(244 114 182/var(--tw-bg-opacity))}.bg-pink-50{--tw-bg-opacity:1;background-color:rgb(253 242 248/var(--tw-bg-opacity))}.bg-pink-500{--tw-bg-opacity:1;background-color:rgb(236 72 153/var(--tw-bg-opacity))}.bg-pink-600{--tw-bg-opacity:1;background-color:rgb(219 39 119/var(--tw-bg-opacity))}.bg-pink-700{--tw-bg-opacity:1;background-color:rgb(190 24 93/var(--tw-bg-opacity))}.bg-pink-900{--tw-bg-opacity:1;background-color:rgb(131 24 67/var(--tw-bg-opacity))}.bg-purple-100{--tw-bg-opacity:1;background-color:rgb(243 232 255/var(--tw-bg-opacity))}.bg-purple-200{--tw-bg-opacity:1;background-color:rgb(233 213 255/var(--tw-bg-opacity))}.bg-purple-300{--tw-bg-opacity:1;background-color:rgb(216 180 254/var(--tw-bg-opacity))}.bg-purple-400{--tw-bg-opacity:1;background-color:rgb(192 132 252/var(--tw-bg-opacity))}.bg-purple-50{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity))}.bg-purple-500{--tw-bg-opacity:1;background-color:rgb(168 85 247/var(--tw-bg-opacity))}.bg-purple-600{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity))}.bg-purple-700{--tw-bg-opacity:1;background-color:rgb(126 34 206/var(--tw-bg-opacity))}.bg-purple-800{--tw-bg-opacity:1;background-color:rgb(107 33 168/var(--tw-bg-opacity))}.bg-purple-900{--tw-bg-opacity:1;background-color:rgb(88 28 135/var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-red-200{--tw-bg-opacity:1;background-color:rgb(254 202 202/var(--tw-bg-opacity))}.bg-red-300{--tw-bg-opacity:1;background-color:rgb(252 165 165/var(--tw-bg-opacity))}.bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity))}.bg-red-700{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity))}.bg-red-800{--tw-bg-opacity:1;background-color:rgb(153 27 27/var(--tw-bg-opacity))}.bg-red-900{--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity))}.bg-rose-100{--tw-bg-opacity:1;background-color:rgb(255 228 230/var(--tw-bg-opacity))}.bg-rose-200{--tw-bg-opacity:1;background-color:rgb(254 205 211/var(--tw-bg-opacity))}.bg-rose-300{--tw-bg-opacity:1;background-color:rgb(253 164 175/var(--tw-bg-opacity))}.bg-rose-400{--tw-bg-opacity:1;background-color:rgb(251 113 133/var(--tw-bg-opacity))}.bg-rose-50{--tw-bg-opacity:1;background-color:rgb(255 241 242/var(--tw-bg-opacity))}.bg-rose-500{--tw-bg-opacity:1;background-color:rgb(244 63 94/var(--tw-bg-opacity))}.bg-rose-600{--tw-bg-opacity:1;background-color:rgb(225 29 72/var(--tw-bg-opacity))}.bg-rose-700{--tw-bg-opacity:1;background-color:rgb(190 18 60/var(--tw-bg-opacity))}.bg-rose-800{--tw-bg-opacity:1;background-color:rgb(159 18 57/var(--tw-bg-opacity))}.bg-rose-900{--tw-bg-opacity:1;background-color:rgb(136 19 55/var(--tw-bg-opacity))}.bg-sky-100{--tw-bg-opacity:1;background-color:rgb(224 242 254/var(--tw-bg-opacity))}.bg-sky-200{--tw-bg-opacity:1;background-color:rgb(186 230 253/var(--tw-bg-opacity))}.bg-sky-300{--tw-bg-opacity:1;background-color:rgb(125 211 252/var(--tw-bg-opacity))}.bg-sky-400{--tw-bg-opacity:1;background-color:rgb(56 189 248/var(--tw-bg-opacity))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity))}.bg-sky-500{--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity))}.bg-sky-600{--tw-bg-opacity:1;background-color:rgb(2 132 199/var(--tw-bg-opacity))}.bg-sky-700{--tw-bg-opacity:1;background-color:rgb(3 105 161/var(--tw-bg-opacity))}.bg-sky-800{--tw-bg-opacity:1;background-color:rgb(7 89 133/var(--tw-bg-opacity))}.bg-sky-900{--tw-bg-opacity:1;background-color:rgb(12 74 110/var(--tw-bg-opacity))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity))}.bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity))}.bg-slate-300{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity))}.bg-slate-400{--tw-bg-opacity:1;background-color:rgb(148 163 184/var(--tw-bg-opacity))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity))}.bg-slate-500{--tw-bg-opacity:1;background-color:rgb(100 116 139/var(--tw-bg-opacity))}.bg-slate-600{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity))}.bg-slate-700{--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity))}.bg-slate-800{--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity))}.bg-slate-900{--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity))}.bg-stone-100{--tw-bg-opacity:1;background-color:rgb(245 245 244/var(--tw-bg-opacity))}.bg-stone-200{--tw-bg-opacity:1;background-color:rgb(231 229 228/var(--tw-bg-opacity))}.bg-stone-300{--tw-bg-opacity:1;background-color:rgb(214 211 209/var(--tw-bg-opacity))}.bg-stone-400{--tw-bg-opacity:1;background-color:rgb(168 162 158/var(--tw-bg-opacity))}.bg-stone-50{--tw-bg-opacity:1;background-color:rgb(250 250 249/var(--tw-bg-opacity))}.bg-stone-500{--tw-bg-opacity:1;background-color:rgb(120 113 108/var(--tw-bg-opacity))}.bg-stone-600{--tw-bg-opacity:1;background-color:rgb(87 83 78/var(--tw-bg-opacity))}.bg-stone-700{--tw-bg-opacity:1;background-color:rgb(68 64 60/var(--tw-bg-opacity))}.bg-stone-800{--tw-bg-opacity:1;background-color:rgb(41 37 36/var(--tw-bg-opacity))}.bg-stone-900{--tw-bg-opacity:1;background-color:rgb(28 25 23/var(--tw-bg-opacity))}.bg-subtle{background-color:var(--cal-bg-subtle)}.bg-success{background-color:var(--cal-bg-success)}.bg-teal-100{--tw-bg-opacity:1;background-color:rgb(204 251 241/var(--tw-bg-opacity))}.bg-teal-200{--tw-bg-opacity:1;background-color:rgb(153 246 228/var(--tw-bg-opacity))}.bg-teal-300{--tw-bg-opacity:1;background-color:rgb(94 234 212/var(--tw-bg-opacity))}.bg-teal-400{--tw-bg-opacity:1;background-color:rgb(45 212 191/var(--tw-bg-opacity))}.bg-teal-50{--tw-bg-opacity:1;background-color:rgb(240 253 250/var(--tw-bg-opacity))}.bg-teal-500{--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity))}.bg-teal-600{--tw-bg-opacity:1;background-color:rgb(13 148 136/var(--tw-bg-opacity))}.bg-teal-700{--tw-bg-opacity:1;background-color:rgb(15 118 110/var(--tw-bg-opacity))}.bg-teal-800{--tw-bg-opacity:1;background-color:rgb(17 94 89/var(--tw-bg-opacity))}.bg-teal-900{--tw-bg-opacity:1;background-color:rgb(19 78 74/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-violet-100{--tw-bg-opacity:1;background-color:rgb(237 233 254/var(--tw-bg-opacity))}.bg-violet-200{--tw-bg-opacity:1;background-color:rgb(221 214 254/var(--tw-bg-opacity))}.bg-violet-300{--tw-bg-opacity:1;background-color:rgb(196 181 253/var(--tw-bg-opacity))}.bg-violet-400{--tw-bg-opacity:1;background-color:rgb(167 139 250/var(--tw-bg-opacity))}.bg-violet-50{--tw-bg-opacity:1;background-color:rgb(245 243 255/var(--tw-bg-opacity))}.bg-violet-500{--tw-bg-opacity:1;background-color:rgb(139 92 246/var(--tw-bg-opacity))}.bg-violet-600{--tw-bg-opacity:1;background-color:rgb(124 58 237/var(--tw-bg-opacity))}.bg-violet-700{--tw-bg-opacity:1;background-color:rgb(109 40 217/var(--tw-bg-opacity))}.bg-violet-800{--tw-bg-opacity:1;background-color:rgb(91 33 182/var(--tw-bg-opacity))}.bg-violet-900{--tw-bg-opacity:1;background-color:rgb(76 29 149/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(254 240 138/var(--tw-bg-opacity))}.bg-yellow-300{--tw-bg-opacity:1;background-color:rgb(253 224 71/var(--tw-bg-opacity))}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity))}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgb(234 179 8/var(--tw-bg-opacity))}.bg-yellow-600{--tw-bg-opacity:1;background-color:rgb(202 138 4/var(--tw-bg-opacity))}.bg-yellow-700{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}.bg-yellow-800{--tw-bg-opacity:1;background-color:rgb(133 77 14/var(--tw-bg-opacity))}.bg-yellow-900{--tw-bg-opacity:1;background-color:rgb(113 63 18/var(--tw-bg-opacity))}.bg-zinc-100{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity))}.bg-zinc-200{--tw-bg-opacity:1;background-color:rgb(228 228 231/var(--tw-bg-opacity))}.bg-zinc-300{--tw-bg-opacity:1;background-color:rgb(212 212 216/var(--tw-bg-opacity))}.bg-zinc-400{--tw-bg-opacity:1;background-color:rgb(161 161 170/var(--tw-bg-opacity))}.bg-zinc-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity))}.bg-zinc-500{--tw-bg-opacity:1;background-color:rgb(113 113 122/var(--tw-bg-opacity))}.bg-zinc-600{--tw-bg-opacity:1;background-color:rgb(82 82 91/var(--tw-bg-opacity))}.bg-zinc-700{--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity))}.bg-zinc-800{--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity))}.bg-zinc-900{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity))}.bg-opacity-30{--tw-bg-opacity:0.3}.bg-opacity-40{--tw-bg-opacity:0.4}.bg-opacity-5{--tw-bg-opacity:0.05}.bg-opacity-50{--tw-bg-opacity:0.5}.bg-opacity-70{--tw-bg-opacity:0.7}.bg-opacity-90{--tw-bg-opacity:0.9}.bg-gradient-primary{background-image:radial-gradient(162.05% 170% at 109.58% 35%,#667593 0,#e3e3e3 100%)}.bg-gradient-to-l{background-image:linear-gradient(to left,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-transparent{--tw-gradient-from:transparent var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-default{--tw-gradient-to:var(--cal-bg,#fff) var(--tw-gradient-to-position)}.bg-cover{background-size:cover}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.bg-center{background-position:50%}.bg-repeat{background-repeat:repeat}.bg-no-repeat{background-repeat:no-repeat}.fill-blue-500{fill:#3b82f6}.fill-current{fill:currentColor}.fill-default{fill:var(--cal-text,#374151)}.fill-emphasis{fill:var(--cal-text-emphasis,#111827)}.fill-muted{fill:var(--cal-text-muted,#9ca3af)}.fill-orange-400{fill:#fb923c}.fill-transparent{fill:transparent}.stroke-2{stroke-width:2}.stroke-\[1\.3px\]{stroke-width:1.3px}.stroke-\[1\.5px\]{stroke-width:1.5px}.stroke-\[2\.5px\]{stroke-width:2.5px}.stroke-\[2px\]{stroke-width:2px}.stroke-\[3px\]{stroke-width:3px}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.object-left{-o-object-position:left;object-position:left}.\!p-0{padding:0!important}.\!p-1{padding:.25rem!important}.\!p-2{padding:.5rem!important}.\!p-5{padding:1.25rem!important}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-10{padding:2.5rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-7{padding:1.75rem}.p-8{padding:2rem}.p-\[1px\]{padding:1px}.p-\[6px\]{padding:6px}.p-px{padding:1px}.\!px-0{padding-left:0!important;padding-right:0!important}.\!py-0{padding-top:0!important;padding-bottom:0!important}.px-0{padding-left:0;padding-right:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-12{padding-left:3rem;padding-right:3rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.px-9{padding-left:2.25rem;padding-right:2.25rem}.px-\[10px\]{padding-left:10px;padding-right:10px}.px-\[6px\]{padding-left:6px;padding-right:6px}.px-px{padding-left:1px;padding-right:1px}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-14{padding-top:3.5rem;padding-bottom:3.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-\[10px\]{padding-top:10px;padding-bottom:10px}.py-\[10x\]{padding-top:10x;padding-bottom:10x}.py-\[19px\]{padding-top:19px;padding-bottom:19px}.py-\[2px\]{padding-top:2px;padding-bottom:2px}.py-\[5px\]{padding-top:5px;padding-bottom:5px}.py-\[6px\]{padding-top:6px;padding-bottom:6px}.py-px{padding-top:1px;padding-bottom:1px}.\!pb-0{padding-bottom:0!important}.\!pl-0{padding-left:0!important}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pb-10{padding-bottom:2.5rem}.pb-11{padding-bottom:2.75rem}.pb-12{padding-bottom:3rem}.pb-2{padding-bottom:.5rem}.pb-2\.5{padding-bottom:.625rem}.pb-20{padding-bottom:5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-5{padding-bottom:1.25rem}.pb-6{padding-bottom:1.5rem}.pb-7{padding-bottom:1.75rem}.pb-8{padding-bottom:2rem}.pb-\[1\.5px\]{padding-bottom:1.5px}.pb-px{padding-bottom:1px}.pe-2{-webkit-padding-end:.5rem;padding-inline-end:.5rem}.pe-3{-webkit-padding-end:.75rem;padding-inline-end:.75rem}.pl-0{padding-left:0}.pl-1{padding-left:.25rem}.pl-10{padding-left:2.5rem}.pl-16{padding-left:4rem}.pl-2{padding-left:.5rem}.pl-2\.5{padding-left:.625rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-5{padding-left:1.25rem}.pl-6{padding-left:1.5rem}.pl-px{padding-left:1px}.pr-0{padding-right:0}.pr-1{padding-right:.25rem}.pr-10{padding-right:2.5rem}.pr-12{padding-right:3rem}.pr-2{padding-right:.5rem}.pr-2\.5{padding-right:.625rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pr-6{padding-right:1.5rem}.pr-8{padding-right:2rem}.pr-px{padding-right:1px}.ps-1{-webkit-padding-start:.25rem;padding-inline-start:.25rem}.ps-6{-webkit-padding-start:1.5rem;padding-inline-start:1.5rem}.pt-0{padding-top:0}.pt-0\.5{padding-top:.125rem}.pt-1{padding-top:.25rem}.pt-12{padding-top:3rem}.pt-16{padding-top:4rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.pt-9{padding-top:2.25rem}.pt-\[100\%\]{padding-top:100%}.pt-\[6px\]{padding-top:6px}.pt-px{padding-top:1px}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-baseline{vertical-align:baseline}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.align-text-top{vertical-align:text-top}.\!font-cal{font-family:var(--font-cal),ui-serif,Georgia,Cambria,times new roman,Times,serif!important}.font-cal{font-family:var(--font-cal),ui-serif,Georgia,Cambria,times new roman,Times,serif}.font-mono{font-family:Roboto Mono,monospace}.font-sans{font-family:var(--font-inter),ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji}.\!text-xl{font-size:1.25rem!important;line-height:1.75rem!important}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-6xl{font-size:3.75rem;line-height:1}.text-\[0\.5rem\]{font-size:.5rem}.text-\[0px\]{font-size:0}.text-\[10px\]{font-size:10px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[28px\]{font-size:28px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.\!font-normal{font-weight:400!important}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-extralight{font-weight:200}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.italic{font-style:italic}.not-italic{font-style:normal}.normal-nums{font-variant-numeric:normal}.ordinal{--tw-ordinal:ordinal}.ordinal,.slashed-zero{font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.slashed-zero{--tw-slashed-zero:slashed-zero}.lining-nums{--tw-numeric-figure:lining-nums}.lining-nums,.oldstyle-nums{font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.oldstyle-nums{--tw-numeric-figure:oldstyle-nums}.proportional-nums{--tw-numeric-spacing:proportional-nums}.proportional-nums,.tabular-nums{font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tabular-nums{--tw-numeric-spacing:tabular-nums}.diagonal-fractions{--tw-numeric-fraction:diagonal-fractions;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-3{line-height:.75rem}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-7{line-height:1.75rem}.leading-\[0\]{line-height:0}.leading-\[18px\]{line-height:18px}.leading-none{line-height:1}.leading-normal{line-height:1.5}.leading-tight{line-height:1.25}.tracking-\[0\.01em\]{letter-spacing:.01em}.tracking-normal{letter-spacing:0}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.\!text-default{color:var(--cal-text,#374151)!important}.\!text-emphasis{color:var(--cal-text-emphasis,#111827)!important}.\!text-green-700{--tw-text-opacity:1!important;color:rgb(21 128 61/var(--tw-text-opacity))!important}.\!text-orange-900{--tw-text-opacity:1!important;color:rgb(124 45 18/var(--tw-text-opacity))!important}.\!text-red-700{--tw-text-opacity:1!important;color:rgb(185 28 28/var(--tw-text-opacity))!important}.text-\[\#5B93F9\]{--tw-text-opacity:1;color:rgb(91 147 249/var(--tw-text-opacity))}.text-\[--cal-brand\]{color:var(--cal-brand)}.text-amber-100{--tw-text-opacity:1;color:rgb(254 243 199/var(--tw-text-opacity))}.text-amber-200{--tw-text-opacity:1;color:rgb(253 230 138/var(--tw-text-opacity))}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity))}.text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity))}.text-amber-50{--tw-text-opacity:1;color:rgb(255 251 235/var(--tw-text-opacity))}.text-amber-500{--tw-text-opacity:1;color:rgb(245 158 11/var(--tw-text-opacity))}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity))}.text-amber-700{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity))}.text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity))}.text-amber-900{--tw-text-opacity:1;color:rgb(120 53 15/var(--tw-text-opacity))}.text-attention{color:var(--cal-text-attention,#73321b)}.text-black{--tw-text-opacity:1;color:rgb(17 17 17/var(--tw-text-opacity))}.text-blue-100{--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity))}.text-blue-200{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity))}.text-blue-300{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity))}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}.text-blue-50{--tw-text-opacity:1;color:rgb(239 246 255/var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.text-blue-800{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity))}.text-blue-900{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity))}.text-brand{color:var(--cal-brand-text,"white")}.text-current{color:currentColor}.text-cyan-100{--tw-text-opacity:1;color:rgb(207 250 254/var(--tw-text-opacity))}.text-cyan-200{--tw-text-opacity:1;color:rgb(165 243 252/var(--tw-text-opacity))}.text-cyan-300{--tw-text-opacity:1;color:rgb(103 232 249/var(--tw-text-opacity))}.text-cyan-400{--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity))}.text-cyan-50{--tw-text-opacity:1;color:rgb(236 254 255/var(--tw-text-opacity))}.text-cyan-500{--tw-text-opacity:1;color:rgb(6 182 212/var(--tw-text-opacity))}.text-cyan-600{--tw-text-opacity:1;color:rgb(8 145 178/var(--tw-text-opacity))}.text-cyan-700{--tw-text-opacity:1;color:rgb(14 116 144/var(--tw-text-opacity))}.text-cyan-800{--tw-text-opacity:1;color:rgb(21 94 117/var(--tw-text-opacity))}.text-cyan-900{--tw-text-opacity:1;color:rgb(22 78 99/var(--tw-text-opacity))}.text-default{color:var(--cal-text,#374151)}.text-emerald-100{--tw-text-opacity:1;color:rgb(209 250 229/var(--tw-text-opacity))}.text-emerald-200{--tw-text-opacity:1;color:rgb(167 243 208/var(--tw-text-opacity))}.text-emerald-300{--tw-text-opacity:1;color:rgb(110 231 183/var(--tw-text-opacity))}.text-emerald-400{--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity))}.text-emerald-50{--tw-text-opacity:1;color:rgb(236 253 245/var(--tw-text-opacity))}.text-emerald-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity))}.text-emerald-700{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity))}.text-emerald-800{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity))}.text-emerald-900{--tw-text-opacity:1;color:rgb(6 78 59/var(--tw-text-opacity))}.text-emphasis{color:var(--cal-text-emphasis,#111827)}.text-error{color:var(--cal-text-error,#752522)}.text-fuchsia-100{--tw-text-opacity:1;color:rgb(250 232 255/var(--tw-text-opacity))}.text-fuchsia-200{--tw-text-opacity:1;color:rgb(245 208 254/var(--tw-text-opacity))}.text-fuchsia-300{--tw-text-opacity:1;color:rgb(240 171 252/var(--tw-text-opacity))}.text-fuchsia-400{--tw-text-opacity:1;color:rgb(232 121 249/var(--tw-text-opacity))}.text-fuchsia-50{--tw-text-opacity:1;color:rgb(253 244 255/var(--tw-text-opacity))}.text-fuchsia-500{--tw-text-opacity:1;color:rgb(217 70 239/var(--tw-text-opacity))}.text-fuchsia-600{--tw-text-opacity:1;color:rgb(192 38 211/var(--tw-text-opacity))}.text-fuchsia-700{--tw-text-opacity:1;color:rgb(162 28 175/var(--tw-text-opacity))}.text-fuchsia-800{--tw-text-opacity:1;color:rgb(134 25 143/var(--tw-text-opacity))}.text-fuchsia-900{--tw-text-opacity:1;color:rgb(112 26 117/var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-green-100{--tw-text-opacity:1;color:rgb(220 252 231/var(--tw-text-opacity))}.text-green-200{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity))}.text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity))}.text-green-50{--tw-text-opacity:1;color:rgb(240 253 244/var(--tw-text-opacity))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity))}.text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity))}.text-green-900{--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity))}.text-indigo-100{--tw-text-opacity:1;color:rgb(224 231 255/var(--tw-text-opacity))}.text-indigo-200{--tw-text-opacity:1;color:rgb(199 210 254/var(--tw-text-opacity))}.text-indigo-300{--tw-text-opacity:1;color:rgb(165 180 252/var(--tw-text-opacity))}.text-indigo-400{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity))}.text-indigo-50{--tw-text-opacity:1;color:rgb(238 242 255/var(--tw-text-opacity))}.text-indigo-500{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.text-indigo-700{--tw-text-opacity:1;color:rgb(67 56 202/var(--tw-text-opacity))}.text-indigo-800{--tw-text-opacity:1;color:rgb(55 48 163/var(--tw-text-opacity))}.text-indigo-900{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity))}.text-info{color:var(--cal-text-info,#253985)}.text-inherit{color:inherit}.text-inverted{color:var(--cal-text-inverted,#fff)}.text-lime-100{--tw-text-opacity:1;color:rgb(236 252 203/var(--tw-text-opacity))}.text-lime-200{--tw-text-opacity:1;color:rgb(217 249 157/var(--tw-text-opacity))}.text-lime-300{--tw-text-opacity:1;color:rgb(190 242 100/var(--tw-text-opacity))}.text-lime-400{--tw-text-opacity:1;color:rgb(163 230 53/var(--tw-text-opacity))}.text-lime-50{--tw-text-opacity:1;color:rgb(247 254 231/var(--tw-text-opacity))}.text-lime-500{--tw-text-opacity:1;color:rgb(132 204 22/var(--tw-text-opacity))}.text-lime-600{--tw-text-opacity:1;color:rgb(101 163 13/var(--tw-text-opacity))}.text-lime-700{--tw-text-opacity:1;color:rgb(77 124 15/var(--tw-text-opacity))}.text-lime-800{--tw-text-opacity:1;color:rgb(63 98 18/var(--tw-text-opacity))}.text-lime-900{--tw-text-opacity:1;color:rgb(54 83 20/var(--tw-text-opacity))}.text-muted{color:var(--cal-text-muted,#9ca3af)}.text-neutral-100{--tw-text-opacity:1;color:rgb(245 245 245/var(--tw-text-opacity))}.text-neutral-200{--tw-text-opacity:1;color:rgb(229 229 229/var(--tw-text-opacity))}.text-neutral-300{--tw-text-opacity:1;color:rgb(212 212 212/var(--tw-text-opacity))}.text-neutral-400{--tw-text-opacity:1;color:rgb(163 163 163/var(--tw-text-opacity))}.text-neutral-50{--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity))}.text-neutral-500{--tw-text-opacity:1;color:rgb(115 115 115/var(--tw-text-opacity))}.text-neutral-600{--tw-text-opacity:1;color:rgb(82 82 82/var(--tw-text-opacity))}.text-neutral-700{--tw-text-opacity:1;color:rgb(64 64 64/var(--tw-text-opacity))}.text-neutral-800{--tw-text-opacity:1;color:rgb(38 38 38/var(--tw-text-opacity))}.text-neutral-900{--tw-text-opacity:1;color:rgb(23 23 23/var(--tw-text-opacity))}.text-orange-100{--tw-text-opacity:1;color:rgb(255 237 213/var(--tw-text-opacity))}.text-orange-200{--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity))}.text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity))}.text-orange-400{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity))}.text-orange-50{--tw-text-opacity:1;color:rgb(255 247 237/var(--tw-text-opacity))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity))}.text-orange-600{--tw-text-opacity:1;color:rgb(234 88 12/var(--tw-text-opacity))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity))}.text-orange-800{--tw-text-opacity:1;color:rgb(154 52 18/var(--tw-text-opacity))}.text-orange-900{--tw-text-opacity:1;color:rgb(124 45 18/var(--tw-text-opacity))}.text-pink-100{--tw-text-opacity:1;color:rgb(252 231 243/var(--tw-text-opacity))}.text-pink-200{--tw-text-opacity:1;color:rgb(251 207 232/var(--tw-text-opacity))}.text-pink-300{--tw-text-opacity:1;color:rgb(249 168 212/var(--tw-text-opacity))}.text-pink-400{--tw-text-opacity:1;color:rgb(244 114 182/var(--tw-text-opacity))}.text-pink-50{--tw-text-opacity:1;color:rgb(253 242 248/var(--tw-text-opacity))}.text-pink-500{--tw-text-opacity:1;color:rgb(236 72 153/var(--tw-text-opacity))}.text-pink-600{--tw-text-opacity:1;color:rgb(219 39 119/var(--tw-text-opacity))}.text-pink-700{--tw-text-opacity:1;color:rgb(190 24 93/var(--tw-text-opacity))}.text-pink-900{--tw-text-opacity:1;color:rgb(131 24 67/var(--tw-text-opacity))}.text-purple-100{--tw-text-opacity:1;color:rgb(243 232 255/var(--tw-text-opacity))}.text-purple-200{--tw-text-opacity:1;color:rgb(233 213 255/var(--tw-text-opacity))}.text-purple-300{--tw-text-opacity:1;color:rgb(216 180 254/var(--tw-text-opacity))}.text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity))}.text-purple-50{--tw-text-opacity:1;color:rgb(250 245 255/var(--tw-text-opacity))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity))}.text-purple-700{--tw-text-opacity:1;color:rgb(126 34 206/var(--tw-text-opacity))}.text-purple-800{--tw-text-opacity:1;color:rgb(107 33 168/var(--tw-text-opacity))}.text-purple-900{--tw-text-opacity:1;color:rgb(88 28 135/var(--tw-text-opacity))}.text-red-100{--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity))}.text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity))}.text-red-50{--tw-text-opacity:1;color:rgb(254 242 242/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-red-700\/30{color:rgba(185,28,28,.3)}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity))}.text-red-900{--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity))}.text-rose-100{--tw-text-opacity:1;color:rgb(255 228 230/var(--tw-text-opacity))}.text-rose-200{--tw-text-opacity:1;color:rgb(254 205 211/var(--tw-text-opacity))}.text-rose-300{--tw-text-opacity:1;color:rgb(253 164 175/var(--tw-text-opacity))}.text-rose-400{--tw-text-opacity:1;color:rgb(251 113 133/var(--tw-text-opacity))}.text-rose-50{--tw-text-opacity:1;color:rgb(255 241 242/var(--tw-text-opacity))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity))}.text-rose-600{--tw-text-opacity:1;color:rgb(225 29 72/var(--tw-text-opacity))}.text-rose-700{--tw-text-opacity:1;color:rgb(190 18 60/var(--tw-text-opacity))}.text-rose-800{--tw-text-opacity:1;color:rgb(159 18 57/var(--tw-text-opacity))}.text-rose-900{--tw-text-opacity:1;color:rgb(136 19 55/var(--tw-text-opacity))}.text-sky-100{--tw-text-opacity:1;color:rgb(224 242 254/var(--tw-text-opacity))}.text-sky-200{--tw-text-opacity:1;color:rgb(186 230 253/var(--tw-text-opacity))}.text-sky-300{--tw-text-opacity:1;color:rgb(125 211 252/var(--tw-text-opacity))}.text-sky-400{--tw-text-opacity:1;color:rgb(56 189 248/var(--tw-text-opacity))}.text-sky-50{--tw-text-opacity:1;color:rgb(240 249 255/var(--tw-text-opacity))}.text-sky-500{--tw-text-opacity:1;color:rgb(14 165 233/var(--tw-text-opacity))}.text-sky-600{--tw-text-opacity:1;color:rgb(2 132 199/var(--tw-text-opacity))}.text-sky-700{--tw-text-opacity:1;color:rgb(3 105 161/var(--tw-text-opacity))}.text-sky-800{--tw-text-opacity:1;color:rgb(7 89 133/var(--tw-text-opacity))}.text-sky-900{--tw-text-opacity:1;color:rgb(12 74 110/var(--tw-text-opacity))}.text-slate-100{--tw-text-opacity:1;color:rgb(241 245 249/var(--tw-text-opacity))}.text-slate-200{--tw-text-opacity:1;color:rgb(226 232 240/var(--tw-text-opacity))}.text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity))}.text-slate-50{--tw-text-opacity:1;color:rgb(248 250 252/var(--tw-text-opacity))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity))}.text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity))}.text-stone-100{--tw-text-opacity:1;color:rgb(245 245 244/var(--tw-text-opacity))}.text-stone-200{--tw-text-opacity:1;color:rgb(231 229 228/var(--tw-text-opacity))}.text-stone-300{--tw-text-opacity:1;color:rgb(214 211 209/var(--tw-text-opacity))}.text-stone-400{--tw-text-opacity:1;color:rgb(168 162 158/var(--tw-text-opacity))}.text-stone-50{--tw-text-opacity:1;color:rgb(250 250 249/var(--tw-text-opacity))}.text-stone-500{--tw-text-opacity:1;color:rgb(120 113 108/var(--tw-text-opacity))}.text-stone-600{--tw-text-opacity:1;color:rgb(87 83 78/var(--tw-text-opacity))}.text-stone-700{--tw-text-opacity:1;color:rgb(68 64 60/var(--tw-text-opacity))}.text-stone-800{--tw-text-opacity:1;color:rgb(41 37 36/var(--tw-text-opacity))}.text-stone-900{--tw-text-opacity:1;color:rgb(28 25 23/var(--tw-text-opacity))}.text-subtle{color:var(--cal-text-subtle,#6b7280)}.text-success{color:var(--cal-text-success,#285231)}.text-teal-100{--tw-text-opacity:1;color:rgb(204 251 241/var(--tw-text-opacity))}.text-teal-200{--tw-text-opacity:1;color:rgb(153 246 228/var(--tw-text-opacity))}.text-teal-300{--tw-text-opacity:1;color:rgb(94 234 212/var(--tw-text-opacity))}.text-teal-400{--tw-text-opacity:1;color:rgb(45 212 191/var(--tw-text-opacity))}.text-teal-50{--tw-text-opacity:1;color:rgb(240 253 250/var(--tw-text-opacity))}.text-teal-500{--tw-text-opacity:1;color:rgb(20 184 166/var(--tw-text-opacity))}.text-teal-600{--tw-text-opacity:1;color:rgb(13 148 136/var(--tw-text-opacity))}.text-teal-700{--tw-text-opacity:1;color:rgb(15 118 110/var(--tw-text-opacity))}.text-teal-800{--tw-text-opacity:1;color:rgb(17 94 89/var(--tw-text-opacity))}.text-teal-900{--tw-text-opacity:1;color:rgb(19 78 74/var(--tw-text-opacity))}.text-transparent{color:transparent}.text-violet-100{--tw-text-opacity:1;color:rgb(237 233 254/var(--tw-text-opacity))}.text-violet-200{--tw-text-opacity:1;color:rgb(221 214 254/var(--tw-text-opacity))}.text-violet-300{--tw-text-opacity:1;color:rgb(196 181 253/var(--tw-text-opacity))}.text-violet-400{--tw-text-opacity:1;color:rgb(167 139 250/var(--tw-text-opacity))}.text-violet-50{--tw-text-opacity:1;color:rgb(245 243 255/var(--tw-text-opacity))}.text-violet-500{--tw-text-opacity:1;color:rgb(139 92 246/var(--tw-text-opacity))}.text-violet-600{--tw-text-opacity:1;color:rgb(124 58 237/var(--tw-text-opacity))}.text-violet-700{--tw-text-opacity:1;color:rgb(109 40 217/var(--tw-text-opacity))}.text-violet-800{--tw-text-opacity:1;color:rgb(91 33 182/var(--tw-text-opacity))}.text-violet-900{--tw-text-opacity:1;color:rgb(76 29 149/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-yellow-100{--tw-text-opacity:1;color:rgb(254 249 195/var(--tw-text-opacity))}.text-yellow-200{--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity))}.text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity))}.text-yellow-50{--tw-text-opacity:1;color:rgb(254 252 232/var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity:1;color:rgb(234 179 8/var(--tw-text-opacity))}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity))}.text-yellow-700{--tw-text-opacity:1;color:rgb(161 98 7/var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity))}.text-yellow-900{--tw-text-opacity:1;color:rgb(113 63 18/var(--tw-text-opacity))}.text-zinc-100{--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity))}.text-zinc-200{--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity))}.text-zinc-300{--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity))}.text-zinc-400{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity))}.text-zinc-50{--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity))}.text-zinc-500{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity))}.text-zinc-600{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity))}.text-zinc-700{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity))}.text-zinc-800{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity))}.text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity))}.underline{text-decoration-line:underline}.overline{text-decoration-line:overline}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.decoration-gray-400{text-decoration-color:#9ca3af}.decoration-dashed{text-decoration-style:dashed}.underline-offset-2{text-underline-offset:2px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subpixel-antialiased{-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.\!opacity-30{opacity:.3!important}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-20{opacity:.2}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.\!shadow-none{--tw-shadow:0 0 #0000!important;--tw-shadow-colored:0 0 #0000!important;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)!important}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-dropdown{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-dropdown{--tw-shadow:0px 2px 6px -1px rgba(0,0,0,.08);--tw-shadow-colored:0px 2px 6px -1px var(--tw-shadow-color)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 rgba(0,0,0,.05);--tw-shadow-colored:inset 0 2px 4px 0 var(--tw-shadow-color)}.shadow-inner,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-none{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.outline-amber-100{outline-color:#fef3c7}.outline-amber-200{outline-color:#fde68a}.outline-amber-300{outline-color:#fcd34d}.outline-amber-400{outline-color:#fbbf24}.outline-amber-50{outline-color:#fffbeb}.outline-amber-500{outline-color:#f59e0b}.outline-amber-600{outline-color:#d97706}.outline-amber-700{outline-color:#b45309}.outline-amber-800{outline-color:#92400e}.outline-amber-900{outline-color:#78350f}.outline-black{outline-color:#111}.outline-blue-100{outline-color:#dbeafe}.outline-blue-200{outline-color:#bfdbfe}.outline-blue-300{outline-color:#93c5fd}.outline-blue-400{outline-color:#60a5fa}.outline-blue-50{outline-color:#eff6ff}.outline-blue-500{outline-color:#3b82f6}.outline-blue-600{outline-color:#2563eb}.outline-blue-700{outline-color:#1d4ed8}.outline-blue-800{outline-color:#1e40af}.outline-blue-900{outline-color:#1e3a8a}.outline-cyan-100{outline-color:#cffafe}.outline-cyan-200{outline-color:#a5f3fc}.outline-cyan-300{outline-color:#67e8f9}.outline-cyan-400{outline-color:#22d3ee}.outline-cyan-50{outline-color:#ecfeff}.outline-cyan-500{outline-color:#06b6d4}.outline-cyan-600{outline-color:#0891b2}.outline-cyan-700{outline-color:#0e7490}.outline-cyan-800{outline-color:#155e75}.outline-cyan-900{outline-color:#164e63}.outline-emerald-100{outline-color:#d1fae5}.outline-emerald-200{outline-color:#a7f3d0}.outline-emerald-300{outline-color:#6ee7b7}.outline-emerald-400{outline-color:#34d399}.outline-emerald-50{outline-color:#ecfdf5}.outline-emerald-500{outline-color:#10b981}.outline-emerald-600{outline-color:#059669}.outline-emerald-700{outline-color:#047857}.outline-emerald-800{outline-color:#065f46}.outline-emerald-900{outline-color:#064e3b}.outline-fuchsia-100{outline-color:#fae8ff}.outline-fuchsia-200{outline-color:#f5d0fe}.outline-fuchsia-300{outline-color:#f0abfc}.outline-fuchsia-400{outline-color:#e879f9}.outline-fuchsia-50{outline-color:#fdf4ff}.outline-fuchsia-500{outline-color:#d946ef}.outline-fuchsia-600{outline-color:#c026d3}.outline-fuchsia-700{outline-color:#a21caf}.outline-fuchsia-800{outline-color:#86198f}.outline-fuchsia-900{outline-color:#701a75}.outline-gray-100{outline-color:#f3f4f6}.outline-gray-200{outline-color:#e5e7eb}.outline-gray-300{outline-color:#d1d5db}.outline-gray-400{outline-color:#9ca3af}.outline-gray-50{outline-color:#f9fafb}.outline-gray-500{outline-color:#6b7280}.outline-gray-600{outline-color:#4b5563}.outline-gray-700{outline-color:#374151}.outline-gray-800{outline-color:#1f2937}.outline-gray-900{outline-color:#111827}.outline-green-100{outline-color:#dcfce7}.outline-green-200{outline-color:#bbf7d0}.outline-green-300{outline-color:#86efac}.outline-green-400{outline-color:#4ade80}.outline-green-50{outline-color:#f0fdf4}.outline-green-500{outline-color:#22c55e}.outline-green-600{outline-color:#16a34a}.outline-green-700{outline-color:#15803d}.outline-green-800{outline-color:#166534}.outline-green-900{outline-color:#14532d}.outline-indigo-100{outline-color:#e0e7ff}.outline-indigo-200{outline-color:#c7d2fe}.outline-indigo-300{outline-color:#a5b4fc}.outline-indigo-400{outline-color:#818cf8}.outline-indigo-50{outline-color:#eef2ff}.outline-indigo-500{outline-color:#6366f1}.outline-indigo-600{outline-color:#4f46e5}.outline-indigo-700{outline-color:#4338ca}.outline-indigo-800{outline-color:#3730a3}.outline-indigo-900{outline-color:#312e81}.outline-lime-100{outline-color:#ecfccb}.outline-lime-200{outline-color:#d9f99d}.outline-lime-300{outline-color:#bef264}.outline-lime-400{outline-color:#a3e635}.outline-lime-50{outline-color:#f7fee7}.outline-lime-500{outline-color:#84cc16}.outline-lime-600{outline-color:#65a30d}.outline-lime-700{outline-color:#4d7c0f}.outline-lime-800{outline-color:#3f6212}.outline-lime-900{outline-color:#365314}.outline-neutral-100{outline-color:#f5f5f5}.outline-neutral-200{outline-color:#e5e5e5}.outline-neutral-300{outline-color:#d4d4d4}.outline-neutral-400{outline-color:#a3a3a3}.outline-neutral-50{outline-color:#fafafa}.outline-neutral-500{outline-color:#737373}.outline-neutral-600{outline-color:#525252}.outline-neutral-700{outline-color:#404040}.outline-neutral-800{outline-color:#262626}.outline-neutral-900{outline-color:#171717}.outline-orange-100{outline-color:#ffedd5}.outline-orange-200{outline-color:#fed7aa}.outline-orange-300{outline-color:#fdba74}.outline-orange-400{outline-color:#fb923c}.outline-orange-50{outline-color:#fff7ed}.outline-orange-500{outline-color:#f97316}.outline-orange-600{outline-color:#ea580c}.outline-orange-700{outline-color:#c2410c}.outline-orange-800{outline-color:#9a3412}.outline-orange-900{outline-color:#7c2d12}.outline-pink-100{outline-color:#fce7f3}.outline-pink-200{outline-color:#fbcfe8}.outline-pink-300{outline-color:#f9a8d4}.outline-pink-400{outline-color:#f472b6}.outline-pink-50{outline-color:#fdf2f8}.outline-pink-500{outline-color:#ec4899}.outline-pink-600{outline-color:#db2777}.outline-pink-700{outline-color:#be185d}.outline-pink-900{outline-color:#831843}.outline-purple-100{outline-color:#f3e8ff}.outline-purple-200{outline-color:#e9d5ff}.outline-purple-300{outline-color:#d8b4fe}.outline-purple-400{outline-color:#c084fc}.outline-purple-50{outline-color:#faf5ff}.outline-purple-500{outline-color:#a855f7}.outline-purple-600{outline-color:#9333ea}.outline-purple-700{outline-color:#7e22ce}.outline-purple-800{outline-color:#6b21a8}.outline-purple-900{outline-color:#581c87}.outline-red-100{outline-color:#fee2e2}.outline-red-200{outline-color:#fecaca}.outline-red-300{outline-color:#fca5a5}.outline-red-400{outline-color:#f87171}.outline-red-50{outline-color:#fef2f2}.outline-red-500{outline-color:#ef4444}.outline-red-600{outline-color:#dc2626}.outline-red-700{outline-color:#b91c1c}.outline-red-800{outline-color:#991b1b}.outline-red-900{outline-color:#7f1d1d}.outline-rose-100{outline-color:#ffe4e6}.outline-rose-200{outline-color:#fecdd3}.outline-rose-300{outline-color:#fda4af}.outline-rose-400{outline-color:#fb7185}.outline-rose-50{outline-color:#fff1f2}.outline-rose-500{outline-color:#f43f5e}.outline-rose-600{outline-color:#e11d48}.outline-rose-700{outline-color:#be123c}.outline-rose-800{outline-color:#9f1239}.outline-rose-900{outline-color:#881337}.outline-sky-100{outline-color:#e0f2fe}.outline-sky-200{outline-color:#bae6fd}.outline-sky-300{outline-color:#7dd3fc}.outline-sky-400{outline-color:#38bdf8}.outline-sky-50{outline-color:#f0f9ff}.outline-sky-500{outline-color:#0ea5e9}.outline-sky-600{outline-color:#0284c7}.outline-sky-700{outline-color:#0369a1}.outline-sky-800{outline-color:#075985}.outline-sky-900{outline-color:#0c4a6e}.outline-slate-100{outline-color:#f1f5f9}.outline-slate-200{outline-color:#e2e8f0}.outline-slate-300{outline-color:#cbd5e1}.outline-slate-400{outline-color:#94a3b8}.outline-slate-50{outline-color:#f8fafc}.outline-slate-500{outline-color:#64748b}.outline-slate-600{outline-color:#475569}.outline-slate-700{outline-color:#334155}.outline-slate-800{outline-color:#1e293b}.outline-slate-900{outline-color:#0f172a}.outline-stone-100{outline-color:#f5f5f4}.outline-stone-200{outline-color:#e7e5e4}.outline-stone-300{outline-color:#d6d3d1}.outline-stone-400{outline-color:#a8a29e}.outline-stone-50{outline-color:#fafaf9}.outline-stone-500{outline-color:#78716c}.outline-stone-600{outline-color:#57534e}.outline-stone-700{outline-color:#44403c}.outline-stone-800{outline-color:#292524}.outline-stone-900{outline-color:#1c1917}.outline-teal-100{outline-color:#ccfbf1}.outline-teal-200{outline-color:#99f6e4}.outline-teal-300{outline-color:#5eead4}.outline-teal-400{outline-color:#2dd4bf}.outline-teal-50{outline-color:#f0fdfa}.outline-teal-500{outline-color:#14b8a6}.outline-teal-600{outline-color:#0d9488}.outline-teal-700{outline-color:#0f766e}.outline-teal-800{outline-color:#115e59}.outline-teal-900{outline-color:#134e4a}.outline-transparent{outline-color:transparent}.outline-violet-100{outline-color:#ede9fe}.outline-violet-200{outline-color:#ddd6fe}.outline-violet-300{outline-color:#c4b5fd}.outline-violet-400{outline-color:#a78bfa}.outline-violet-50{outline-color:#f5f3ff}.outline-violet-500{outline-color:#8b5cf6}.outline-violet-600{outline-color:#7c3aed}.outline-violet-700{outline-color:#6d28d9}.outline-violet-800{outline-color:#5b21b6}.outline-violet-900{outline-color:#4c1d95}.outline-white{outline-color:#fff}.outline-yellow-100{outline-color:#fef9c3}.outline-yellow-200{outline-color:#fef08a}.outline-yellow-300{outline-color:#fde047}.outline-yellow-400{outline-color:#facc15}.outline-yellow-50{outline-color:#fefce8}.outline-yellow-500{outline-color:#eab308}.outline-yellow-600{outline-color:#ca8a04}.outline-yellow-700{outline-color:#a16207}.outline-yellow-800{outline-color:#854d0e}.outline-yellow-900{outline-color:#713f12}.outline-zinc-100{outline-color:#f4f4f5}.outline-zinc-200{outline-color:#e4e4e7}.outline-zinc-300{outline-color:#d4d4d8}.outline-zinc-400{outline-color:#a1a1aa}.outline-zinc-50{outline-color:#fafafa}.outline-zinc-500{outline-color:#71717a}.outline-zinc-600{outline-color:#52525b}.outline-zinc-700{outline-color:#3f3f46}.outline-zinc-800{outline-color:#27272a}.outline-zinc-900{outline-color:#18181b}.\!ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color)!important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring,.ring-0{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-1,.ring-2{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-inset{--tw-ring-inset:inset}.ring-amber-100{--tw-ring-opacity:1;--tw-ring-color:rgb(254 243 199/var(--tw-ring-opacity))}.ring-amber-200{--tw-ring-opacity:1;--tw-ring-color:rgb(253 230 138/var(--tw-ring-opacity))}.ring-amber-300{--tw-ring-opacity:1;--tw-ring-color:rgb(252 211 77/var(--tw-ring-opacity))}.ring-amber-400{--tw-ring-opacity:1;--tw-ring-color:rgb(251 191 36/var(--tw-ring-opacity))}.ring-amber-50{--tw-ring-opacity:1;--tw-ring-color:rgb(255 251 235/var(--tw-ring-opacity))}.ring-amber-500{--tw-ring-opacity:1;--tw-ring-color:rgb(245 158 11/var(--tw-ring-opacity))}.ring-amber-600{--tw-ring-opacity:1;--tw-ring-color:rgb(217 119 6/var(--tw-ring-opacity))}.ring-amber-700{--tw-ring-opacity:1;--tw-ring-color:rgb(180 83 9/var(--tw-ring-opacity))}.ring-amber-800{--tw-ring-opacity:1;--tw-ring-color:rgb(146 64 14/var(--tw-ring-opacity))}.ring-amber-900{--tw-ring-opacity:1;--tw-ring-color:rgb(120 53 15/var(--tw-ring-opacity))}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(17 17 17/var(--tw-ring-opacity))}.ring-blue-100{--tw-ring-opacity:1;--tw-ring-color:rgb(219 234 254/var(--tw-ring-opacity))}.ring-blue-200{--tw-ring-opacity:1;--tw-ring-color:rgb(191 219 254/var(--tw-ring-opacity))}.ring-blue-300{--tw-ring-opacity:1;--tw-ring-color:rgb(147 197 253/var(--tw-ring-opacity))}.ring-blue-400{--tw-ring-opacity:1;--tw-ring-color:rgb(96 165 250/var(--tw-ring-opacity))}.ring-blue-50{--tw-ring-opacity:1;--tw-ring-color:rgb(239 246 255/var(--tw-ring-opacity))}.ring-blue-500{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity))}.ring-blue-600{--tw-ring-opacity:1;--tw-ring-color:rgb(37 99 235/var(--tw-ring-opacity))}.ring-blue-700{--tw-ring-opacity:1;--tw-ring-color:rgb(29 78 216/var(--tw-ring-opacity))}.ring-blue-800{--tw-ring-opacity:1;--tw-ring-color:rgb(30 64 175/var(--tw-ring-opacity))}.ring-blue-900{--tw-ring-opacity:1;--tw-ring-color:rgb(30 58 138/var(--tw-ring-opacity))}.ring-cyan-100{--tw-ring-opacity:1;--tw-ring-color:rgb(207 250 254/var(--tw-ring-opacity))}.ring-cyan-200{--tw-ring-opacity:1;--tw-ring-color:rgb(165 243 252/var(--tw-ring-opacity))}.ring-cyan-300{--tw-ring-opacity:1;--tw-ring-color:rgb(103 232 249/var(--tw-ring-opacity))}.ring-cyan-400{--tw-ring-opacity:1;--tw-ring-color:rgb(34 211 238/var(--tw-ring-opacity))}.ring-cyan-50{--tw-ring-opacity:1;--tw-ring-color:rgb(236 254 255/var(--tw-ring-opacity))}.ring-cyan-500{--tw-ring-opacity:1;--tw-ring-color:rgb(6 182 212/var(--tw-ring-opacity))}.ring-cyan-600{--tw-ring-opacity:1;--tw-ring-color:rgb(8 145 178/var(--tw-ring-opacity))}.ring-cyan-700{--tw-ring-opacity:1;--tw-ring-color:rgb(14 116 144/var(--tw-ring-opacity))}.ring-cyan-800{--tw-ring-opacity:1;--tw-ring-color:rgb(21 94 117/var(--tw-ring-opacity))}.ring-cyan-900{--tw-ring-opacity:1;--tw-ring-color:rgb(22 78 99/var(--tw-ring-opacity))}.ring-emerald-100{--tw-ring-opacity:1;--tw-ring-color:rgb(209 250 229/var(--tw-ring-opacity))}.ring-emerald-200{--tw-ring-opacity:1;--tw-ring-color:rgb(167 243 208/var(--tw-ring-opacity))}.ring-emerald-300{--tw-ring-opacity:1;--tw-ring-color:rgb(110 231 183/var(--tw-ring-opacity))}.ring-emerald-400{--tw-ring-opacity:1;--tw-ring-color:rgb(52 211 153/var(--tw-ring-opacity))}.ring-emerald-50{--tw-ring-opacity:1;--tw-ring-color:rgb(236 253 245/var(--tw-ring-opacity))}.ring-emerald-500{--tw-ring-opacity:1;--tw-ring-color:rgb(16 185 129/var(--tw-ring-opacity))}.ring-emerald-600{--tw-ring-opacity:1;--tw-ring-color:rgb(5 150 105/var(--tw-ring-opacity))}.ring-emerald-700{--tw-ring-opacity:1;--tw-ring-color:rgb(4 120 87/var(--tw-ring-opacity))}.ring-emerald-800{--tw-ring-opacity:1;--tw-ring-color:rgb(6 95 70/var(--tw-ring-opacity))}.ring-emerald-900{--tw-ring-opacity:1;--tw-ring-color:rgb(6 78 59/var(--tw-ring-opacity))}.ring-fuchsia-100{--tw-ring-opacity:1;--tw-ring-color:rgb(250 232 255/var(--tw-ring-opacity))}.ring-fuchsia-200{--tw-ring-opacity:1;--tw-ring-color:rgb(245 208 254/var(--tw-ring-opacity))}.ring-fuchsia-300{--tw-ring-opacity:1;--tw-ring-color:rgb(240 171 252/var(--tw-ring-opacity))}.ring-fuchsia-400{--tw-ring-opacity:1;--tw-ring-color:rgb(232 121 249/var(--tw-ring-opacity))}.ring-fuchsia-50{--tw-ring-opacity:1;--tw-ring-color:rgb(253 244 255/var(--tw-ring-opacity))}.ring-fuchsia-500{--tw-ring-opacity:1;--tw-ring-color:rgb(217 70 239/var(--tw-ring-opacity))}.ring-fuchsia-600{--tw-ring-opacity:1;--tw-ring-color:rgb(192 38 211/var(--tw-ring-opacity))}.ring-fuchsia-700{--tw-ring-opacity:1;--tw-ring-color:rgb(162 28 175/var(--tw-ring-opacity))}.ring-fuchsia-800{--tw-ring-opacity:1;--tw-ring-color:rgb(134 25 143/var(--tw-ring-opacity))}.ring-fuchsia-900{--tw-ring-opacity:1;--tw-ring-color:rgb(112 26 117/var(--tw-ring-opacity))}.ring-gray-100{--tw-ring-opacity:1;--tw-ring-color:rgb(243 244 246/var(--tw-ring-opacity))}.ring-gray-200{--tw-ring-opacity:1;--tw-ring-color:rgb(229 231 235/var(--tw-ring-opacity))}.ring-gray-300{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity))}.ring-gray-400{--tw-ring-opacity:1;--tw-ring-color:rgb(156 163 175/var(--tw-ring-opacity))}.ring-gray-50{--tw-ring-opacity:1;--tw-ring-color:rgb(249 250 251/var(--tw-ring-opacity))}.ring-gray-500{--tw-ring-opacity:1;--tw-ring-color:rgb(107 114 128/var(--tw-ring-opacity))}.ring-gray-600{--tw-ring-opacity:1;--tw-ring-color:rgb(75 85 99/var(--tw-ring-opacity))}.ring-gray-700{--tw-ring-opacity:1;--tw-ring-color:rgb(55 65 81/var(--tw-ring-opacity))}.ring-gray-800{--tw-ring-opacity:1;--tw-ring-color:rgb(31 41 55/var(--tw-ring-opacity))}.ring-gray-900{--tw-ring-opacity:1;--tw-ring-color:rgb(17 24 39/var(--tw-ring-opacity))}.ring-green-100{--tw-ring-opacity:1;--tw-ring-color:rgb(220 252 231/var(--tw-ring-opacity))}.ring-green-200{--tw-ring-opacity:1;--tw-ring-color:rgb(187 247 208/var(--tw-ring-opacity))}.ring-green-300{--tw-ring-opacity:1;--tw-ring-color:rgb(134 239 172/var(--tw-ring-opacity))}.ring-green-400{--tw-ring-opacity:1;--tw-ring-color:rgb(74 222 128/var(--tw-ring-opacity))}.ring-green-50{--tw-ring-opacity:1;--tw-ring-color:rgb(240 253 244/var(--tw-ring-opacity))}.ring-green-500{--tw-ring-opacity:1;--tw-ring-color:rgb(34 197 94/var(--tw-ring-opacity))}.ring-green-600{--tw-ring-opacity:1;--tw-ring-color:rgb(22 163 74/var(--tw-ring-opacity))}.ring-green-700{--tw-ring-opacity:1;--tw-ring-color:rgb(21 128 61/var(--tw-ring-opacity))}.ring-green-800{--tw-ring-opacity:1;--tw-ring-color:rgb(22 101 52/var(--tw-ring-opacity))}.ring-green-900{--tw-ring-opacity:1;--tw-ring-color:rgb(20 83 45/var(--tw-ring-opacity))}.ring-indigo-100{--tw-ring-opacity:1;--tw-ring-color:rgb(224 231 255/var(--tw-ring-opacity))}.ring-indigo-200{--tw-ring-opacity:1;--tw-ring-color:rgb(199 210 254/var(--tw-ring-opacity))}.ring-indigo-300{--tw-ring-opacity:1;--tw-ring-color:rgb(165 180 252/var(--tw-ring-opacity))}.ring-indigo-400{--tw-ring-opacity:1;--tw-ring-color:rgb(129 140 248/var(--tw-ring-opacity))}.ring-indigo-50{--tw-ring-opacity:1;--tw-ring-color:rgb(238 242 255/var(--tw-ring-opacity))}.ring-indigo-500{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.ring-indigo-600{--tw-ring-opacity:1;--tw-ring-color:rgb(79 70 229/var(--tw-ring-opacity))}.ring-indigo-700{--tw-ring-opacity:1;--tw-ring-color:rgb(67 56 202/var(--tw-ring-opacity))}.ring-indigo-800{--tw-ring-opacity:1;--tw-ring-color:rgb(55 48 163/var(--tw-ring-opacity))}.ring-indigo-900{--tw-ring-opacity:1;--tw-ring-color:rgb(49 46 129/var(--tw-ring-opacity))}.ring-inverted{--tw-ring-color:var(--cal-bg-inverted)}.ring-lime-100{--tw-ring-opacity:1;--tw-ring-color:rgb(236 252 203/var(--tw-ring-opacity))}.ring-lime-200{--tw-ring-opacity:1;--tw-ring-color:rgb(217 249 157/var(--tw-ring-opacity))}.ring-lime-300{--tw-ring-opacity:1;--tw-ring-color:rgb(190 242 100/var(--tw-ring-opacity))}.ring-lime-400{--tw-ring-opacity:1;--tw-ring-color:rgb(163 230 53/var(--tw-ring-opacity))}.ring-lime-50{--tw-ring-opacity:1;--tw-ring-color:rgb(247 254 231/var(--tw-ring-opacity))}.ring-lime-500{--tw-ring-opacity:1;--tw-ring-color:rgb(132 204 22/var(--tw-ring-opacity))}.ring-lime-600{--tw-ring-opacity:1;--tw-ring-color:rgb(101 163 13/var(--tw-ring-opacity))}.ring-lime-700{--tw-ring-opacity:1;--tw-ring-color:rgb(77 124 15/var(--tw-ring-opacity))}.ring-lime-800{--tw-ring-opacity:1;--tw-ring-color:rgb(63 98 18/var(--tw-ring-opacity))}.ring-lime-900{--tw-ring-opacity:1;--tw-ring-color:rgb(54 83 20/var(--tw-ring-opacity))}.ring-muted{--tw-ring-color:var(--cal-bg-muted)}.ring-neutral-100{--tw-ring-opacity:1;--tw-ring-color:rgb(245 245 245/var(--tw-ring-opacity))}.ring-neutral-200{--tw-ring-opacity:1;--tw-ring-color:rgb(229 229 229/var(--tw-ring-opacity))}.ring-neutral-300{--tw-ring-opacity:1;--tw-ring-color:rgb(212 212 212/var(--tw-ring-opacity))}.ring-neutral-400{--tw-ring-opacity:1;--tw-ring-color:rgb(163 163 163/var(--tw-ring-opacity))}.ring-neutral-50{--tw-ring-opacity:1;--tw-ring-color:rgb(250 250 250/var(--tw-ring-opacity))}.ring-neutral-500{--tw-ring-opacity:1;--tw-ring-color:rgb(115 115 115/var(--tw-ring-opacity))}.ring-neutral-600{--tw-ring-opacity:1;--tw-ring-color:rgb(82 82 82/var(--tw-ring-opacity))}.ring-neutral-700{--tw-ring-opacity:1;--tw-ring-color:rgb(64 64 64/var(--tw-ring-opacity))}.ring-neutral-800{--tw-ring-opacity:1;--tw-ring-color:rgb(38 38 38/var(--tw-ring-opacity))}.ring-neutral-900{--tw-ring-opacity:1;--tw-ring-color:rgb(23 23 23/var(--tw-ring-opacity))}.ring-orange-100{--tw-ring-opacity:1;--tw-ring-color:rgb(255 237 213/var(--tw-ring-opacity))}.ring-orange-200{--tw-ring-opacity:1;--tw-ring-color:rgb(254 215 170/var(--tw-ring-opacity))}.ring-orange-300{--tw-ring-opacity:1;--tw-ring-color:rgb(253 186 116/var(--tw-ring-opacity))}.ring-orange-400{--tw-ring-opacity:1;--tw-ring-color:rgb(251 146 60/var(--tw-ring-opacity))}.ring-orange-50{--tw-ring-opacity:1;--tw-ring-color:rgb(255 247 237/var(--tw-ring-opacity))}.ring-orange-500{--tw-ring-opacity:1;--tw-ring-color:rgb(249 115 22/var(--tw-ring-opacity))}.ring-orange-600{--tw-ring-opacity:1;--tw-ring-color:rgb(234 88 12/var(--tw-ring-opacity))}.ring-orange-700{--tw-ring-opacity:1;--tw-ring-color:rgb(194 65 12/var(--tw-ring-opacity))}.ring-orange-800{--tw-ring-opacity:1;--tw-ring-color:rgb(154 52 18/var(--tw-ring-opacity))}.ring-orange-900{--tw-ring-opacity:1;--tw-ring-color:rgb(124 45 18/var(--tw-ring-opacity))}.ring-pink-100{--tw-ring-opacity:1;--tw-ring-color:rgb(252 231 243/var(--tw-ring-opacity))}.ring-pink-200{--tw-ring-opacity:1;--tw-ring-color:rgb(251 207 232/var(--tw-ring-opacity))}.ring-pink-300{--tw-ring-opacity:1;--tw-ring-color:rgb(249 168 212/var(--tw-ring-opacity))}.ring-pink-400{--tw-ring-opacity:1;--tw-ring-color:rgb(244 114 182/var(--tw-ring-opacity))}.ring-pink-50{--tw-ring-opacity:1;--tw-ring-color:rgb(253 242 248/var(--tw-ring-opacity))}.ring-pink-500{--tw-ring-opacity:1;--tw-ring-color:rgb(236 72 153/var(--tw-ring-opacity))}.ring-pink-600{--tw-ring-opacity:1;--tw-ring-color:rgb(219 39 119/var(--tw-ring-opacity))}.ring-pink-700{--tw-ring-opacity:1;--tw-ring-color:rgb(190 24 93/var(--tw-ring-opacity))}.ring-pink-900{--tw-ring-opacity:1;--tw-ring-color:rgb(131 24 67/var(--tw-ring-opacity))}.ring-purple-100{--tw-ring-opacity:1;--tw-ring-color:rgb(243 232 255/var(--tw-ring-opacity))}.ring-purple-200{--tw-ring-opacity:1;--tw-ring-color:rgb(233 213 255/var(--tw-ring-opacity))}.ring-purple-300{--tw-ring-opacity:1;--tw-ring-color:rgb(216 180 254/var(--tw-ring-opacity))}.ring-purple-400{--tw-ring-opacity:1;--tw-ring-color:rgb(192 132 252/var(--tw-ring-opacity))}.ring-purple-50{--tw-ring-opacity:1;--tw-ring-color:rgb(250 245 255/var(--tw-ring-opacity))}.ring-purple-500{--tw-ring-opacity:1;--tw-ring-color:rgb(168 85 247/var(--tw-ring-opacity))}.ring-purple-600{--tw-ring-opacity:1;--tw-ring-color:rgb(147 51 234/var(--tw-ring-opacity))}.ring-purple-700{--tw-ring-opacity:1;--tw-ring-color:rgb(126 34 206/var(--tw-ring-opacity))}.ring-purple-800{--tw-ring-opacity:1;--tw-ring-color:rgb(107 33 168/var(--tw-ring-opacity))}.ring-purple-900{--tw-ring-opacity:1;--tw-ring-color:rgb(88 28 135/var(--tw-ring-opacity))}.ring-red-100{--tw-ring-opacity:1;--tw-ring-color:rgb(254 226 226/var(--tw-ring-opacity))}.ring-red-200{--tw-ring-opacity:1;--tw-ring-color:rgb(254 202 202/var(--tw-ring-opacity))}.ring-red-300{--tw-ring-opacity:1;--tw-ring-color:rgb(252 165 165/var(--tw-ring-opacity))}.ring-red-400{--tw-ring-opacity:1;--tw-ring-color:rgb(248 113 113/var(--tw-ring-opacity))}.ring-red-50{--tw-ring-opacity:1;--tw-ring-color:rgb(254 242 242/var(--tw-ring-opacity))}.ring-red-500{--tw-ring-opacity:1;--tw-ring-color:rgb(239 68 68/var(--tw-ring-opacity))}.ring-red-600{--tw-ring-opacity:1;--tw-ring-color:rgb(220 38 38/var(--tw-ring-opacity))}.ring-red-700{--tw-ring-opacity:1;--tw-ring-color:rgb(185 28 28/var(--tw-ring-opacity))}.ring-red-800{--tw-ring-opacity:1;--tw-ring-color:rgb(153 27 27/var(--tw-ring-opacity))}.ring-red-900{--tw-ring-opacity:1;--tw-ring-color:rgb(127 29 29/var(--tw-ring-opacity))}.ring-rose-100{--tw-ring-opacity:1;--tw-ring-color:rgb(255 228 230/var(--tw-ring-opacity))}.ring-rose-200{--tw-ring-opacity:1;--tw-ring-color:rgb(254 205 211/var(--tw-ring-opacity))}.ring-rose-300{--tw-ring-opacity:1;--tw-ring-color:rgb(253 164 175/var(--tw-ring-opacity))}.ring-rose-400{--tw-ring-opacity:1;--tw-ring-color:rgb(251 113 133/var(--tw-ring-opacity))}.ring-rose-50{--tw-ring-opacity:1;--tw-ring-color:rgb(255 241 242/var(--tw-ring-opacity))}.ring-rose-500{--tw-ring-opacity:1;--tw-ring-color:rgb(244 63 94/var(--tw-ring-opacity))}.ring-rose-600{--tw-ring-opacity:1;--tw-ring-color:rgb(225 29 72/var(--tw-ring-opacity))}.ring-rose-700{--tw-ring-opacity:1;--tw-ring-color:rgb(190 18 60/var(--tw-ring-opacity))}.ring-rose-800{--tw-ring-opacity:1;--tw-ring-color:rgb(159 18 57/var(--tw-ring-opacity))}.ring-rose-900{--tw-ring-opacity:1;--tw-ring-color:rgb(136 19 55/var(--tw-ring-opacity))}.ring-sky-100{--tw-ring-opacity:1;--tw-ring-color:rgb(224 242 254/var(--tw-ring-opacity))}.ring-sky-200{--tw-ring-opacity:1;--tw-ring-color:rgb(186 230 253/var(--tw-ring-opacity))}.ring-sky-300{--tw-ring-opacity:1;--tw-ring-color:rgb(125 211 252/var(--tw-ring-opacity))}.ring-sky-400{--tw-ring-opacity:1;--tw-ring-color:rgb(56 189 248/var(--tw-ring-opacity))}.ring-sky-50{--tw-ring-opacity:1;--tw-ring-color:rgb(240 249 255/var(--tw-ring-opacity))}.ring-sky-500{--tw-ring-opacity:1;--tw-ring-color:rgb(14 165 233/var(--tw-ring-opacity))}.ring-sky-600{--tw-ring-opacity:1;--tw-ring-color:rgb(2 132 199/var(--tw-ring-opacity))}.ring-sky-700{--tw-ring-opacity:1;--tw-ring-color:rgb(3 105 161/var(--tw-ring-opacity))}.ring-sky-800{--tw-ring-opacity:1;--tw-ring-color:rgb(7 89 133/var(--tw-ring-opacity))}.ring-sky-900{--tw-ring-opacity:1;--tw-ring-color:rgb(12 74 110/var(--tw-ring-opacity))}.ring-slate-100{--tw-ring-opacity:1;--tw-ring-color:rgb(241 245 249/var(--tw-ring-opacity))}.ring-slate-200{--tw-ring-opacity:1;--tw-ring-color:rgb(226 232 240/var(--tw-ring-opacity))}.ring-slate-300{--tw-ring-opacity:1;--tw-ring-color:rgb(203 213 225/var(--tw-ring-opacity))}.ring-slate-400{--tw-ring-opacity:1;--tw-ring-color:rgb(148 163 184/var(--tw-ring-opacity))}.ring-slate-50{--tw-ring-opacity:1;--tw-ring-color:rgb(248 250 252/var(--tw-ring-opacity))}.ring-slate-500{--tw-ring-opacity:1;--tw-ring-color:rgb(100 116 139/var(--tw-ring-opacity))}.ring-slate-600{--tw-ring-opacity:1;--tw-ring-color:rgb(71 85 105/var(--tw-ring-opacity))}.ring-slate-700{--tw-ring-opacity:1;--tw-ring-color:rgb(51 65 85/var(--tw-ring-opacity))}.ring-slate-800{--tw-ring-opacity:1;--tw-ring-color:rgb(30 41 59/var(--tw-ring-opacity))}.ring-slate-900{--tw-ring-opacity:1;--tw-ring-color:rgb(15 23 42/var(--tw-ring-opacity))}.ring-stone-100{--tw-ring-opacity:1;--tw-ring-color:rgb(245 245 244/var(--tw-ring-opacity))}.ring-stone-200{--tw-ring-opacity:1;--tw-ring-color:rgb(231 229 228/var(--tw-ring-opacity))}.ring-stone-300{--tw-ring-opacity:1;--tw-ring-color:rgb(214 211 209/var(--tw-ring-opacity))}.ring-stone-400{--tw-ring-opacity:1;--tw-ring-color:rgb(168 162 158/var(--tw-ring-opacity))}.ring-stone-50{--tw-ring-opacity:1;--tw-ring-color:rgb(250 250 249/var(--tw-ring-opacity))}.ring-stone-500{--tw-ring-opacity:1;--tw-ring-color:rgb(120 113 108/var(--tw-ring-opacity))}.ring-stone-600{--tw-ring-opacity:1;--tw-ring-color:rgb(87 83 78/var(--tw-ring-opacity))}.ring-stone-700{--tw-ring-opacity:1;--tw-ring-color:rgb(68 64 60/var(--tw-ring-opacity))}.ring-stone-800{--tw-ring-opacity:1;--tw-ring-color:rgb(41 37 36/var(--tw-ring-opacity))}.ring-stone-900{--tw-ring-opacity:1;--tw-ring-color:rgb(28 25 23/var(--tw-ring-opacity))}.ring-subtle{--tw-ring-color:var(--cal-bg-subtle)}.ring-teal-100{--tw-ring-opacity:1;--tw-ring-color:rgb(204 251 241/var(--tw-ring-opacity))}.ring-teal-200{--tw-ring-opacity:1;--tw-ring-color:rgb(153 246 228/var(--tw-ring-opacity))}.ring-teal-300{--tw-ring-opacity:1;--tw-ring-color:rgb(94 234 212/var(--tw-ring-opacity))}.ring-teal-400{--tw-ring-opacity:1;--tw-ring-color:rgb(45 212 191/var(--tw-ring-opacity))}.ring-teal-50{--tw-ring-opacity:1;--tw-ring-color:rgb(240 253 250/var(--tw-ring-opacity))}.ring-teal-500{--tw-ring-opacity:1;--tw-ring-color:rgb(20 184 166/var(--tw-ring-opacity))}.ring-teal-600{--tw-ring-opacity:1;--tw-ring-color:rgb(13 148 136/var(--tw-ring-opacity))}.ring-teal-700{--tw-ring-opacity:1;--tw-ring-color:rgb(15 118 110/var(--tw-ring-opacity))}.ring-teal-800{--tw-ring-opacity:1;--tw-ring-color:rgb(17 94 89/var(--tw-ring-opacity))}.ring-teal-900{--tw-ring-opacity:1;--tw-ring-color:rgb(19 78 74/var(--tw-ring-opacity))}.ring-transparent{--tw-ring-color:transparent}.ring-violet-100{--tw-ring-opacity:1;--tw-ring-color:rgb(237 233 254/var(--tw-ring-opacity))}.ring-violet-200{--tw-ring-opacity:1;--tw-ring-color:rgb(221 214 254/var(--tw-ring-opacity))}.ring-violet-300{--tw-ring-opacity:1;--tw-ring-color:rgb(196 181 253/var(--tw-ring-opacity))}.ring-violet-400{--tw-ring-opacity:1;--tw-ring-color:rgb(167 139 250/var(--tw-ring-opacity))}.ring-violet-50{--tw-ring-opacity:1;--tw-ring-color:rgb(245 243 255/var(--tw-ring-opacity))}.ring-violet-500{--tw-ring-opacity:1;--tw-ring-color:rgb(139 92 246/var(--tw-ring-opacity))}.ring-violet-600{--tw-ring-opacity:1;--tw-ring-color:rgb(124 58 237/var(--tw-ring-opacity))}.ring-violet-700{--tw-ring-opacity:1;--tw-ring-color:rgb(109 40 217/var(--tw-ring-opacity))}.ring-violet-800{--tw-ring-opacity:1;--tw-ring-color:rgb(91 33 182/var(--tw-ring-opacity))}.ring-violet-900{--tw-ring-opacity:1;--tw-ring-color:rgb(76 29 149/var(--tw-ring-opacity))}.ring-white{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}.ring-yellow-100{--tw-ring-opacity:1;--tw-ring-color:rgb(254 249 195/var(--tw-ring-opacity))}.ring-yellow-200{--tw-ring-opacity:1;--tw-ring-color:rgb(254 240 138/var(--tw-ring-opacity))}.ring-yellow-300{--tw-ring-opacity:1;--tw-ring-color:rgb(253 224 71/var(--tw-ring-opacity))}.ring-yellow-400{--tw-ring-opacity:1;--tw-ring-color:rgb(250 204 21/var(--tw-ring-opacity))}.ring-yellow-50{--tw-ring-opacity:1;--tw-ring-color:rgb(254 252 232/var(--tw-ring-opacity))}.ring-yellow-500{--tw-ring-opacity:1;--tw-ring-color:rgb(234 179 8/var(--tw-ring-opacity))}.ring-yellow-600{--tw-ring-opacity:1;--tw-ring-color:rgb(202 138 4/var(--tw-ring-opacity))}.ring-yellow-700{--tw-ring-opacity:1;--tw-ring-color:rgb(161 98 7/var(--tw-ring-opacity))}.ring-yellow-800{--tw-ring-opacity:1;--tw-ring-color:rgb(133 77 14/var(--tw-ring-opacity))}.ring-yellow-900{--tw-ring-opacity:1;--tw-ring-color:rgb(113 63 18/var(--tw-ring-opacity))}.ring-zinc-100{--tw-ring-opacity:1;--tw-ring-color:rgb(244 244 245/var(--tw-ring-opacity))}.ring-zinc-200{--tw-ring-opacity:1;--tw-ring-color:rgb(228 228 231/var(--tw-ring-opacity))}.ring-zinc-300{--tw-ring-opacity:1;--tw-ring-color:rgb(212 212 216/var(--tw-ring-opacity))}.ring-zinc-400{--tw-ring-opacity:1;--tw-ring-color:rgb(161 161 170/var(--tw-ring-opacity))}.ring-zinc-50{--tw-ring-opacity:1;--tw-ring-color:rgb(250 250 250/var(--tw-ring-opacity))}.ring-zinc-500{--tw-ring-opacity:1;--tw-ring-color:rgb(113 113 122/var(--tw-ring-opacity))}.ring-zinc-600{--tw-ring-opacity:1;--tw-ring-color:rgb(82 82 91/var(--tw-ring-opacity))}.ring-zinc-700{--tw-ring-opacity:1;--tw-ring-color:rgb(63 63 70/var(--tw-ring-opacity))}.ring-zinc-800{--tw-ring-opacity:1;--tw-ring-color:rgb(39 39 42/var(--tw-ring-opacity))}.ring-zinc-900{--tw-ring-opacity:1;--tw-ring-color:rgb(24 24 27/var(--tw-ring-opacity))}.ring-offset-1{--tw-ring-offset-width:1px}.ring-offset-2{--tw-ring-offset-width:2px}.blur{--tw-blur:blur(8px)}.blur,.blur-\[3px\]{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-\[3px\]{--tw-blur:blur(3px)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))}.drop-shadow,.grayscale{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.grayscale{--tw-grayscale:grayscale(100%)}.grayscale-0{--tw-grayscale:grayscale(0)}.grayscale-0,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.sepia{--tw-sepia:sepia(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur:blur(8px)}.backdrop-blur,.backdrop-blur-\[1px\]{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-\[1px\]{--tw-backdrop-blur:blur(1px)}.backdrop-blur-lg{--tw-backdrop-blur:blur(16px)}.backdrop-blur-lg,.backdrop-blur-md{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-grayscale{--tw-backdrop-grayscale:grayscale(100%)}.backdrop-grayscale,.backdrop-invert{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-invert{--tw-backdrop-invert:invert(100%)}.backdrop-sepia{--tw-backdrop-sepia:sepia(100%)}.backdrop-filter,.backdrop-sepia{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-none{transition-property:none}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.delay-1000{transition-delay:1s}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.will-change-transform{will-change:transform}.scrollbar{--scrollbar-track:initial;--scrollbar-thumb:initial;--scrollbar-corner:initial;--scrollbar-track-hover:var(--scrollbar-track);--scrollbar-thumb-hover:var(--scrollbar-thumb);--scrollbar-corner-hover:var(--scrollbar-corner);--scrollbar-track-active:var(--scrollbar-track-hover);--scrollbar-thumb-active:var(--scrollbar-thumb-hover);--scrollbar-corner-active:var(--scrollbar-corner-hover);scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track);overflow:overlay}.scrollbar.overflow-x-hidden{overflow-x:hidden}.scrollbar.overflow-y-hidden{overflow-y:hidden}.scrollbar::-webkit-scrollbar-track{background-color:var(--scrollbar-track)}.scrollbar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb)}.scrollbar::-webkit-scrollbar-corner{background-color:var(--scrollbar-corner)}.scrollbar::-webkit-scrollbar-track:hover{background-color:var(--scrollbar-track-hover)}.scrollbar::-webkit-scrollbar-thumb:hover{background-color:var(--scrollbar-thumb-hover)}.scrollbar::-webkit-scrollbar-corner:hover{background-color:var(--scrollbar-corner-hover)}.scrollbar::-webkit-scrollbar-track:active{background-color:var(--scrollbar-track-active)}.scrollbar::-webkit-scrollbar-thumb:active{background-color:var(--scrollbar-thumb-active)}.scrollbar::-webkit-scrollbar-corner:active{background-color:var(--scrollbar-corner-active)}.scrollbar{scrollbar-width:auto}.scrollbar::-webkit-scrollbar{width:16px;height:16px}.scrollbar-thin.overflow-x-hidden{overflow-x:hidden}.scrollbar-thin.overflow-y-hidden{overflow-y:hidden}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0) scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1)) rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0) scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1)) rotate(var(--tw-exit-rotate,0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity:initial;--tw-enter-scale:initial;--tw-enter-rotate:initial;--tw-enter-translate-x:initial;--tw-enter-translate-y:initial}.zoom-in{--tw-enter-scale:0}.zoom-out{--tw-exit-scale:0}.slide-in-from-bottom{--tw-enter-translate-y:100%}.slide-in-from-left{--tw-enter-translate-x:-100%}.slide-in-from-right{--tw-enter-translate-x:100%}.slide-in-from-top{--tw-enter-translate-y:-100%}.duration-100{animation-duration:.1s}.duration-150{animation-duration:.15s}.duration-200{animation-duration:.2s}.duration-300{animation-duration:.3s}.delay-1000{animation-delay:1s}.ease-in{animation-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{animation-timing-function:cubic-bezier(0,0,.2,1)}.running{animation-play-state:running}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.\[--booker-main-width\:420px\]{--booker-main-width:420px}.\[--booker-main-width\:480px\]{--booker-main-width:480px}.\[--booker-meta-width\:240px\]{--booker-meta-width:240px}.\[--booker-meta-width\:340px\]{--booker-meta-width:340px}.\[--booker-timeslots-width\:240px\]{--booker-timeslots-width:240px}.\[--cal-brand-accent\:\#fafafa\]{--cal-brand-accent:#fafafa}.\[--cal-brand-emphasis\:\#101010\]{--cal-brand-emphasis:#101010}.\[--cal-brand-subtle\:\#9CA3AF\]{--cal-brand-subtle:#9ca3af}.\[--cal-brand-text\:\#FFFFFF\],.\[--cal-brand-text\:white\]{--cal-brand-text:#fff}.\[--cal-brand\:\#111827\]{--cal-brand:#111827}.\[--calendar-dates-sticky-offset\:66px\]{--calendar-dates-sticky-offset:66px}.\[--disabled-gradient-background\:\#E5E7EB\]{--disabled-gradient-background:#e5e7eb}.\[--disabled-gradient-background\:\#F8F9FB\]{--disabled-gradient-background:#f8f9fb}.\[--disabled-gradient-foreground\:\#D1D5DB\]{--disabled-gradient-foreground:#d1d5db}.\[--disabled-gradient-foreground\:\#E6E7EB\]{--disabled-gradient-foreground:#e6e7eb}.\[--troublehooster-meta-width\:0px\]{--troublehooster-meta-width:0px}.\[--troublehooster-meta-width\:250px\]{--troublehooster-meta-width:250px}.\[-webkit-app-region\:no-drag\]{-webkit-app-region:no-drag}.\[appearance\:textfield\]{-webkit-appearance:textfield;-moz-appearance:textfield;appearance:textfield}.\[filter\:invert\(0\.5\)_brightness\(0\.5\)\]{filter:invert(.5) brightness(.5)}.\[grid-area\:calendar\]{grid-area:calendar}.\[grid-area\:header\]{grid-area:header}.\[grid-area\:main\]{grid-area:main}.\[grid-area\:meta\]{grid-area:meta}.\[grid-area\:timeslots\]{grid-area:timeslots}:root{--cal-bg-emphasis:#e5e7eb;--cal-bg:#fff;--cal-bg-subtle:#f3f4f6;--cal-bg-muted:#f9fafb;--cal-bg-inverted:#0f0f0f;--cal-bg-info:#f6f9fe;--cal-bg-success:#e4fbe9;--cal-bg-attention:#fcefd9;--cal-bg-error:hsla(3,66,93,1);--cal-bg-dark-error:#772522;--cal-border-emphasis:#9ca3b0;--cal-border:#d1d5db;--cal-border-subtle:#e5e7eb;--cal-border-booker:#e5e7eb;--cal-border-muted:#f3f4f6;--cal-border-error:#aa2f27;--cal-text-emphasis:#384252;--cal-text:#384252;--cal-text-subtle:#6b7280;--cal-text-muted:#9ca3b0;--cal-text-inverted:#fff;--cal-text-info:#253883;--cal-text-success:#285231;--cal-text-attention:#74331b;--cal-text-error:#772522;--cal-brand:#111827;--cal-brand-emphasis:#0f0f0f;--cal-brand-text:#fff}.dark{--cal-bg-emphasis:#404040;--cal-bg:#1a1a1a;--cal-bg-subtle:#2e2e2e;--cal-bg-muted:#1f1f1f;--cal-bg-inverted:#f3f4f6;--cal-bg-info:#253883;--cal-bg-success:#285231;--cal-bg-attention:#74331b;--cal-bg-error:#772522;--cal-bg-dark-error:#772522;--cal-border-emphasis:#757575;--cal-border:#575757;--cal-border-subtle:#383838;--cal-border-booker:#383838;--cal-border-muted:#2e2e2e;--cal-border-error:#aa2f27;--cal-text-emphasis:#fcfcfd;--cal-text:#d6d6d6;--cal-text-subtle:#a6a6a6;--cal-text-muted:#575757;--cal-text-inverted:#1a1a1a;--cal-text-info:#dee9fc;--cal-text-success:#e4fbe9;--cal-text-attention:#fcefd9;--cal-text-error:#f9e3e1;--cal-brand:#fff;--cal-brand-emphasis:#9ca3b0;--cal-brand-text:#000}::-moz-selection{color:var(--cal-brand-text);background:var(--cal-brand)}::selection{color:var(--cal-brand-text);background:var(--cal-brand)}body{text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}html.todesktop a,html.todesktop button,html.todesktop header,html.todesktop img{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:default!important}html.todesktop,html.todesktop div{cursor:default!important}html.todesktop header{-webkit-app-region:drag}html.todesktop header a,html.todesktop header button{-webkit-app-region:no-drag}html.todesktop-platform-darwin.dark main.bg-default{background:rgba(0,0,0,.6)!important}html.todesktop-platform-darwin.light main.bg-default{background:hsla(0,0%,100%,.8)!important}html.todesktop.light{--cal-bg-emphasis:rgba(28,28,28,.1)}html.todesktop.dark{--cal-bg-emphasis:rgba(65,66,68,.3)}[multiple]:focus,[type=checkbox]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=radio]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus{--tw-ring-color:var(--brand-color);border-color:var(--brand-color)}@media only screen and (max-width:768px){#launcher,div[role=presentation]>iframe{display:none!important}.BeaconFabButtonFrame{margin-left:-30px;left:50%;bottom:28px!important;z-index:1058!important}}.DateRangePickerInput__withBorder{border:0!important}.DateInput_input{border:1px solid #d1d5db!important;border-radius:2px!important;font-size:inherit!important;font-weight:inherit!important;color:#000;padding:11px â11px 9px!important;line-height:16px!important}.DateInput_input__focused{border:2px solid #000!important;border-radius:2px!important;box-shadow:none!important;padding:10px â10px 9px!important}.DateRangePickerInput_arrow{padding:0 10px}.loader{display:block;width:30px;height:30px;margin:60px auto;position:relative;border-width:4px;border-style:solid;animation:loader 2s ease infinite}.loader-inner{vertical-align:top;display:inline-block;width:100%;animation:loader-inner 2s ease-in infinite}.no-ring-inset{--tw-ring-inset:unset}@keyframes loader{0%{transform:rotate(0deg)}25%{transform:rotate(180deg)}50%{transform:rotate(180deg)}75%{transform:rotate(1turn)}to{transform:rotate(1turn)}}@keyframes loader-inner{0%{height:0%}25%{height:0%}50%{height:100%}75%{height:100%}to{height:0%}}.text-inverted-important{color:#fff!important}#timeZone input:focus{box-shadow:none}.react-date-picker__wrapper{border:none!important}.react-date-picker__inputGroup__input{padding-top:0;padding-bottom:0}.slideInBottom{animation-duration:.3s;animation-fill-mode:both;animation-name:slideInBottom}@keyframes slideInBottom{0%{opacity:0;transform:translateY(30%);pointer-events:none}to{opacity:1;pointer-events:auto}}.slideInTop{animation-duration:.3s;animation-fill-mode:both;animation-name:slideInTop}@keyframes slideInTop{0%{opacity:0;transform:translateY(-20%);pointer-events:none}to{opacity:1;pointer-events:auto;transform:translateY(0)}}.fadeIn{animation-duration:.3s;animation-fill-mode:both;animation-name:fadeIn;animation-timing-function:ease-in-out}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@supports(-webkit-touch-callout:none){.h-screen{height:-webkit-fill-available}}::-webkit-search-cancel-button{-webkit-appearance:none}.react-tel-input .country-list .country.highlight,.react-tel-input .country-list .country:hover{background-color:var(--cal-bg-emphasis)!important}.react-tel-input .flag-dropdown .selected-flag,.react-tel-input .flag-dropdown.open .selected-flag{background-color:var(--cal-bg,#fff)!important}.react-tel-input .flag-dropdown{left:.125rem;border-top-width:0!important;border-bottom-width:0!important;border-left-width:0!important;border-right-color:var(--cal-border,#d1d5db)!important}.first-letter\:text-xs:first-letter{font-size:.75rem;line-height:1rem}.selection\:bg-subtle ::-moz-selection{background-color:var(--cal-bg-subtle)}.selection\:bg-subtle ::selection{background-color:var(--cal-bg-subtle)}.selection\:bg-subtle::-moz-selection{background-color:var(--cal-bg-subtle)}.selection\:bg-subtle::selection{background-color:var(--cal-bg-subtle)}.placeholder\:text-sm::-moz-placeholder{font-size:.875rem;line-height:1.25rem}.placeholder\:text-sm::placeholder{font-size:.875rem;line-height:1.25rem}.placeholder\:font-normal::-moz-placeholder{font-weight:400}.placeholder\:font-normal::placeholder{font-weight:400}.placeholder\:text-gray-500::-moz-placeholder{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.placeholder\:text-gray-500::placeholder{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.placeholder\:text-muted::-moz-placeholder{color:var(--cal-text-muted,#9ca3af)}.placeholder\:text-muted::placeholder{color:var(--cal-text-muted,#9ca3af)}.placeholder\:text-subtle::-moz-placeholder{color:var(--cal-text-subtle,#6b7280)}.placeholder\:text-subtle::placeholder{color:var(--cal-text-subtle,#6b7280)}.before\:pointer-events-none:before{content:var(--tw-content);pointer-events:none}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:inset-0:before{content:var(--tw-content);inset:0}.before\:bottom-\[-3px\]:before{content:var(--tw-content);bottom:-3px}.before\:bottom-\[-5px\]:before{content:var(--tw-content);bottom:-5px}.before\:left-\[-30px\]:before{content:var(--tw-content);left:-30px}.before\:top-\[-3px\]:before{content:var(--tw-content);top:-3px}.before\:top-\[-5px\]:before{content:var(--tw-content);top:-5px}.before\:w-\[calc\(100\%_\+_35px\)\]:before{content:var(--tw-content);width:calc(100% + 35px)}.before\:rounded-md:before{content:var(--tw-content);border-radius:.375rem}.before\:border-0:before{content:var(--tw-content);border-width:0}.before\:bg-default:before{content:var(--tw-content);background-color:var(--cal-bg,#fff)}.before\:bg-subtle:before{content:var(--tw-content);background-color:var(--cal-bg-subtle)}.before\:py-3:before{content:var(--tw-content);padding-top:.75rem;padding-bottom:.75rem}.before\:opacity-0:before{content:var(--tw-content);opacity:0}.before\:transition-opacity:before{content:var(--tw-content);transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:block:after{content:var(--tw-content);display:block}.after\:h-2:after{content:var(--tw-content);height:.5rem}.after\:h-\[6px\]:after{content:var(--tw-content);height:6px}.after\:w-2:after{content:var(--tw-content);width:.5rem}.after\:w-\[6px\]:after{content:var(--tw-content);width:6px}.after\:rounded-full:after{content:var(--tw-content);border-radius:9999px}.after\:bg-default:after{content:var(--tw-content);background-color:var(--cal-bg,#fff)}.after\:bg-inverted:after{content:var(--tw-content);background-color:var(--cal-bg-inverted)}.after\:bg-muted:after{content:var(--tw-content);background-color:var(--cal-bg-muted)}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.first\:mt-0:first-child{margin-top:0}.first\:rounded-t-md:first-child{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.last\:mb-0:last-child{margin-bottom:0}.last\:mb-2:last-child{margin-bottom:.5rem}.last\:mr-0:last-child{margin-right:0}.last\:rounded-b-md:last-child{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.last\:border-b-0:last-child{border-bottom-width:0}.last\:pb-0:last-child{padding-bottom:0}.last\:font-medium:last-child{font-weight:500}.only\:w-full:only-child{width:100%}.first-of-type\:rounded-t-\[inherit\]:first-of-type{border-top-left-radius:inherit;border-top-right-radius:inherit}.last-of-type\:rounded-b-\[inherit\]:last-of-type{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.visited\:text-blue-500:visited{color:#3b82f6}.checked\:bg-brand-default:checked{background-color:var(--cal-brand,#111827)}.checked\:bg-darkerror:checked{background-color:var(--cal-bg-dark-error)}.checked\:bg-gray-300:checked{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.checked\:bg-gray-800:checked{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.empty\:before\:inline-block:empty:before{content:var(--tw-content);display:inline-block}.empty\:before\:content-\[\'\'\]:empty:before{--tw-content:"";content:var(--tw-content)}.focus-within\:bg-subtle:focus-within{background-color:var(--cal-bg-subtle)}.focus-within\:outline-none:focus-within{outline:2px solid transparent;outline-offset:2px}.focus-within\:ring-0:focus-within{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-within\:ring-2:focus-within{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-within\:ring-brand-default:focus-within{--tw-ring-color:var(--cal-brand,#111827)}.focus-within\:ring-emphasis:focus-within{--tw-ring-color:var(--cal-bg-emphasis)}.focus-within\:ring-offset-2:focus-within{--tw-ring-offset-width:2px}.focus\:z-10:focus{z-index:10}.focus\:border:focus{border-width:1px}.focus\:border-0:focus{border-width:0}.focus\:border-2:focus{border-width:2px}.focus\:border-r:focus{border-right-width:1px}.focus\:border-black:focus{--tw-border-opacity:1;border-color:rgb(17 17 17/var(--tw-border-opacity))}.focus\:border-brand-default:focus{border-color:var(--cal-brand,#111827)}.focus\:border-emphasis:focus{border-color:var(--cal-border-emphasis,#9ca3af)}.focus\:border-gray-800:focus{--tw-border-opacity:1;border-color:rgb(31 41 55/var(--tw-border-opacity))}.focus\:border-neutral-500:focus{--tw-border-opacity:1;border-color:rgb(115 115 115/var(--tw-border-opacity))}.focus\:border-neutral-800:focus{--tw-border-opacity:1;border-color:rgb(38 38 38/var(--tw-border-opacity))}.focus\:border-neutral-900:focus{--tw-border-opacity:1;border-color:rgb(23 23 23/var(--tw-border-opacity))}.focus\:border-orange-400:focus{--tw-border-opacity:1;border-color:rgb(251 146 60/var(--tw-border-opacity))}.focus\:border-red-500:focus{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity))}.focus\:border-subtle:focus{border-color:var(--cal-border-subtle,#e5e7eb)}.focus\:\!bg-transparent:focus{background-color:transparent!important}.focus\:bg-default:focus{background-color:var(--cal-bg,#fff)}.focus\:bg-emphasis:focus{background-color:var(--cal-bg-emphasis)}.focus\:bg-muted:focus{background-color:var(--cal-bg-muted)}.focus\:bg-subtle:focus{background-color:var(--cal-bg-subtle)}.focus\:text-emphasis:focus{color:var(--cal-text-emphasis,#111827)}.focus\:\!outline-none:focus{outline:2px solid transparent!important;outline-offset:2px!important}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:\!ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color)!important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important}.focus\:ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-0:focus,.focus\:ring-1:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-amber-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 243 199/var(--tw-ring-opacity))}.focus\:ring-amber-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(253 230 138/var(--tw-ring-opacity))}.focus\:ring-amber-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(252 211 77/var(--tw-ring-opacity))}.focus\:ring-amber-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(251 191 36/var(--tw-ring-opacity))}.focus\:ring-amber-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 251 235/var(--tw-ring-opacity))}.focus\:ring-amber-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(245 158 11/var(--tw-ring-opacity))}.focus\:ring-amber-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(217 119 6/var(--tw-ring-opacity))}.focus\:ring-amber-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(180 83 9/var(--tw-ring-opacity))}.focus\:ring-amber-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(146 64 14/var(--tw-ring-opacity))}.focus\:ring-amber-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(120 53 15/var(--tw-ring-opacity))}.focus\:ring-black:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(17 17 17/var(--tw-ring-opacity))}.focus\:ring-blue-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(219 234 254/var(--tw-ring-opacity))}.focus\:ring-blue-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(191 219 254/var(--tw-ring-opacity))}.focus\:ring-blue-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(147 197 253/var(--tw-ring-opacity))}.focus\:ring-blue-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(96 165 250/var(--tw-ring-opacity))}.focus\:ring-blue-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(239 246 255/var(--tw-ring-opacity))}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity))}.focus\:ring-blue-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(37 99 235/var(--tw-ring-opacity))}.focus\:ring-blue-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(29 78 216/var(--tw-ring-opacity))}.focus\:ring-blue-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(30 64 175/var(--tw-ring-opacity))}.focus\:ring-blue-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(30 58 138/var(--tw-ring-opacity))}.focus\:ring-brand-default:focus{--tw-ring-color:var(--cal-brand,#111827)}.focus\:ring-cyan-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(207 250 254/var(--tw-ring-opacity))}.focus\:ring-cyan-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(165 243 252/var(--tw-ring-opacity))}.focus\:ring-cyan-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(103 232 249/var(--tw-ring-opacity))}.focus\:ring-cyan-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(34 211 238/var(--tw-ring-opacity))}.focus\:ring-cyan-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(236 254 255/var(--tw-ring-opacity))}.focus\:ring-cyan-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(6 182 212/var(--tw-ring-opacity))}.focus\:ring-cyan-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(8 145 178/var(--tw-ring-opacity))}.focus\:ring-cyan-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(14 116 144/var(--tw-ring-opacity))}.focus\:ring-cyan-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(21 94 117/var(--tw-ring-opacity))}.focus\:ring-cyan-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(22 78 99/var(--tw-ring-opacity))}.focus\:ring-emerald-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(209 250 229/var(--tw-ring-opacity))}.focus\:ring-emerald-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(167 243 208/var(--tw-ring-opacity))}.focus\:ring-emerald-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(110 231 183/var(--tw-ring-opacity))}.focus\:ring-emerald-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(52 211 153/var(--tw-ring-opacity))}.focus\:ring-emerald-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(236 253 245/var(--tw-ring-opacity))}.focus\:ring-emerald-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(16 185 129/var(--tw-ring-opacity))}.focus\:ring-emerald-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(5 150 105/var(--tw-ring-opacity))}.focus\:ring-emerald-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(4 120 87/var(--tw-ring-opacity))}.focus\:ring-emerald-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(6 95 70/var(--tw-ring-opacity))}.focus\:ring-emerald-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(6 78 59/var(--tw-ring-opacity))}.focus\:ring-emphasis:focus{--tw-ring-color:var(--cal-bg-emphasis)}.focus\:ring-fuchsia-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(250 232 255/var(--tw-ring-opacity))}.focus\:ring-fuchsia-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(245 208 254/var(--tw-ring-opacity))}.focus\:ring-fuchsia-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(240 171 252/var(--tw-ring-opacity))}.focus\:ring-fuchsia-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(232 121 249/var(--tw-ring-opacity))}.focus\:ring-fuchsia-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(253 244 255/var(--tw-ring-opacity))}.focus\:ring-fuchsia-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(217 70 239/var(--tw-ring-opacity))}.focus\:ring-fuchsia-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(192 38 211/var(--tw-ring-opacity))}.focus\:ring-fuchsia-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(162 28 175/var(--tw-ring-opacity))}.focus\:ring-fuchsia-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(134 25 143/var(--tw-ring-opacity))}.focus\:ring-fuchsia-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(112 26 117/var(--tw-ring-opacity))}.focus\:ring-gray-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(243 244 246/var(--tw-ring-opacity))}.focus\:ring-gray-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(229 231 235/var(--tw-ring-opacity))}.focus\:ring-gray-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity))}.focus\:ring-gray-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(156 163 175/var(--tw-ring-opacity))}.focus\:ring-gray-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(249 250 251/var(--tw-ring-opacity))}.focus\:ring-gray-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(107 114 128/var(--tw-ring-opacity))}.focus\:ring-gray-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(75 85 99/var(--tw-ring-opacity))}.focus\:ring-gray-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(55 65 81/var(--tw-ring-opacity))}.focus\:ring-gray-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(31 41 55/var(--tw-ring-opacity))}.focus\:ring-gray-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(17 24 39/var(--tw-ring-opacity))}.focus\:ring-green-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(220 252 231/var(--tw-ring-opacity))}.focus\:ring-green-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(187 247 208/var(--tw-ring-opacity))}.focus\:ring-green-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(134 239 172/var(--tw-ring-opacity))}.focus\:ring-green-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(74 222 128/var(--tw-ring-opacity))}.focus\:ring-green-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(240 253 244/var(--tw-ring-opacity))}.focus\:ring-green-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(34 197 94/var(--tw-ring-opacity))}.focus\:ring-green-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(22 163 74/var(--tw-ring-opacity))}.focus\:ring-green-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(21 128 61/var(--tw-ring-opacity))}.focus\:ring-green-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(22 101 52/var(--tw-ring-opacity))}.focus\:ring-green-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(20 83 45/var(--tw-ring-opacity))}.focus\:ring-indigo-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(224 231 255/var(--tw-ring-opacity))}.focus\:ring-indigo-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(199 210 254/var(--tw-ring-opacity))}.focus\:ring-indigo-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(165 180 252/var(--tw-ring-opacity))}.focus\:ring-indigo-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(129 140 248/var(--tw-ring-opacity))}.focus\:ring-indigo-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(238 242 255/var(--tw-ring-opacity))}.focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.focus\:ring-indigo-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(79 70 229/var(--tw-ring-opacity))}.focus\:ring-indigo-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(67 56 202/var(--tw-ring-opacity))}.focus\:ring-indigo-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(55 48 163/var(--tw-ring-opacity))}.focus\:ring-indigo-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(49 46 129/var(--tw-ring-opacity))}.focus\:ring-lime-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(236 252 203/var(--tw-ring-opacity))}.focus\:ring-lime-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(217 249 157/var(--tw-ring-opacity))}.focus\:ring-lime-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(190 242 100/var(--tw-ring-opacity))}.focus\:ring-lime-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(163 230 53/var(--tw-ring-opacity))}.focus\:ring-lime-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(247 254 231/var(--tw-ring-opacity))}.focus\:ring-lime-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(132 204 22/var(--tw-ring-opacity))}.focus\:ring-lime-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(101 163 13/var(--tw-ring-opacity))}.focus\:ring-lime-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(77 124 15/var(--tw-ring-opacity))}.focus\:ring-lime-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(63 98 18/var(--tw-ring-opacity))}.focus\:ring-lime-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(54 83 20/var(--tw-ring-opacity))}.focus\:ring-muted:focus{--tw-ring-color:var(--cal-bg-muted)}.focus\:ring-neutral-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(245 245 245/var(--tw-ring-opacity))}.focus\:ring-neutral-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(229 229 229/var(--tw-ring-opacity))}.focus\:ring-neutral-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(212 212 212/var(--tw-ring-opacity))}.focus\:ring-neutral-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(163 163 163/var(--tw-ring-opacity))}.focus\:ring-neutral-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(250 250 250/var(--tw-ring-opacity))}.focus\:ring-neutral-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(115 115 115/var(--tw-ring-opacity))}.focus\:ring-neutral-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(82 82 82/var(--tw-ring-opacity))}.focus\:ring-neutral-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(64 64 64/var(--tw-ring-opacity))}.focus\:ring-neutral-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(38 38 38/var(--tw-ring-opacity))}.focus\:ring-neutral-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(23 23 23/var(--tw-ring-opacity))}.focus\:ring-orange-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 237 213/var(--tw-ring-opacity))}.focus\:ring-orange-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 215 170/var(--tw-ring-opacity))}.focus\:ring-orange-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(253 186 116/var(--tw-ring-opacity))}.focus\:ring-orange-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(251 146 60/var(--tw-ring-opacity))}.focus\:ring-orange-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 247 237/var(--tw-ring-opacity))}.focus\:ring-orange-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(249 115 22/var(--tw-ring-opacity))}.focus\:ring-orange-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(234 88 12/var(--tw-ring-opacity))}.focus\:ring-orange-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(194 65 12/var(--tw-ring-opacity))}.focus\:ring-orange-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(154 52 18/var(--tw-ring-opacity))}.focus\:ring-orange-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(124 45 18/var(--tw-ring-opacity))}.focus\:ring-pink-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(252 231 243/var(--tw-ring-opacity))}.focus\:ring-pink-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(251 207 232/var(--tw-ring-opacity))}.focus\:ring-pink-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(249 168 212/var(--tw-ring-opacity))}.focus\:ring-pink-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(244 114 182/var(--tw-ring-opacity))}.focus\:ring-pink-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(253 242 248/var(--tw-ring-opacity))}.focus\:ring-pink-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(236 72 153/var(--tw-ring-opacity))}.focus\:ring-pink-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(219 39 119/var(--tw-ring-opacity))}.focus\:ring-pink-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(190 24 93/var(--tw-ring-opacity))}.focus\:ring-pink-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(131 24 67/var(--tw-ring-opacity))}.focus\:ring-purple-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(243 232 255/var(--tw-ring-opacity))}.focus\:ring-purple-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(233 213 255/var(--tw-ring-opacity))}.focus\:ring-purple-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(216 180 254/var(--tw-ring-opacity))}.focus\:ring-purple-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(192 132 252/var(--tw-ring-opacity))}.focus\:ring-purple-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(250 245 255/var(--tw-ring-opacity))}.focus\:ring-purple-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(168 85 247/var(--tw-ring-opacity))}.focus\:ring-purple-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(147 51 234/var(--tw-ring-opacity))}.focus\:ring-purple-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(126 34 206/var(--tw-ring-opacity))}.focus\:ring-purple-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(107 33 168/var(--tw-ring-opacity))}.focus\:ring-purple-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(88 28 135/var(--tw-ring-opacity))}.focus\:ring-red-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 226 226/var(--tw-ring-opacity))}.focus\:ring-red-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 202 202/var(--tw-ring-opacity))}.focus\:ring-red-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(252 165 165/var(--tw-ring-opacity))}.focus\:ring-red-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(248 113 113/var(--tw-ring-opacity))}.focus\:ring-red-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 242 242/var(--tw-ring-opacity))}.focus\:ring-red-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(239 68 68/var(--tw-ring-opacity))}.focus\:ring-red-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(220 38 38/var(--tw-ring-opacity))}.focus\:ring-red-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(185 28 28/var(--tw-ring-opacity))}.focus\:ring-red-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(153 27 27/var(--tw-ring-opacity))}.focus\:ring-red-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(127 29 29/var(--tw-ring-opacity))}.focus\:ring-rose-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 228 230/var(--tw-ring-opacity))}.focus\:ring-rose-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 205 211/var(--tw-ring-opacity))}.focus\:ring-rose-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(253 164 175/var(--tw-ring-opacity))}.focus\:ring-rose-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(251 113 133/var(--tw-ring-opacity))}.focus\:ring-rose-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 241 242/var(--tw-ring-opacity))}.focus\:ring-rose-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(244 63 94/var(--tw-ring-opacity))}.focus\:ring-rose-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(225 29 72/var(--tw-ring-opacity))}.focus\:ring-rose-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(190 18 60/var(--tw-ring-opacity))}.focus\:ring-rose-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(159 18 57/var(--tw-ring-opacity))}.focus\:ring-rose-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(136 19 55/var(--tw-ring-opacity))}.focus\:ring-sky-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(224 242 254/var(--tw-ring-opacity))}.focus\:ring-sky-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(186 230 253/var(--tw-ring-opacity))}.focus\:ring-sky-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(125 211 252/var(--tw-ring-opacity))}.focus\:ring-sky-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(56 189 248/var(--tw-ring-opacity))}.focus\:ring-sky-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(240 249 255/var(--tw-ring-opacity))}.focus\:ring-sky-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(14 165 233/var(--tw-ring-opacity))}.focus\:ring-sky-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(2 132 199/var(--tw-ring-opacity))}.focus\:ring-sky-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(3 105 161/var(--tw-ring-opacity))}.focus\:ring-sky-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(7 89 133/var(--tw-ring-opacity))}.focus\:ring-sky-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(12 74 110/var(--tw-ring-opacity))}.focus\:ring-slate-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(241 245 249/var(--tw-ring-opacity))}.focus\:ring-slate-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(226 232 240/var(--tw-ring-opacity))}.focus\:ring-slate-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(203 213 225/var(--tw-ring-opacity))}.focus\:ring-slate-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(148 163 184/var(--tw-ring-opacity))}.focus\:ring-slate-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(248 250 252/var(--tw-ring-opacity))}.focus\:ring-slate-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(100 116 139/var(--tw-ring-opacity))}.focus\:ring-slate-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(71 85 105/var(--tw-ring-opacity))}.focus\:ring-slate-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(51 65 85/var(--tw-ring-opacity))}.focus\:ring-slate-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(30 41 59/var(--tw-ring-opacity))}.focus\:ring-slate-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(15 23 42/var(--tw-ring-opacity))}.focus\:ring-stone-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(245 245 244/var(--tw-ring-opacity))}.focus\:ring-stone-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(231 229 228/var(--tw-ring-opacity))}.focus\:ring-stone-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(214 211 209/var(--tw-ring-opacity))}.focus\:ring-stone-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(168 162 158/var(--tw-ring-opacity))}.focus\:ring-stone-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(250 250 249/var(--tw-ring-opacity))}.focus\:ring-stone-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(120 113 108/var(--tw-ring-opacity))}.focus\:ring-stone-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(87 83 78/var(--tw-ring-opacity))}.focus\:ring-stone-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(68 64 60/var(--tw-ring-opacity))}.focus\:ring-stone-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(41 37 36/var(--tw-ring-opacity))}.focus\:ring-stone-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(28 25 23/var(--tw-ring-opacity))}.focus\:ring-teal-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(204 251 241/var(--tw-ring-opacity))}.focus\:ring-teal-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(153 246 228/var(--tw-ring-opacity))}.focus\:ring-teal-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(94 234 212/var(--tw-ring-opacity))}.focus\:ring-teal-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(45 212 191/var(--tw-ring-opacity))}.focus\:ring-teal-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(240 253 250/var(--tw-ring-opacity))}.focus\:ring-teal-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(20 184 166/var(--tw-ring-opacity))}.focus\:ring-teal-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(13 148 136/var(--tw-ring-opacity))}.focus\:ring-teal-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(15 118 110/var(--tw-ring-opacity))}.focus\:ring-teal-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(17 94 89/var(--tw-ring-opacity))}.focus\:ring-teal-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(19 78 74/var(--tw-ring-opacity))}.focus\:ring-transparent:focus{--tw-ring-color:transparent}.focus\:ring-violet-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(237 233 254/var(--tw-ring-opacity))}.focus\:ring-violet-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(221 214 254/var(--tw-ring-opacity))}.focus\:ring-violet-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(196 181 253/var(--tw-ring-opacity))}.focus\:ring-violet-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(167 139 250/var(--tw-ring-opacity))}.focus\:ring-violet-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(245 243 255/var(--tw-ring-opacity))}.focus\:ring-violet-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(139 92 246/var(--tw-ring-opacity))}.focus\:ring-violet-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(124 58 237/var(--tw-ring-opacity))}.focus\:ring-violet-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(109 40 217/var(--tw-ring-opacity))}.focus\:ring-violet-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(91 33 182/var(--tw-ring-opacity))}.focus\:ring-violet-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(76 29 149/var(--tw-ring-opacity))}.focus\:ring-white:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}.focus\:ring-yellow-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 249 195/var(--tw-ring-opacity))}.focus\:ring-yellow-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 240 138/var(--tw-ring-opacity))}.focus\:ring-yellow-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(253 224 71/var(--tw-ring-opacity))}.focus\:ring-yellow-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(250 204 21/var(--tw-ring-opacity))}.focus\:ring-yellow-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 252 232/var(--tw-ring-opacity))}.focus\:ring-yellow-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(234 179 8/var(--tw-ring-opacity))}.focus\:ring-yellow-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(202 138 4/var(--tw-ring-opacity))}.focus\:ring-yellow-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(161 98 7/var(--tw-ring-opacity))}.focus\:ring-yellow-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(133 77 14/var(--tw-ring-opacity))}.focus\:ring-yellow-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(113 63 18/var(--tw-ring-opacity))}.focus\:ring-zinc-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(244 244 245/var(--tw-ring-opacity))}.focus\:ring-zinc-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(228 228 231/var(--tw-ring-opacity))}.focus\:ring-zinc-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(212 212 216/var(--tw-ring-opacity))}.focus\:ring-zinc-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(161 161 170/var(--tw-ring-opacity))}.focus\:ring-zinc-50:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(250 250 250/var(--tw-ring-opacity))}.focus\:ring-zinc-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(113 113 122/var(--tw-ring-opacity))}.focus\:ring-zinc-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(82 82 91/var(--tw-ring-opacity))}.focus\:ring-zinc-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(63 63 70/var(--tw-ring-opacity))}.focus\:ring-zinc-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(39 39 42/var(--tw-ring-opacity))}.focus\:ring-zinc-900:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(24 24 27/var(--tw-ring-opacity))}.focus\:\!ring-offset-0:focus{--tw-ring-offset-width:0px!important}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px}.focus\:ring-offset-1:focus{--tw-ring-offset-width:1px}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus-visible\:border-red-100:focus-visible{--tw-border-opacity:1;border-color:rgb(254 226 226/var(--tw-border-opacity))}.focus-visible\:bg-error:focus-visible{background-color:var(--cal-bg-error)}.focus-visible\:bg-subtle:focus-visible{background-color:var(--cal-bg-subtle)}.focus-visible\:bg-transparent:focus-visible{background-color:transparent}.focus-visible\:text-red-700:focus-visible{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-brand-default:focus-visible{--tw-ring-color:var(--cal-brand,#111827)}.focus-visible\:ring-red-700:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(185 28 28/var(--tw-ring-opacity))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-default:disabled{cursor:default}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:border-red-200:disabled{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity))}.disabled\:border-subtle:disabled{border-color:var(--cal-border-subtle,#e5e7eb)}.disabled\:border-transparent:disabled{border-color:transparent}.disabled\:bg-brand-subtle:disabled{background-color:var(--cal-brand-subtle,#9ca3af)}.disabled\:bg-emphasis:disabled{background-color:var(--cal-bg-emphasis)}.disabled\:bg-gray-200:disabled{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.disabled\:bg-red-100:disabled{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.disabled\:bg-subtle:disabled{background-color:var(--cal-bg-subtle)}.disabled\:bg-opacity-30:disabled{--tw-bg-opacity:0.3}.disabled\:font-light:disabled{font-weight:300}.disabled\:text-muted:disabled{color:var(--cal-text-muted,#9ca3af)}.disabled\:text-red-700:disabled{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:visible{visibility:visible}.group:hover .group-hover\:flex{display:flex}.group:hover .group-hover\:inline-flex{display:inline-flex}.group:hover .group-hover\:scale-100{--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:cursor-pointer{cursor:pointer}.group:hover .group-hover\:border-emphasis{border-color:var(--cal-border-emphasis,#9ca3af)}.group\/remove:hover .group-hover\/remove\:bg-inverted{background-color:var(--cal-bg-inverted)}.group:hover .group-hover\:bg-opacity-40{--tw-bg-opacity:0.4}.group\/remove:hover .group-hover\/remove\:text-inverted{color:var(--cal-text-inverted,#fff)}.group:hover .group-hover\:text-default{color:var(--cal-text,#374151)}.group:hover .group-hover\:text-emphasis{color:var(--cal-text-emphasis,#111827)}.group:hover .group-hover\:text-subtle{color:var(--cal-text-subtle,#6b7280)}.group:hover .group-hover\:opacity-100{opacity:1}.peer:checked~.peer-checked\:text-emphasis{color:var(--cal-text-emphasis,#111827)}.peer:checked~.peer-checked\:ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.aria-checked\:bg-emphasis[aria-checked=true]{background-color:var(--cal-bg-emphasis)}.aria-selected\:bg-muted[aria-selected=true]{background-color:var(--cal-bg-muted)}.aria-selected\:text-emphasis[aria-selected=true]{color:var(--cal-text-emphasis,#111827)}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[state\=checked\]\:bg-brand-default[data-state=checked]{background-color:var(--cal-brand,#111827)}.data-\[state\=selected\]\:bg-subtle[data-state=selected]{background-color:var(--cal-bg-subtle)}.data-\[state\=checked\]\:text-brand[data-state=checked]{color:var(--cal-brand-text,"white")}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity:initial;--tw-exit-scale:initial;--tw-exit-rotate:initial;--tw-exit-translate-x:initial;--tw-exit-translate-y:initial}.data-\[state\=closed\]\:fade-out[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=open\]\:fade-in[data-state=open]{--tw-enter-opacity:0}html.todesktop .todesktop\:-mt-3{margin-top:-.75rem}html.todesktop .todesktop\:mt-10{margin-top:2.5rem}html.todesktop .todesktop\:mt-4{margin-top:1rem}html.todesktop .todesktop\:block{display:block}html.todesktop .todesktop\:hidden{display:none}html.todesktop .todesktop\:flex-col-reverse{flex-direction:column-reverse}html.todesktop .todesktop\:\!bg-transparent{background-color:transparent!important}html.todesktop .todesktop\:py-\[7px\]{padding-top:7px;padding-bottom:7px}html.todesktop .todesktop\:\!text-blue-500{--tw-text-opacity:1!important;color:rgb(59 130 246/var(--tw-text-opacity))!important}html.todesktop .todesktop\:\[-webkit-app-region\:drag\]{-webkit-app-region:drag}.prose-headings\:text-emphasis :is(:where(h1,h2,h3,h4,h5,h6,th):not(:where([class~=not-prose] *))){color:var(--cal-text-emphasis,#111827)}.prose-h3\:font-cal :is(:where(h3):not(:where([class~=not-prose] *))){font-family:var(--font-cal),ui-serif,Georgia,Cambria,times new roman,Times,serif}.prose-h3\:text-white :is(:where(h3):not(:where([class~=not-prose] *))){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.prose-a\:text-default :is(:where(a):not(:where([class~=not-prose] *))){color:var(--cal-text,#374151)}.prose-a\:text-white :is(:where(a):not(:where([class~=not-prose] *))){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.prose-strong\:text-default :is(:where(strong):not(:where([class~=not-prose] *))){color:var(--cal-text,#374151)}.prose-code\:text-default :is(:where(code):not(:where([class~=not-prose] *))){color:var(--cal-text,#374151)}.hover\:-mt-px:hover{margin-top:-1px}.hover\:cursor-default:hover{cursor:default}.hover\:cursor-not-allowed:hover{cursor:not-allowed}.hover\:cursor-pointer:hover{cursor:pointer}.hover\:rounded-md:hover{border-radius:.375rem}.hover\:border-b-2:hover{border-bottom-width:2px}.hover\:border-amber-100:hover{--tw-border-opacity:1;border-color:rgb(254 243 199/var(--tw-border-opacity))}.hover\:border-amber-200:hover{--tw-border-opacity:1;border-color:rgb(253 230 138/var(--tw-border-opacity))}.hover\:border-amber-300:hover{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity))}.hover\:border-amber-400:hover{--tw-border-opacity:1;border-color:rgb(251 191 36/var(--tw-border-opacity))}.hover\:border-amber-50:hover{--tw-border-opacity:1;border-color:rgb(255 251 235/var(--tw-border-opacity))}.hover\:border-amber-500:hover{--tw-border-opacity:1;border-color:rgb(245 158 11/var(--tw-border-opacity))}.hover\:border-amber-600:hover{--tw-border-opacity:1;border-color:rgb(217 119 6/var(--tw-border-opacity))}.hover\:border-amber-700:hover{--tw-border-opacity:1;border-color:rgb(180 83 9/var(--tw-border-opacity))}.hover\:border-amber-800:hover{--tw-border-opacity:1;border-color:rgb(146 64 14/var(--tw-border-opacity))}.hover\:border-amber-900:hover{--tw-border-opacity:1;border-color:rgb(120 53 15/var(--tw-border-opacity))}.hover\:border-black:hover{--tw-border-opacity:1;border-color:rgb(17 17 17/var(--tw-border-opacity))}.hover\:border-blue-100:hover{--tw-border-opacity:1;border-color:rgb(219 234 254/var(--tw-border-opacity))}.hover\:border-blue-200:hover{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity))}.hover\:border-blue-300:hover{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity))}.hover\:border-blue-400:hover{--tw-border-opacity:1;border-color:rgb(96 165 250/var(--tw-border-opacity))}.hover\:border-blue-50:hover{--tw-border-opacity:1;border-color:rgb(239 246 255/var(--tw-border-opacity))}.hover\:border-blue-500:hover{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.hover\:border-blue-600:hover{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity))}.hover\:border-blue-700:hover{--tw-border-opacity:1;border-color:rgb(29 78 216/var(--tw-border-opacity))}.hover\:border-blue-800:hover{--tw-border-opacity:1;border-color:rgb(30 64 175/var(--tw-border-opacity))}.hover\:border-blue-900:hover{--tw-border-opacity:1;border-color:rgb(30 58 138/var(--tw-border-opacity))}.hover\:border-brand-default:hover{border-color:var(--cal-brand,#111827)}.hover\:border-cyan-100:hover{--tw-border-opacity:1;border-color:rgb(207 250 254/var(--tw-border-opacity))}.hover\:border-cyan-200:hover{--tw-border-opacity:1;border-color:rgb(165 243 252/var(--tw-border-opacity))}.hover\:border-cyan-300:hover{--tw-border-opacity:1;border-color:rgb(103 232 249/var(--tw-border-opacity))}.hover\:border-cyan-400:hover{--tw-border-opacity:1;border-color:rgb(34 211 238/var(--tw-border-opacity))}.hover\:border-cyan-50:hover{--tw-border-opacity:1;border-color:rgb(236 254 255/var(--tw-border-opacity))}.hover\:border-cyan-500:hover{--tw-border-opacity:1;border-color:rgb(6 182 212/var(--tw-border-opacity))}.hover\:border-cyan-600:hover{--tw-border-opacity:1;border-color:rgb(8 145 178/var(--tw-border-opacity))}.hover\:border-cyan-700:hover{--tw-border-opacity:1;border-color:rgb(14 116 144/var(--tw-border-opacity))}.hover\:border-cyan-800:hover{--tw-border-opacity:1;border-color:rgb(21 94 117/var(--tw-border-opacity))}.hover\:border-cyan-900:hover{--tw-border-opacity:1;border-color:rgb(22 78 99/var(--tw-border-opacity))}.hover\:border-default:hover{border-color:var(--cal-border,#d1d5db)}.hover\:border-emerald-100:hover{--tw-border-opacity:1;border-color:rgb(209 250 229/var(--tw-border-opacity))}.hover\:border-emerald-200:hover{--tw-border-opacity:1;border-color:rgb(167 243 208/var(--tw-border-opacity))}.hover\:border-emerald-300:hover{--tw-border-opacity:1;border-color:rgb(110 231 183/var(--tw-border-opacity))}.hover\:border-emerald-400:hover{--tw-border-opacity:1;border-color:rgb(52 211 153/var(--tw-border-opacity))}.hover\:border-emerald-50:hover{--tw-border-opacity:1;border-color:rgb(236 253 245/var(--tw-border-opacity))}.hover\:border-emerald-500:hover{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity))}.hover\:border-emerald-600:hover{--tw-border-opacity:1;border-color:rgb(5 150 105/var(--tw-border-opacity))}.hover\:border-emerald-700:hover{--tw-border-opacity:1;border-color:rgb(4 120 87/var(--tw-border-opacity))}.hover\:border-emerald-800:hover{--tw-border-opacity:1;border-color:rgb(6 95 70/var(--tw-border-opacity))}.hover\:border-emerald-900:hover{--tw-border-opacity:1;border-color:rgb(6 78 59/var(--tw-border-opacity))}.hover\:border-emphasis:hover{border-color:var(--cal-border-emphasis,#9ca3af)}.hover\:border-error:hover{border-color:var(--cal-border-error,#aa2e26)}.hover\:border-fuchsia-100:hover{--tw-border-opacity:1;border-color:rgb(250 232 255/var(--tw-border-opacity))}.hover\:border-fuchsia-200:hover{--tw-border-opacity:1;border-color:rgb(245 208 254/var(--tw-border-opacity))}.hover\:border-fuchsia-300:hover{--tw-border-opacity:1;border-color:rgb(240 171 252/var(--tw-border-opacity))}.hover\:border-fuchsia-400:hover{--tw-border-opacity:1;border-color:rgb(232 121 249/var(--tw-border-opacity))}.hover\:border-fuchsia-50:hover{--tw-border-opacity:1;border-color:rgb(253 244 255/var(--tw-border-opacity))}.hover\:border-fuchsia-500:hover{--tw-border-opacity:1;border-color:rgb(217 70 239/var(--tw-border-opacity))}.hover\:border-fuchsia-600:hover{--tw-border-opacity:1;border-color:rgb(192 38 211/var(--tw-border-opacity))}.hover\:border-fuchsia-700:hover{--tw-border-opacity:1;border-color:rgb(162 28 175/var(--tw-border-opacity))}.hover\:border-fuchsia-800:hover{--tw-border-opacity:1;border-color:rgb(134 25 143/var(--tw-border-opacity))}.hover\:border-fuchsia-900:hover{--tw-border-opacity:1;border-color:rgb(112 26 117/var(--tw-border-opacity))}.hover\:border-gray-100:hover{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.hover\:border-gray-200:hover{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.hover\:border-gray-400:hover{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.hover\:border-gray-50:hover{--tw-border-opacity:1;border-color:rgb(249 250 251/var(--tw-border-opacity))}.hover\:border-gray-500:hover{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.hover\:border-gray-600:hover{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}.hover\:border-gray-700:hover{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}.hover\:border-gray-800:hover{--tw-border-opacity:1;border-color:rgb(31 41 55/var(--tw-border-opacity))}.hover\:border-gray-900:hover{--tw-border-opacity:1;border-color:rgb(17 24 39/var(--tw-border-opacity))}.hover\:border-green-100:hover{--tw-border-opacity:1;border-color:rgb(220 252 231/var(--tw-border-opacity))}.hover\:border-green-200:hover{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity))}.hover\:border-green-300:hover{--tw-border-opacity:1;border-color:rgb(134 239 172/var(--tw-border-opacity))}.hover\:border-green-400:hover{--tw-border-opacity:1;border-color:rgb(74 222 128/var(--tw-border-opacity))}.hover\:border-green-50:hover{--tw-border-opacity:1;border-color:rgb(240 253 244/var(--tw-border-opacity))}.hover\:border-green-500:hover{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity))}.hover\:border-green-600:hover{--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity))}.hover\:border-green-700:hover{--tw-border-opacity:1;border-color:rgb(21 128 61/var(--tw-border-opacity))}.hover\:border-green-800:hover{--tw-border-opacity:1;border-color:rgb(22 101 52/var(--tw-border-opacity))}.hover\:border-green-900:hover{--tw-border-opacity:1;border-color:rgb(20 83 45/var(--tw-border-opacity))}.hover\:border-indigo-100:hover{--tw-border-opacity:1;border-color:rgb(224 231 255/var(--tw-border-opacity))}.hover\:border-indigo-200:hover{--tw-border-opacity:1;border-color:rgb(199 210 254/var(--tw-border-opacity))}.hover\:border-indigo-300:hover{--tw-border-opacity:1;border-color:rgb(165 180 252/var(--tw-border-opacity))}.hover\:border-indigo-400:hover{--tw-border-opacity:1;border-color:rgb(129 140 248/var(--tw-border-opacity))}.hover\:border-indigo-50:hover{--tw-border-opacity:1;border-color:rgb(238 242 255/var(--tw-border-opacity))}.hover\:border-indigo-500:hover{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity))}.hover\:border-indigo-600:hover{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity))}.hover\:border-indigo-700:hover{--tw-border-opacity:1;border-color:rgb(67 56 202/var(--tw-border-opacity))}.hover\:border-indigo-800:hover{--tw-border-opacity:1;border-color:rgb(55 48 163/var(--tw-border-opacity))}.hover\:border-indigo-900:hover{--tw-border-opacity:1;border-color:rgb(49 46 129/var(--tw-border-opacity))}.hover\:border-lime-100:hover{--tw-border-opacity:1;border-color:rgb(236 252 203/var(--tw-border-opacity))}.hover\:border-lime-200:hover{--tw-border-opacity:1;border-color:rgb(217 249 157/var(--tw-border-opacity))}.hover\:border-lime-300:hover{--tw-border-opacity:1;border-color:rgb(190 242 100/var(--tw-border-opacity))}.hover\:border-lime-400:hover{--tw-border-opacity:1;border-color:rgb(163 230 53/var(--tw-border-opacity))}.hover\:border-lime-50:hover{--tw-border-opacity:1;border-color:rgb(247 254 231/var(--tw-border-opacity))}.hover\:border-lime-500:hover{--tw-border-opacity:1;border-color:rgb(132 204 22/var(--tw-border-opacity))}.hover\:border-lime-600:hover{--tw-border-opacity:1;border-color:rgb(101 163 13/var(--tw-border-opacity))}.hover\:border-lime-700:hover{--tw-border-opacity:1;border-color:rgb(77 124 15/var(--tw-border-opacity))}.hover\:border-lime-800:hover{--tw-border-opacity:1;border-color:rgb(63 98 18/var(--tw-border-opacity))}.hover\:border-lime-900:hover{--tw-border-opacity:1;border-color:rgb(54 83 20/var(--tw-border-opacity))}.hover\:border-neutral-100:hover{--tw-border-opacity:1;border-color:rgb(245 245 245/var(--tw-border-opacity))}.hover\:border-neutral-200:hover{--tw-border-opacity:1;border-color:rgb(229 229 229/var(--tw-border-opacity))}.hover\:border-neutral-300:hover{--tw-border-opacity:1;border-color:rgb(212 212 212/var(--tw-border-opacity))}.hover\:border-neutral-400:hover{--tw-border-opacity:1;border-color:rgb(163 163 163/var(--tw-border-opacity))}.hover\:border-neutral-50:hover{--tw-border-opacity:1;border-color:rgb(250 250 250/var(--tw-border-opacity))}.hover\:border-neutral-500:hover{--tw-border-opacity:1;border-color:rgb(115 115 115/var(--tw-border-opacity))}.hover\:border-neutral-600:hover{--tw-border-opacity:1;border-color:rgb(82 82 82/var(--tw-border-opacity))}.hover\:border-neutral-700:hover{--tw-border-opacity:1;border-color:rgb(64 64 64/var(--tw-border-opacity))}.hover\:border-neutral-800:hover{--tw-border-opacity:1;border-color:rgb(38 38 38/var(--tw-border-opacity))}.hover\:border-neutral-900:hover{--tw-border-opacity:1;border-color:rgb(23 23 23/var(--tw-border-opacity))}.hover\:border-orange-100:hover{--tw-border-opacity:1;border-color:rgb(255 237 213/var(--tw-border-opacity))}.hover\:border-orange-200:hover{--tw-border-opacity:1;border-color:rgb(254 215 170/var(--tw-border-opacity))}.hover\:border-orange-300:hover{--tw-border-opacity:1;border-color:rgb(253 186 116/var(--tw-border-opacity))}.hover\:border-orange-400:hover{--tw-border-opacity:1;border-color:rgb(251 146 60/var(--tw-border-opacity))}.hover\:border-orange-50:hover{--tw-border-opacity:1;border-color:rgb(255 247 237/var(--tw-border-opacity))}.hover\:border-orange-500:hover{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity))}.hover\:border-orange-600:hover{--tw-border-opacity:1;border-color:rgb(234 88 12/var(--tw-border-opacity))}.hover\:border-orange-700:hover{--tw-border-opacity:1;border-color:rgb(194 65 12/var(--tw-border-opacity))}.hover\:border-orange-800:hover{--tw-border-opacity:1;border-color:rgb(154 52 18/var(--tw-border-opacity))}.hover\:border-orange-900:hover{--tw-border-opacity:1;border-color:rgb(124 45 18/var(--tw-border-opacity))}.hover\:border-pink-100:hover{--tw-border-opacity:1;border-color:rgb(252 231 243/var(--tw-border-opacity))}.hover\:border-pink-200:hover{--tw-border-opacity:1;border-color:rgb(251 207 232/var(--tw-border-opacity))}.hover\:border-pink-300:hover{--tw-border-opacity:1;border-color:rgb(249 168 212/var(--tw-border-opacity))}.hover\:border-pink-400:hover{--tw-border-opacity:1;border-color:rgb(244 114 182/var(--tw-border-opacity))}.hover\:border-pink-50:hover{--tw-border-opacity:1;border-color:rgb(253 242 248/var(--tw-border-opacity))}.hover\:border-pink-500:hover{--tw-border-opacity:1;border-color:rgb(236 72 153/var(--tw-border-opacity))}.hover\:border-pink-600:hover{--tw-border-opacity:1;border-color:rgb(219 39 119/var(--tw-border-opacity))}.hover\:border-pink-700:hover{--tw-border-opacity:1;border-color:rgb(190 24 93/var(--tw-border-opacity))}.hover\:border-pink-900:hover{--tw-border-opacity:1;border-color:rgb(131 24 67/var(--tw-border-opacity))}.hover\:border-purple-100:hover{--tw-border-opacity:1;border-color:rgb(243 232 255/var(--tw-border-opacity))}.hover\:border-purple-200:hover{--tw-border-opacity:1;border-color:rgb(233 213 255/var(--tw-border-opacity))}.hover\:border-purple-300:hover{--tw-border-opacity:1;border-color:rgb(216 180 254/var(--tw-border-opacity))}.hover\:border-purple-400:hover{--tw-border-opacity:1;border-color:rgb(192 132 252/var(--tw-border-opacity))}.hover\:border-purple-50:hover{--tw-border-opacity:1;border-color:rgb(250 245 255/var(--tw-border-opacity))}.hover\:border-purple-500:hover{--tw-border-opacity:1;border-color:rgb(168 85 247/var(--tw-border-opacity))}.hover\:border-purple-600:hover{--tw-border-opacity:1;border-color:rgb(147 51 234/var(--tw-border-opacity))}.hover\:border-purple-700:hover{--tw-border-opacity:1;border-color:rgb(126 34 206/var(--tw-border-opacity))}.hover\:border-purple-800:hover{--tw-border-opacity:1;border-color:rgb(107 33 168/var(--tw-border-opacity))}.hover\:border-purple-900:hover{--tw-border-opacity:1;border-color:rgb(88 28 135/var(--tw-border-opacity))}.hover\:border-red-100:hover{--tw-border-opacity:1;border-color:rgb(254 226 226/var(--tw-border-opacity))}.hover\:border-red-200:hover{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity))}.hover\:border-red-300:hover{--tw-border-opacity:1;border-color:rgb(252 165 165/var(--tw-border-opacity))}.hover\:border-red-400:hover{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.hover\:border-red-50:hover{--tw-border-opacity:1;border-color:rgb(254 242 242/var(--tw-border-opacity))}.hover\:border-red-500:hover{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity))}.hover\:border-red-600:hover{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity))}.hover\:border-red-700:hover{--tw-border-opacity:1;border-color:rgb(185 28 28/var(--tw-border-opacity))}.hover\:border-red-800:hover{--tw-border-opacity:1;border-color:rgb(153 27 27/var(--tw-border-opacity))}.hover\:border-red-900:hover{--tw-border-opacity:1;border-color:rgb(127 29 29/var(--tw-border-opacity))}.hover\:border-rose-100:hover{--tw-border-opacity:1;border-color:rgb(255 228 230/var(--tw-border-opacity))}.hover\:border-rose-200:hover{--tw-border-opacity:1;border-color:rgb(254 205 211/var(--tw-border-opacity))}.hover\:border-rose-300:hover{--tw-border-opacity:1;border-color:rgb(253 164 175/var(--tw-border-opacity))}.hover\:border-rose-400:hover{--tw-border-opacity:1;border-color:rgb(251 113 133/var(--tw-border-opacity))}.hover\:border-rose-50:hover{--tw-border-opacity:1;border-color:rgb(255 241 242/var(--tw-border-opacity))}.hover\:border-rose-500:hover{--tw-border-opacity:1;border-color:rgb(244 63 94/var(--tw-border-opacity))}.hover\:border-rose-600:hover{--tw-border-opacity:1;border-color:rgb(225 29 72/var(--tw-border-opacity))}.hover\:border-rose-700:hover{--tw-border-opacity:1;border-color:rgb(190 18 60/var(--tw-border-opacity))}.hover\:border-rose-800:hover{--tw-border-opacity:1;border-color:rgb(159 18 57/var(--tw-border-opacity))}.hover\:border-rose-900:hover{--tw-border-opacity:1;border-color:rgb(136 19 55/var(--tw-border-opacity))}.hover\:border-sky-100:hover{--tw-border-opacity:1;border-color:rgb(224 242 254/var(--tw-border-opacity))}.hover\:border-sky-200:hover{--tw-border-opacity:1;border-color:rgb(186 230 253/var(--tw-border-opacity))}.hover\:border-sky-300:hover{--tw-border-opacity:1;border-color:rgb(125 211 252/var(--tw-border-opacity))}.hover\:border-sky-400:hover{--tw-border-opacity:1;border-color:rgb(56 189 248/var(--tw-border-opacity))}.hover\:border-sky-50:hover{--tw-border-opacity:1;border-color:rgb(240 249 255/var(--tw-border-opacity))}.hover\:border-sky-500:hover{--tw-border-opacity:1;border-color:rgb(14 165 233/var(--tw-border-opacity))}.hover\:border-sky-600:hover{--tw-border-opacity:1;border-color:rgb(2 132 199/var(--tw-border-opacity))}.hover\:border-sky-700:hover{--tw-border-opacity:1;border-color:rgb(3 105 161/var(--tw-border-opacity))}.hover\:border-sky-800:hover{--tw-border-opacity:1;border-color:rgb(7 89 133/var(--tw-border-opacity))}.hover\:border-sky-900:hover{--tw-border-opacity:1;border-color:rgb(12 74 110/var(--tw-border-opacity))}.hover\:border-slate-100:hover{--tw-border-opacity:1;border-color:rgb(241 245 249/var(--tw-border-opacity))}.hover\:border-slate-200:hover{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity))}.hover\:border-slate-300:hover{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity))}.hover\:border-slate-400:hover{--tw-border-opacity:1;border-color:rgb(148 163 184/var(--tw-border-opacity))}.hover\:border-slate-50:hover{--tw-border-opacity:1;border-color:rgb(248 250 252/var(--tw-border-opacity))}.hover\:border-slate-500:hover{--tw-border-opacity:1;border-color:rgb(100 116 139/var(--tw-border-opacity))}.hover\:border-slate-600:hover{--tw-border-opacity:1;border-color:rgb(71 85 105/var(--tw-border-opacity))}.hover\:border-slate-700:hover{--tw-border-opacity:1;border-color:rgb(51 65 85/var(--tw-border-opacity))}.hover\:border-slate-800:hover{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity))}.hover\:border-slate-900:hover{--tw-border-opacity:1;border-color:rgb(15 23 42/var(--tw-border-opacity))}.hover\:border-stone-100:hover{--tw-border-opacity:1;border-color:rgb(245 245 244/var(--tw-border-opacity))}.hover\:border-stone-200:hover{--tw-border-opacity:1;border-color:rgb(231 229 228/var(--tw-border-opacity))}.hover\:border-stone-300:hover{--tw-border-opacity:1;border-color:rgb(214 211 209/var(--tw-border-opacity))}.hover\:border-stone-400:hover{--tw-border-opacity:1;border-color:rgb(168 162 158/var(--tw-border-opacity))}.hover\:border-stone-50:hover{--tw-border-opacity:1;border-color:rgb(250 250 249/var(--tw-border-opacity))}.hover\:border-stone-500:hover{--tw-border-opacity:1;border-color:rgb(120 113 108/var(--tw-border-opacity))}.hover\:border-stone-600:hover{--tw-border-opacity:1;border-color:rgb(87 83 78/var(--tw-border-opacity))}.hover\:border-stone-700:hover{--tw-border-opacity:1;border-color:rgb(68 64 60/var(--tw-border-opacity))}.hover\:border-stone-800:hover{--tw-border-opacity:1;border-color:rgb(41 37 36/var(--tw-border-opacity))}.hover\:border-stone-900:hover{--tw-border-opacity:1;border-color:rgb(28 25 23/var(--tw-border-opacity))}.hover\:border-teal-100:hover{--tw-border-opacity:1;border-color:rgb(204 251 241/var(--tw-border-opacity))}.hover\:border-teal-200:hover{--tw-border-opacity:1;border-color:rgb(153 246 228/var(--tw-border-opacity))}.hover\:border-teal-300:hover{--tw-border-opacity:1;border-color:rgb(94 234 212/var(--tw-border-opacity))}.hover\:border-teal-400:hover{--tw-border-opacity:1;border-color:rgb(45 212 191/var(--tw-border-opacity))}.hover\:border-teal-50:hover{--tw-border-opacity:1;border-color:rgb(240 253 250/var(--tw-border-opacity))}.hover\:border-teal-500:hover{--tw-border-opacity:1;border-color:rgb(20 184 166/var(--tw-border-opacity))}.hover\:border-teal-600:hover{--tw-border-opacity:1;border-color:rgb(13 148 136/var(--tw-border-opacity))}.hover\:border-teal-700:hover{--tw-border-opacity:1;border-color:rgb(15 118 110/var(--tw-border-opacity))}.hover\:border-teal-800:hover{--tw-border-opacity:1;border-color:rgb(17 94 89/var(--tw-border-opacity))}.hover\:border-teal-900:hover{--tw-border-opacity:1;border-color:rgb(19 78 74/var(--tw-border-opacity))}.hover\:border-transparent:hover{border-color:transparent}.hover\:border-violet-100:hover{--tw-border-opacity:1;border-color:rgb(237 233 254/var(--tw-border-opacity))}.hover\:border-violet-200:hover{--tw-border-opacity:1;border-color:rgb(221 214 254/var(--tw-border-opacity))}.hover\:border-violet-300:hover{--tw-border-opacity:1;border-color:rgb(196 181 253/var(--tw-border-opacity))}.hover\:border-violet-400:hover{--tw-border-opacity:1;border-color:rgb(167 139 250/var(--tw-border-opacity))}.hover\:border-violet-50:hover{--tw-border-opacity:1;border-color:rgb(245 243 255/var(--tw-border-opacity))}.hover\:border-violet-500:hover{--tw-border-opacity:1;border-color:rgb(139 92 246/var(--tw-border-opacity))}.hover\:border-violet-600:hover{--tw-border-opacity:1;border-color:rgb(124 58 237/var(--tw-border-opacity))}.hover\:border-violet-700:hover{--tw-border-opacity:1;border-color:rgb(109 40 217/var(--tw-border-opacity))}.hover\:border-violet-800:hover{--tw-border-opacity:1;border-color:rgb(91 33 182/var(--tw-border-opacity))}.hover\:border-violet-900:hover{--tw-border-opacity:1;border-color:rgb(76 29 149/var(--tw-border-opacity))}.hover\:border-white:hover{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity))}.hover\:border-yellow-100:hover{--tw-border-opacity:1;border-color:rgb(254 249 195/var(--tw-border-opacity))}.hover\:border-yellow-200:hover{--tw-border-opacity:1;border-color:rgb(254 240 138/var(--tw-border-opacity))}.hover\:border-yellow-300:hover{--tw-border-opacity:1;border-color:rgb(253 224 71/var(--tw-border-opacity))}.hover\:border-yellow-400:hover{--tw-border-opacity:1;border-color:rgb(250 204 21/var(--tw-border-opacity))}.hover\:border-yellow-50:hover{--tw-border-opacity:1;border-color:rgb(254 252 232/var(--tw-border-opacity))}.hover\:border-yellow-500:hover{--tw-border-opacity:1;border-color:rgb(234 179 8/var(--tw-border-opacity))}.hover\:border-yellow-600:hover{--tw-border-opacity:1;border-color:rgb(202 138 4/var(--tw-border-opacity))}.hover\:border-yellow-700:hover{--tw-border-opacity:1;border-color:rgb(161 98 7/var(--tw-border-opacity))}.hover\:border-yellow-800:hover{--tw-border-opacity:1;border-color:rgb(133 77 14/var(--tw-border-opacity))}.hover\:border-yellow-900:hover{--tw-border-opacity:1;border-color:rgb(113 63 18/var(--tw-border-opacity))}.hover\:border-zinc-100:hover{--tw-border-opacity:1;border-color:rgb(244 244 245/var(--tw-border-opacity))}.hover\:border-zinc-200:hover{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity))}.hover\:border-zinc-300:hover{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity))}.hover\:border-zinc-400:hover{--tw-border-opacity:1;border-color:rgb(161 161 170/var(--tw-border-opacity))}.hover\:border-zinc-50:hover{--tw-border-opacity:1;border-color:rgb(250 250 250/var(--tw-border-opacity))}.hover\:border-zinc-500:hover{--tw-border-opacity:1;border-color:rgb(113 113 122/var(--tw-border-opacity))}.hover\:border-zinc-600:hover{--tw-border-opacity:1;border-color:rgb(82 82 91/var(--tw-border-opacity))}.hover\:border-zinc-700:hover{--tw-border-opacity:1;border-color:rgb(63 63 70/var(--tw-border-opacity))}.hover\:border-zinc-800:hover{--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity))}.hover\:border-zinc-900:hover{--tw-border-opacity:1;border-color:rgb(24 24 27/var(--tw-border-opacity))}.hover\:\!bg-emphasis:hover{background-color:var(--cal-bg-emphasis)!important}.hover\:\!bg-transparent:hover{background-color:transparent!important}.hover\:bg-amber-100:hover{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity))}.hover\:bg-amber-200:hover{--tw-bg-opacity:1;background-color:rgb(253 230 138/var(--tw-bg-opacity))}.hover\:bg-amber-300:hover{--tw-bg-opacity:1;background-color:rgb(252 211 77/var(--tw-bg-opacity))}.hover\:bg-amber-400:hover{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity))}.hover\:bg-amber-50:hover{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity))}.hover\:bg-amber-500:hover{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity))}.hover\:bg-amber-600:hover{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity))}.hover\:bg-amber-700:hover{--tw-bg-opacity:1;background-color:rgb(180 83 9/var(--tw-bg-opacity))}.hover\:bg-amber-800:hover{--tw-bg-opacity:1;background-color:rgb(146 64 14/var(--tw-bg-opacity))}.hover\:bg-amber-900:hover{--tw-bg-opacity:1;background-color:rgb(120 53 15/var(--tw-bg-opacity))}.hover\:bg-attention:hover{background-color:var(--cal-bg-attention)}.hover\:bg-black:hover{--tw-bg-opacity:1;background-color:rgb(17 17 17/var(--tw-bg-opacity))}.hover\:bg-blue-100:hover{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity))}.hover\:bg-blue-200:hover{--tw-bg-opacity:1;background-color:rgb(191 219 254/var(--tw-bg-opacity))}.hover\:bg-blue-300:hover{--tw-bg-opacity:1;background-color:rgb(147 197 253/var(--tw-bg-opacity))}.hover\:bg-blue-400:hover{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity))}.hover\:bg-blue-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.hover\:bg-blue-500:hover{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity))}.hover\:bg-blue-800:hover{--tw-bg-opacity:1;background-color:rgb(30 64 175/var(--tw-bg-opacity))}.hover\:bg-blue-900:hover{--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity))}.hover\:bg-brand-default:hover{background-color:var(--cal-brand,#111827)}.hover\:bg-brand-emphasis:hover{background-color:var(--cal-brand-emphasis,#101010)}.hover\:bg-cyan-100:hover{--tw-bg-opacity:1;background-color:rgb(207 250 254/var(--tw-bg-opacity))}.hover\:bg-cyan-200:hover{--tw-bg-opacity:1;background-color:rgb(165 243 252/var(--tw-bg-opacity))}.hover\:bg-cyan-300:hover{--tw-bg-opacity:1;background-color:rgb(103 232 249/var(--tw-bg-opacity))}.hover\:bg-cyan-400:hover{--tw-bg-opacity:1;background-color:rgb(34 211 238/var(--tw-bg-opacity))}.hover\:bg-cyan-50:hover{--tw-bg-opacity:1;background-color:rgb(236 254 255/var(--tw-bg-opacity))}.hover\:bg-cyan-500:hover{--tw-bg-opacity:1;background-color:rgb(6 182 212/var(--tw-bg-opacity))}.hover\:bg-cyan-600:hover{--tw-bg-opacity:1;background-color:rgb(8 145 178/var(--tw-bg-opacity))}.hover\:bg-cyan-700:hover{--tw-bg-opacity:1;background-color:rgb(14 116 144/var(--tw-bg-opacity))}.hover\:bg-cyan-800:hover{--tw-bg-opacity:1;background-color:rgb(21 94 117/var(--tw-bg-opacity))}.hover\:bg-cyan-900:hover{--tw-bg-opacity:1;background-color:rgb(22 78 99/var(--tw-bg-opacity))}.hover\:bg-emerald-100:hover{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity))}.hover\:bg-emerald-200:hover{--tw-bg-opacity:1;background-color:rgb(167 243 208/var(--tw-bg-opacity))}.hover\:bg-emerald-300:hover{--tw-bg-opacity:1;background-color:rgb(110 231 183/var(--tw-bg-opacity))}.hover\:bg-emerald-400:hover{--tw-bg-opacity:1;background-color:rgb(52 211 153/var(--tw-bg-opacity))}.hover\:bg-emerald-50:hover{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity))}.hover\:bg-emerald-500:hover{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity))}.hover\:bg-emerald-600:hover{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity))}.hover\:bg-emerald-700:hover{--tw-bg-opacity:1;background-color:rgb(4 120 87/var(--tw-bg-opacity))}.hover\:bg-emerald-800:hover{--tw-bg-opacity:1;background-color:rgb(6 95 70/var(--tw-bg-opacity))}.hover\:bg-emerald-900:hover{--tw-bg-opacity:1;background-color:rgb(6 78 59/var(--tw-bg-opacity))}.hover\:bg-emphasis:hover{background-color:var(--cal-bg-emphasis)}.hover\:bg-error:hover{background-color:var(--cal-bg-error)}.hover\:bg-fuchsia-100:hover{--tw-bg-opacity:1;background-color:rgb(250 232 255/var(--tw-bg-opacity))}.hover\:bg-fuchsia-200:hover{--tw-bg-opacity:1;background-color:rgb(245 208 254/var(--tw-bg-opacity))}.hover\:bg-fuchsia-300:hover{--tw-bg-opacity:1;background-color:rgb(240 171 252/var(--tw-bg-opacity))}.hover\:bg-fuchsia-400:hover{--tw-bg-opacity:1;background-color:rgb(232 121 249/var(--tw-bg-opacity))}.hover\:bg-fuchsia-50:hover{--tw-bg-opacity:1;background-color:rgb(253 244 255/var(--tw-bg-opacity))}.hover\:bg-fuchsia-500:hover{--tw-bg-opacity:1;background-color:rgb(217 70 239/var(--tw-bg-opacity))}.hover\:bg-fuchsia-600:hover{--tw-bg-opacity:1;background-color:rgb(192 38 211/var(--tw-bg-opacity))}.hover\:bg-fuchsia-700:hover{--tw-bg-opacity:1;background-color:rgb(162 28 175/var(--tw-bg-opacity))}.hover\:bg-fuchsia-800:hover{--tw-bg-opacity:1;background-color:rgb(134 25 143/var(--tw-bg-opacity))}.hover\:bg-fuchsia-900:hover{--tw-bg-opacity:1;background-color:rgb(112 26 117/var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.hover\:bg-gray-400:hover{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.hover\:bg-gray-500:hover{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.hover\:bg-gray-800:hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.hover\:bg-gray-900:hover{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.hover\:bg-green-100:hover{--tw-bg-opacity:1;background-color:rgb(220 252 231/var(--tw-bg-opacity))}.hover\:bg-green-200:hover{--tw-bg-opacity:1;background-color:rgb(187 247 208/var(--tw-bg-opacity))}.hover\:bg-green-300:hover{--tw-bg-opacity:1;background-color:rgb(134 239 172/var(--tw-bg-opacity))}.hover\:bg-green-400:hover{--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity))}.hover\:bg-green-50:hover{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity))}.hover\:bg-green-500:hover{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity))}.hover\:bg-green-700:hover{--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity))}.hover\:bg-green-800:hover{--tw-bg-opacity:1;background-color:rgb(22 101 52/var(--tw-bg-opacity))}.hover\:bg-green-900:hover{--tw-bg-opacity:1;background-color:rgb(20 83 45/var(--tw-bg-opacity))}.hover\:bg-indigo-100:hover{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity))}.hover\:bg-indigo-200:hover{--tw-bg-opacity:1;background-color:rgb(199 210 254/var(--tw-bg-opacity))}.hover\:bg-indigo-300:hover{--tw-bg-opacity:1;background-color:rgb(165 180 252/var(--tw-bg-opacity))}.hover\:bg-indigo-400:hover{--tw-bg-opacity:1;background-color:rgb(129 140 248/var(--tw-bg-opacity))}.hover\:bg-indigo-50:hover{--tw-bg-opacity:1;background-color:rgb(238 242 255/var(--tw-bg-opacity))}.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.hover\:bg-indigo-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:bg-indigo-700:hover{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity))}.hover\:bg-indigo-800:hover{--tw-bg-opacity:1;background-color:rgb(55 48 163/var(--tw-bg-opacity))}.hover\:bg-indigo-900:hover{--tw-bg-opacity:1;background-color:rgb(49 46 129/var(--tw-bg-opacity))}.hover\:bg-inverted:hover{background-color:var(--cal-bg-inverted)}.hover\:bg-lime-100:hover{--tw-bg-opacity:1;background-color:rgb(236 252 203/var(--tw-bg-opacity))}.hover\:bg-lime-200:hover{--tw-bg-opacity:1;background-color:rgb(217 249 157/var(--tw-bg-opacity))}.hover\:bg-lime-300:hover{--tw-bg-opacity:1;background-color:rgb(190 242 100/var(--tw-bg-opacity))}.hover\:bg-lime-400:hover{--tw-bg-opacity:1;background-color:rgb(163 230 53/var(--tw-bg-opacity))}.hover\:bg-lime-50:hover{--tw-bg-opacity:1;background-color:rgb(247 254 231/var(--tw-bg-opacity))}.hover\:bg-lime-500:hover{--tw-bg-opacity:1;background-color:rgb(132 204 22/var(--tw-bg-opacity))}.hover\:bg-lime-600:hover{--tw-bg-opacity:1;background-color:rgb(101 163 13/var(--tw-bg-opacity))}.hover\:bg-lime-700:hover{--tw-bg-opacity:1;background-color:rgb(77 124 15/var(--tw-bg-opacity))}.hover\:bg-lime-800:hover{--tw-bg-opacity:1;background-color:rgb(63 98 18/var(--tw-bg-opacity))}.hover\:bg-lime-900:hover{--tw-bg-opacity:1;background-color:rgb(54 83 20/var(--tw-bg-opacity))}.hover\:bg-muted:hover{background-color:var(--cal-bg-muted)}.hover\:bg-neutral-100:hover{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity))}.hover\:bg-neutral-200:hover{--tw-bg-opacity:1;background-color:rgb(229 229 229/var(--tw-bg-opacity))}.hover\:bg-neutral-300:hover{--tw-bg-opacity:1;background-color:rgb(212 212 212/var(--tw-bg-opacity))}.hover\:bg-neutral-400:hover{--tw-bg-opacity:1;background-color:rgb(163 163 163/var(--tw-bg-opacity))}.hover\:bg-neutral-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity))}.hover\:bg-neutral-500:hover{--tw-bg-opacity:1;background-color:rgb(115 115 115/var(--tw-bg-opacity))}.hover\:bg-neutral-600:hover{--tw-bg-opacity:1;background-color:rgb(82 82 82/var(--tw-bg-opacity))}.hover\:bg-neutral-700:hover{--tw-bg-opacity:1;background-color:rgb(64 64 64/var(--tw-bg-opacity))}.hover\:bg-neutral-800:hover{--tw-bg-opacity:1;background-color:rgb(38 38 38/var(--tw-bg-opacity))}.hover\:bg-neutral-900:hover{--tw-bg-opacity:1;background-color:rgb(23 23 23/var(--tw-bg-opacity))}.hover\:bg-orange-100:hover{--tw-bg-opacity:1;background-color:rgb(255 237 213/var(--tw-bg-opacity))}.hover\:bg-orange-200:hover{--tw-bg-opacity:1;background-color:rgb(254 215 170/var(--tw-bg-opacity))}.hover\:bg-orange-300:hover{--tw-bg-opacity:1;background-color:rgb(253 186 116/var(--tw-bg-opacity))}.hover\:bg-orange-400:hover{--tw-bg-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity))}.hover\:bg-orange-50:hover{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity))}.hover\:bg-orange-500:hover{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity))}.hover\:bg-orange-600:hover{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity))}.hover\:bg-orange-700:hover{--tw-bg-opacity:1;background-color:rgb(194 65 12/var(--tw-bg-opacity))}.hover\:bg-orange-800:hover{--tw-bg-opacity:1;background-color:rgb(154 52 18/var(--tw-bg-opacity))}.hover\:bg-orange-900:hover{--tw-bg-opacity:1;background-color:rgb(124 45 18/var(--tw-bg-opacity))}.hover\:bg-pink-100:hover{--tw-bg-opacity:1;background-color:rgb(252 231 243/var(--tw-bg-opacity))}.hover\:bg-pink-200:hover{--tw-bg-opacity:1;background-color:rgb(251 207 232/var(--tw-bg-opacity))}.hover\:bg-pink-300:hover{--tw-bg-opacity:1;background-color:rgb(249 168 212/var(--tw-bg-opacity))}.hover\:bg-pink-400:hover{--tw-bg-opacity:1;background-color:rgb(244 114 182/var(--tw-bg-opacity))}.hover\:bg-pink-50:hover{--tw-bg-opacity:1;background-color:rgb(253 242 248/var(--tw-bg-opacity))}.hover\:bg-pink-500:hover{--tw-bg-opacity:1;background-color:rgb(236 72 153/var(--tw-bg-opacity))}.hover\:bg-pink-600:hover{--tw-bg-opacity:1;background-color:rgb(219 39 119/var(--tw-bg-opacity))}.hover\:bg-pink-700:hover{--tw-bg-opacity:1;background-color:rgb(190 24 93/var(--tw-bg-opacity))}.hover\:bg-pink-900:hover{--tw-bg-opacity:1;background-color:rgb(131 24 67/var(--tw-bg-opacity))}.hover\:bg-purple-100:hover{--tw-bg-opacity:1;background-color:rgb(243 232 255/var(--tw-bg-opacity))}.hover\:bg-purple-200:hover{--tw-bg-opacity:1;background-color:rgb(233 213 255/var(--tw-bg-opacity))}.hover\:bg-purple-300:hover{--tw-bg-opacity:1;background-color:rgb(216 180 254/var(--tw-bg-opacity))}.hover\:bg-purple-400:hover{--tw-bg-opacity:1;background-color:rgb(192 132 252/var(--tw-bg-opacity))}.hover\:bg-purple-50:hover{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity))}.hover\:bg-purple-500:hover{--tw-bg-opacity:1;background-color:rgb(168 85 247/var(--tw-bg-opacity))}.hover\:bg-purple-600:hover{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity))}.hover\:bg-purple-700:hover{--tw-bg-opacity:1;background-color:rgb(126 34 206/var(--tw-bg-opacity))}.hover\:bg-purple-800:hover{--tw-bg-opacity:1;background-color:rgb(107 33 168/var(--tw-bg-opacity))}.hover\:bg-purple-900:hover{--tw-bg-opacity:1;background-color:rgb(88 28 135/var(--tw-bg-opacity))}.hover\:bg-red-100:hover{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.hover\:bg-red-200:hover{--tw-bg-opacity:1;background-color:rgb(254 202 202/var(--tw-bg-opacity))}.hover\:bg-red-300:hover{--tw-bg-opacity:1;background-color:rgb(252 165 165/var(--tw-bg-opacity))}.hover\:bg-red-400:hover{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity))}.hover\:bg-red-50:hover{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity))}.hover\:bg-red-500:hover{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity))}.hover\:bg-red-800:hover{--tw-bg-opacity:1;background-color:rgb(153 27 27/var(--tw-bg-opacity))}.hover\:bg-red-900:hover{--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity))}.hover\:bg-rose-100:hover{--tw-bg-opacity:1;background-color:rgb(255 228 230/var(--tw-bg-opacity))}.hover\:bg-rose-200:hover{--tw-bg-opacity:1;background-color:rgb(254 205 211/var(--tw-bg-opacity))}.hover\:bg-rose-300:hover{--tw-bg-opacity:1;background-color:rgb(253 164 175/var(--tw-bg-opacity))}.hover\:bg-rose-400:hover{--tw-bg-opacity:1;background-color:rgb(251 113 133/var(--tw-bg-opacity))}.hover\:bg-rose-50:hover{--tw-bg-opacity:1;background-color:rgb(255 241 242/var(--tw-bg-opacity))}.hover\:bg-rose-500:hover{--tw-bg-opacity:1;background-color:rgb(244 63 94/var(--tw-bg-opacity))}.hover\:bg-rose-600:hover{--tw-bg-opacity:1;background-color:rgb(225 29 72/var(--tw-bg-opacity))}.hover\:bg-rose-700:hover{--tw-bg-opacity:1;background-color:rgb(190 18 60/var(--tw-bg-opacity))}.hover\:bg-rose-800:hover{--tw-bg-opacity:1;background-color:rgb(159 18 57/var(--tw-bg-opacity))}.hover\:bg-rose-900:hover{--tw-bg-opacity:1;background-color:rgb(136 19 55/var(--tw-bg-opacity))}.hover\:bg-sky-100:hover{--tw-bg-opacity:1;background-color:rgb(224 242 254/var(--tw-bg-opacity))}.hover\:bg-sky-200:hover{--tw-bg-opacity:1;background-color:rgb(186 230 253/var(--tw-bg-opacity))}.hover\:bg-sky-300:hover{--tw-bg-opacity:1;background-color:rgb(125 211 252/var(--tw-bg-opacity))}.hover\:bg-sky-400:hover{--tw-bg-opacity:1;background-color:rgb(56 189 248/var(--tw-bg-opacity))}.hover\:bg-sky-50:hover{--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity))}.hover\:bg-sky-500:hover{--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity))}.hover\:bg-sky-600:hover{--tw-bg-opacity:1;background-color:rgb(2 132 199/var(--tw-bg-opacity))}.hover\:bg-sky-700:hover{--tw-bg-opacity:1;background-color:rgb(3 105 161/var(--tw-bg-opacity))}.hover\:bg-sky-800:hover{--tw-bg-opacity:1;background-color:rgb(7 89 133/var(--tw-bg-opacity))}.hover\:bg-sky-900:hover{--tw-bg-opacity:1;background-color:rgb(12 74 110/var(--tw-bg-opacity))}.hover\:bg-slate-100:hover{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity))}.hover\:bg-slate-200:hover{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity))}.hover\:bg-slate-300:hover{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity))}.hover\:bg-slate-400:hover{--tw-bg-opacity:1;background-color:rgb(148 163 184/var(--tw-bg-opacity))}.hover\:bg-slate-50:hover{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity))}.hover\:bg-slate-500:hover{--tw-bg-opacity:1;background-color:rgb(100 116 139/var(--tw-bg-opacity))}.hover\:bg-slate-600:hover{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity))}.hover\:bg-slate-700:hover{--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity))}.hover\:bg-slate-800:hover{--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity))}.hover\:bg-slate-900:hover{--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity))}.hover\:bg-stone-100:hover{--tw-bg-opacity:1;background-color:rgb(245 245 244/var(--tw-bg-opacity))}.hover\:bg-stone-200:hover{--tw-bg-opacity:1;background-color:rgb(231 229 228/var(--tw-bg-opacity))}.hover\:bg-stone-300:hover{--tw-bg-opacity:1;background-color:rgb(214 211 209/var(--tw-bg-opacity))}.hover\:bg-stone-400:hover{--tw-bg-opacity:1;background-color:rgb(168 162 158/var(--tw-bg-opacity))}.hover\:bg-stone-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 249/var(--tw-bg-opacity))}.hover\:bg-stone-500:hover{--tw-bg-opacity:1;background-color:rgb(120 113 108/var(--tw-bg-opacity))}.hover\:bg-stone-600:hover{--tw-bg-opacity:1;background-color:rgb(87 83 78/var(--tw-bg-opacity))}.hover\:bg-stone-700:hover{--tw-bg-opacity:1;background-color:rgb(68 64 60/var(--tw-bg-opacity))}.hover\:bg-stone-800:hover{--tw-bg-opacity:1;background-color:rgb(41 37 36/var(--tw-bg-opacity))}.hover\:bg-stone-900:hover{--tw-bg-opacity:1;background-color:rgb(28 25 23/var(--tw-bg-opacity))}.hover\:bg-subtle:hover{background-color:var(--cal-bg-subtle)}.hover\:bg-success:hover{background-color:var(--cal-bg-success)}.hover\:bg-teal-100:hover{--tw-bg-opacity:1;background-color:rgb(204 251 241/var(--tw-bg-opacity))}.hover\:bg-teal-200:hover{--tw-bg-opacity:1;background-color:rgb(153 246 228/var(--tw-bg-opacity))}.hover\:bg-teal-300:hover{--tw-bg-opacity:1;background-color:rgb(94 234 212/var(--tw-bg-opacity))}.hover\:bg-teal-400:hover{--tw-bg-opacity:1;background-color:rgb(45 212 191/var(--tw-bg-opacity))}.hover\:bg-teal-50:hover{--tw-bg-opacity:1;background-color:rgb(240 253 250/var(--tw-bg-opacity))}.hover\:bg-teal-500:hover{--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity))}.hover\:bg-teal-600:hover{--tw-bg-opacity:1;background-color:rgb(13 148 136/var(--tw-bg-opacity))}.hover\:bg-teal-700:hover{--tw-bg-opacity:1;background-color:rgb(15 118 110/var(--tw-bg-opacity))}.hover\:bg-teal-800:hover{--tw-bg-opacity:1;background-color:rgb(17 94 89/var(--tw-bg-opacity))}.hover\:bg-teal-900:hover{--tw-bg-opacity:1;background-color:rgb(19 78 74/var(--tw-bg-opacity))}.hover\:bg-transparent:hover{background-color:transparent}.hover\:bg-violet-100:hover{--tw-bg-opacity:1;background-color:rgb(237 233 254/var(--tw-bg-opacity))}.hover\:bg-violet-200:hover{--tw-bg-opacity:1;background-color:rgb(221 214 254/var(--tw-bg-opacity))}.hover\:bg-violet-300:hover{--tw-bg-opacity:1;background-color:rgb(196 181 253/var(--tw-bg-opacity))}.hover\:bg-violet-400:hover{--tw-bg-opacity:1;background-color:rgb(167 139 250/var(--tw-bg-opacity))}.hover\:bg-violet-50:hover{--tw-bg-opacity:1;background-color:rgb(245 243 255/var(--tw-bg-opacity))}.hover\:bg-violet-500:hover{--tw-bg-opacity:1;background-color:rgb(139 92 246/var(--tw-bg-opacity))}.hover\:bg-violet-600:hover{--tw-bg-opacity:1;background-color:rgb(124 58 237/var(--tw-bg-opacity))}.hover\:bg-violet-700:hover{--tw-bg-opacity:1;background-color:rgb(109 40 217/var(--tw-bg-opacity))}.hover\:bg-violet-800:hover{--tw-bg-opacity:1;background-color:rgb(91 33 182/var(--tw-bg-opacity))}.hover\:bg-violet-900:hover{--tw-bg-opacity:1;background-color:rgb(76 29 149/var(--tw-bg-opacity))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.hover\:bg-yellow-100:hover{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity))}.hover\:bg-yellow-200:hover{--tw-bg-opacity:1;background-color:rgb(254 240 138/var(--tw-bg-opacity))}.hover\:bg-yellow-300:hover{--tw-bg-opacity:1;background-color:rgb(253 224 71/var(--tw-bg-opacity))}.hover\:bg-yellow-400:hover{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity))}.hover\:bg-yellow-50:hover{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity))}.hover\:bg-yellow-500:hover{--tw-bg-opacity:1;background-color:rgb(234 179 8/var(--tw-bg-opacity))}.hover\:bg-yellow-600:hover{--tw-bg-opacity:1;background-color:rgb(202 138 4/var(--tw-bg-opacity))}.hover\:bg-yellow-700:hover{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity))}.hover\:bg-yellow-800:hover{--tw-bg-opacity:1;background-color:rgb(133 77 14/var(--tw-bg-opacity))}.hover\:bg-yellow-900:hover{--tw-bg-opacity:1;background-color:rgb(113 63 18/var(--tw-bg-opacity))}.hover\:bg-zinc-100:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity))}.hover\:bg-zinc-200:hover{--tw-bg-opacity:1;background-color:rgb(228 228 231/var(--tw-bg-opacity))}.hover\:bg-zinc-300:hover{--tw-bg-opacity:1;background-color:rgb(212 212 216/var(--tw-bg-opacity))}.hover\:bg-zinc-400:hover{--tw-bg-opacity:1;background-color:rgb(161 161 170/var(--tw-bg-opacity))}.hover\:bg-zinc-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity))}.hover\:bg-zinc-500:hover{--tw-bg-opacity:1;background-color:rgb(113 113 122/var(--tw-bg-opacity))}.hover\:bg-zinc-600:hover{--tw-bg-opacity:1;background-color:rgb(82 82 91/var(--tw-bg-opacity))}.hover\:bg-zinc-700:hover{--tw-bg-opacity:1;background-color:rgb(63 63 70/var(--tw-bg-opacity))}.hover\:bg-zinc-800:hover{--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity))}.hover\:bg-zinc-900:hover{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity))}.hover\:font-normal:hover{font-weight:400}.hover\:text-amber-100:hover{--tw-text-opacity:1;color:rgb(254 243 199/var(--tw-text-opacity))}.hover\:text-amber-200:hover{--tw-text-opacity:1;color:rgb(253 230 138/var(--tw-text-opacity))}.hover\:text-amber-300:hover{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity))}.hover\:text-amber-400:hover{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity))}.hover\:text-amber-50:hover{--tw-text-opacity:1;color:rgb(255 251 235/var(--tw-text-opacity))}.hover\:text-amber-500:hover{--tw-text-opacity:1;color:rgb(245 158 11/var(--tw-text-opacity))}.hover\:text-amber-600:hover{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity))}.hover\:text-amber-700:hover{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity))}.hover\:text-amber-800:hover{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity))}.hover\:text-amber-900:hover{--tw-text-opacity:1;color:rgb(120 53 15/var(--tw-text-opacity))}.hover\:text-attention:hover{color:var(--cal-text-attention,#73321b)}.hover\:text-black:hover{--tw-text-opacity:1;color:rgb(17 17 17/var(--tw-text-opacity))}.hover\:text-blue-100:hover{--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity))}.hover\:text-blue-200:hover{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity))}.hover\:text-blue-300:hover{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity))}.hover\:text-blue-400:hover{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}.hover\:text-blue-50:hover{--tw-text-opacity:1;color:rgb(239 246 255/var(--tw-text-opacity))}.hover\:text-blue-500:hover{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.hover\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.hover\:text-blue-700:hover{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.hover\:text-blue-800:hover{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity))}.hover\:text-blue-900:hover{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity))}.hover\:text-cyan-100:hover{--tw-text-opacity:1;color:rgb(207 250 254/var(--tw-text-opacity))}.hover\:text-cyan-200:hover{--tw-text-opacity:1;color:rgb(165 243 252/var(--tw-text-opacity))}.hover\:text-cyan-300:hover{--tw-text-opacity:1;color:rgb(103 232 249/var(--tw-text-opacity))}.hover\:text-cyan-400:hover{--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity))}.hover\:text-cyan-50:hover{--tw-text-opacity:1;color:rgb(236 254 255/var(--tw-text-opacity))}.hover\:text-cyan-500:hover{--tw-text-opacity:1;color:rgb(6 182 212/var(--tw-text-opacity))}.hover\:text-cyan-600:hover{--tw-text-opacity:1;color:rgb(8 145 178/var(--tw-text-opacity))}.hover\:text-cyan-700:hover{--tw-text-opacity:1;color:rgb(14 116 144/var(--tw-text-opacity))}.hover\:text-cyan-800:hover{--tw-text-opacity:1;color:rgb(21 94 117/var(--tw-text-opacity))}.hover\:text-cyan-900:hover{--tw-text-opacity:1;color:rgb(22 78 99/var(--tw-text-opacity))}.hover\:text-default:hover{color:var(--cal-text,#374151)}.hover\:text-emerald-100:hover{--tw-text-opacity:1;color:rgb(209 250 229/var(--tw-text-opacity))}.hover\:text-emerald-200:hover{--tw-text-opacity:1;color:rgb(167 243 208/var(--tw-text-opacity))}.hover\:text-emerald-300:hover{--tw-text-opacity:1;color:rgb(110 231 183/var(--tw-text-opacity))}.hover\:text-emerald-400:hover{--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity))}.hover\:text-emerald-50:hover{--tw-text-opacity:1;color:rgb(236 253 245/var(--tw-text-opacity))}.hover\:text-emerald-500:hover{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity))}.hover\:text-emerald-600:hover{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity))}.hover\:text-emerald-700:hover{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity))}.hover\:text-emerald-800:hover{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity))}.hover\:text-emerald-900:hover{--tw-text-opacity:1;color:rgb(6 78 59/var(--tw-text-opacity))}.hover\:text-emphasis:hover{color:var(--cal-text-emphasis,#111827)}.hover\:text-fuchsia-100:hover{--tw-text-opacity:1;color:rgb(250 232 255/var(--tw-text-opacity))}.hover\:text-fuchsia-200:hover{--tw-text-opacity:1;color:rgb(245 208 254/var(--tw-text-opacity))}.hover\:text-fuchsia-300:hover{--tw-text-opacity:1;color:rgb(240 171 252/var(--tw-text-opacity))}.hover\:text-fuchsia-400:hover{--tw-text-opacity:1;color:rgb(232 121 249/var(--tw-text-opacity))}.hover\:text-fuchsia-50:hover{--tw-text-opacity:1;color:rgb(253 244 255/var(--tw-text-opacity))}.hover\:text-fuchsia-500:hover{--tw-text-opacity:1;color:rgb(217 70 239/var(--tw-text-opacity))}.hover\:text-fuchsia-600:hover{--tw-text-opacity:1;color:rgb(192 38 211/var(--tw-text-opacity))}.hover\:text-fuchsia-700:hover{--tw-text-opacity:1;color:rgb(162 28 175/var(--tw-text-opacity))}.hover\:text-fuchsia-800:hover{--tw-text-opacity:1;color:rgb(134 25 143/var(--tw-text-opacity))}.hover\:text-fuchsia-900:hover{--tw-text-opacity:1;color:rgb(112 26 117/var(--tw-text-opacity))}.hover\:text-gray-100:hover{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.hover\:text-gray-200:hover{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.hover\:text-gray-300:hover{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.hover\:text-gray-400:hover{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.hover\:text-gray-50:hover{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.hover\:text-gray-500:hover{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.hover\:text-green-100:hover{--tw-text-opacity:1;color:rgb(220 252 231/var(--tw-text-opacity))}.hover\:text-green-200:hover{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity))}.hover\:text-green-300:hover{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity))}.hover\:text-green-400:hover{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity))}.hover\:text-green-50:hover{--tw-text-opacity:1;color:rgb(240 253 244/var(--tw-text-opacity))}.hover\:text-green-500:hover{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity))}.hover\:text-green-600:hover{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity))}.hover\:text-green-700:hover{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity))}.hover\:text-green-800:hover{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity))}.hover\:text-green-900:hover{--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity))}.hover\:text-indigo-100:hover{--tw-text-opacity:1;color:rgb(224 231 255/var(--tw-text-opacity))}.hover\:text-indigo-200:hover{--tw-text-opacity:1;color:rgb(199 210 254/var(--tw-text-opacity))}.hover\:text-indigo-300:hover{--tw-text-opacity:1;color:rgb(165 180 252/var(--tw-text-opacity))}.hover\:text-indigo-400:hover{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity))}.hover\:text-indigo-50:hover{--tw-text-opacity:1;color:rgb(238 242 255/var(--tw-text-opacity))}.hover\:text-indigo-500:hover{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity))}.hover\:text-indigo-600:hover{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.hover\:text-indigo-700:hover{--tw-text-opacity:1;color:rgb(67 56 202/var(--tw-text-opacity))}.hover\:text-indigo-800:hover{--tw-text-opacity:1;color:rgb(55 48 163/var(--tw-text-opacity))}.hover\:text-indigo-900:hover{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity))}.hover\:text-lime-100:hover{--tw-text-opacity:1;color:rgb(236 252 203/var(--tw-text-opacity))}.hover\:text-lime-200:hover{--tw-text-opacity:1;color:rgb(217 249 157/var(--tw-text-opacity))}.hover\:text-lime-300:hover{--tw-text-opacity:1;color:rgb(190 242 100/var(--tw-text-opacity))}.hover\:text-lime-400:hover{--tw-text-opacity:1;color:rgb(163 230 53/var(--tw-text-opacity))}.hover\:text-lime-50:hover{--tw-text-opacity:1;color:rgb(247 254 231/var(--tw-text-opacity))}.hover\:text-lime-500:hover{--tw-text-opacity:1;color:rgb(132 204 22/var(--tw-text-opacity))}.hover\:text-lime-600:hover{--tw-text-opacity:1;color:rgb(101 163 13/var(--tw-text-opacity))}.hover\:text-lime-700:hover{--tw-text-opacity:1;color:rgb(77 124 15/var(--tw-text-opacity))}.hover\:text-lime-800:hover{--tw-text-opacity:1;color:rgb(63 98 18/var(--tw-text-opacity))}.hover\:text-lime-900:hover{--tw-text-opacity:1;color:rgb(54 83 20/var(--tw-text-opacity))}.hover\:text-muted:hover{color:var(--cal-text-muted,#9ca3af)}.hover\:text-neutral-100:hover{--tw-text-opacity:1;color:rgb(245 245 245/var(--tw-text-opacity))}.hover\:text-neutral-200:hover{--tw-text-opacity:1;color:rgb(229 229 229/var(--tw-text-opacity))}.hover\:text-neutral-300:hover{--tw-text-opacity:1;color:rgb(212 212 212/var(--tw-text-opacity))}.hover\:text-neutral-400:hover{--tw-text-opacity:1;color:rgb(163 163 163/var(--tw-text-opacity))}.hover\:text-neutral-50:hover{--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity))}.hover\:text-neutral-500:hover{--tw-text-opacity:1;color:rgb(115 115 115/var(--tw-text-opacity))}.hover\:text-neutral-600:hover{--tw-text-opacity:1;color:rgb(82 82 82/var(--tw-text-opacity))}.hover\:text-neutral-700:hover{--tw-text-opacity:1;color:rgb(64 64 64/var(--tw-text-opacity))}.hover\:text-neutral-800:hover{--tw-text-opacity:1;color:rgb(38 38 38/var(--tw-text-opacity))}.hover\:text-neutral-900:hover{--tw-text-opacity:1;color:rgb(23 23 23/var(--tw-text-opacity))}.hover\:text-orange-100:hover{--tw-text-opacity:1;color:rgb(255 237 213/var(--tw-text-opacity))}.hover\:text-orange-200:hover{--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity))}.hover\:text-orange-300:hover{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity))}.hover\:text-orange-400:hover{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity))}.hover\:text-orange-50:hover{--tw-text-opacity:1;color:rgb(255 247 237/var(--tw-text-opacity))}.hover\:text-orange-500:hover{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity))}.hover\:text-orange-600:hover{--tw-text-opacity:1;color:rgb(234 88 12/var(--tw-text-opacity))}.hover\:text-orange-700:hover{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity))}.hover\:text-orange-800:hover{--tw-text-opacity:1;color:rgb(154 52 18/var(--tw-text-opacity))}.hover\:text-orange-900:hover{--tw-text-opacity:1;color:rgb(124 45 18/var(--tw-text-opacity))}.hover\:text-pink-100:hover{--tw-text-opacity:1;color:rgb(252 231 243/var(--tw-text-opacity))}.hover\:text-pink-200:hover{--tw-text-opacity:1;color:rgb(251 207 232/var(--tw-text-opacity))}.hover\:text-pink-300:hover{--tw-text-opacity:1;color:rgb(249 168 212/var(--tw-text-opacity))}.hover\:text-pink-400:hover{--tw-text-opacity:1;color:rgb(244 114 182/var(--tw-text-opacity))}.hover\:text-pink-50:hover{--tw-text-opacity:1;color:rgb(253 242 248/var(--tw-text-opacity))}.hover\:text-pink-500:hover{--tw-text-opacity:1;color:rgb(236 72 153/var(--tw-text-opacity))}.hover\:text-pink-600:hover{--tw-text-opacity:1;color:rgb(219 39 119/var(--tw-text-opacity))}.hover\:text-pink-700:hover{--tw-text-opacity:1;color:rgb(190 24 93/var(--tw-text-opacity))}.hover\:text-pink-900:hover{--tw-text-opacity:1;color:rgb(131 24 67/var(--tw-text-opacity))}.hover\:text-purple-100:hover{--tw-text-opacity:1;color:rgb(243 232 255/var(--tw-text-opacity))}.hover\:text-purple-200:hover{--tw-text-opacity:1;color:rgb(233 213 255/var(--tw-text-opacity))}.hover\:text-purple-300:hover{--tw-text-opacity:1;color:rgb(216 180 254/var(--tw-text-opacity))}.hover\:text-purple-400:hover{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity))}.hover\:text-purple-50:hover{--tw-text-opacity:1;color:rgb(250 245 255/var(--tw-text-opacity))}.hover\:text-purple-500:hover{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity))}.hover\:text-purple-600:hover{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity))}.hover\:text-purple-700:hover{--tw-text-opacity:1;color:rgb(126 34 206/var(--tw-text-opacity))}.hover\:text-purple-800:hover{--tw-text-opacity:1;color:rgb(107 33 168/var(--tw-text-opacity))}.hover\:text-purple-900:hover{--tw-text-opacity:1;color:rgb(88 28 135/var(--tw-text-opacity))}.hover\:text-red-100:hover{--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity))}.hover\:text-red-200:hover{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity))}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity))}.hover\:text-red-400:hover{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity))}.hover\:text-red-50:hover{--tw-text-opacity:1;color:rgb(254 242 242/var(--tw-text-opacity))}.hover\:text-red-500:hover{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.hover\:text-red-600:hover{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}.hover\:text-red-700:hover{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.hover\:text-red-700\/30:hover{color:rgba(185,28,28,.3)}.hover\:text-red-800:hover{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity))}.hover\:text-red-900:hover{--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity))}.hover\:text-rose-100:hover{--tw-text-opacity:1;color:rgb(255 228 230/var(--tw-text-opacity))}.hover\:text-rose-200:hover{--tw-text-opacity:1;color:rgb(254 205 211/var(--tw-text-opacity))}.hover\:text-rose-300:hover{--tw-text-opacity:1;color:rgb(253 164 175/var(--tw-text-opacity))}.hover\:text-rose-400:hover{--tw-text-opacity:1;color:rgb(251 113 133/var(--tw-text-opacity))}.hover\:text-rose-50:hover{--tw-text-opacity:1;color:rgb(255 241 242/var(--tw-text-opacity))}.hover\:text-rose-500:hover{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity))}.hover\:text-rose-600:hover{--tw-text-opacity:1;color:rgb(225 29 72/var(--tw-text-opacity))}.hover\:text-rose-700:hover{--tw-text-opacity:1;color:rgb(190 18 60/var(--tw-text-opacity))}.hover\:text-rose-800:hover{--tw-text-opacity:1;color:rgb(159 18 57/var(--tw-text-opacity))}.hover\:text-rose-900:hover{--tw-text-opacity:1;color:rgb(136 19 55/var(--tw-text-opacity))}.hover\:text-sky-100:hover{--tw-text-opacity:1;color:rgb(224 242 254/var(--tw-text-opacity))}.hover\:text-sky-200:hover{--tw-text-opacity:1;color:rgb(186 230 253/var(--tw-text-opacity))}.hover\:text-sky-300:hover{--tw-text-opacity:1;color:rgb(125 211 252/var(--tw-text-opacity))}.hover\:text-sky-400:hover{--tw-text-opacity:1;color:rgb(56 189 248/var(--tw-text-opacity))}.hover\:text-sky-50:hover{--tw-text-opacity:1;color:rgb(240 249 255/var(--tw-text-opacity))}.hover\:text-sky-500:hover{--tw-text-opacity:1;color:rgb(14 165 233/var(--tw-text-opacity))}.hover\:text-sky-600:hover{--tw-text-opacity:1;color:rgb(2 132 199/var(--tw-text-opacity))}.hover\:text-sky-700:hover{--tw-text-opacity:1;color:rgb(3 105 161/var(--tw-text-opacity))}.hover\:text-sky-800:hover{--tw-text-opacity:1;color:rgb(7 89 133/var(--tw-text-opacity))}.hover\:text-sky-900:hover{--tw-text-opacity:1;color:rgb(12 74 110/var(--tw-text-opacity))}.hover\:text-slate-100:hover{--tw-text-opacity:1;color:rgb(241 245 249/var(--tw-text-opacity))}.hover\:text-slate-200:hover{--tw-text-opacity:1;color:rgb(226 232 240/var(--tw-text-opacity))}.hover\:text-slate-300:hover{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity))}.hover\:text-slate-400:hover{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity))}.hover\:text-slate-50:hover{--tw-text-opacity:1;color:rgb(248 250 252/var(--tw-text-opacity))}.hover\:text-slate-500:hover{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity))}.hover\:text-slate-600:hover{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity))}.hover\:text-slate-700:hover{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity))}.hover\:text-slate-800:hover{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity))}.hover\:text-slate-900:hover{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity))}.hover\:text-stone-100:hover{--tw-text-opacity:1;color:rgb(245 245 244/var(--tw-text-opacity))}.hover\:text-stone-200:hover{--tw-text-opacity:1;color:rgb(231 229 228/var(--tw-text-opacity))}.hover\:text-stone-300:hover{--tw-text-opacity:1;color:rgb(214 211 209/var(--tw-text-opacity))}.hover\:text-stone-400:hover{--tw-text-opacity:1;color:rgb(168 162 158/var(--tw-text-opacity))}.hover\:text-stone-50:hover{--tw-text-opacity:1;color:rgb(250 250 249/var(--tw-text-opacity))}.hover\:text-stone-500:hover{--tw-text-opacity:1;color:rgb(120 113 108/var(--tw-text-opacity))}.hover\:text-stone-600:hover{--tw-text-opacity:1;color:rgb(87 83 78/var(--tw-text-opacity))}.hover\:text-stone-700:hover{--tw-text-opacity:1;color:rgb(68 64 60/var(--tw-text-opacity))}.hover\:text-stone-800:hover{--tw-text-opacity:1;color:rgb(41 37 36/var(--tw-text-opacity))}.hover\:text-stone-900:hover{--tw-text-opacity:1;color:rgb(28 25 23/var(--tw-text-opacity))}.hover\:text-subtle:hover{color:var(--cal-text-subtle,#6b7280)}.hover\:text-teal-100:hover{--tw-text-opacity:1;color:rgb(204 251 241/var(--tw-text-opacity))}.hover\:text-teal-200:hover{--tw-text-opacity:1;color:rgb(153 246 228/var(--tw-text-opacity))}.hover\:text-teal-300:hover{--tw-text-opacity:1;color:rgb(94 234 212/var(--tw-text-opacity))}.hover\:text-teal-400:hover{--tw-text-opacity:1;color:rgb(45 212 191/var(--tw-text-opacity))}.hover\:text-teal-50:hover{--tw-text-opacity:1;color:rgb(240 253 250/var(--tw-text-opacity))}.hover\:text-teal-500:hover{--tw-text-opacity:1;color:rgb(20 184 166/var(--tw-text-opacity))}.hover\:text-teal-600:hover{--tw-text-opacity:1;color:rgb(13 148 136/var(--tw-text-opacity))}.hover\:text-teal-700:hover{--tw-text-opacity:1;color:rgb(15 118 110/var(--tw-text-opacity))}.hover\:text-teal-800:hover{--tw-text-opacity:1;color:rgb(17 94 89/var(--tw-text-opacity))}.hover\:text-teal-900:hover{--tw-text-opacity:1;color:rgb(19 78 74/var(--tw-text-opacity))}.hover\:text-transparent:hover{color:transparent}.hover\:text-violet-100:hover{--tw-text-opacity:1;color:rgb(237 233 254/var(--tw-text-opacity))}.hover\:text-violet-200:hover{--tw-text-opacity:1;color:rgb(221 214 254/var(--tw-text-opacity))}.hover\:text-violet-300:hover{--tw-text-opacity:1;color:rgb(196 181 253/var(--tw-text-opacity))}.hover\:text-violet-400:hover{--tw-text-opacity:1;color:rgb(167 139 250/var(--tw-text-opacity))}.hover\:text-violet-50:hover{--tw-text-opacity:1;color:rgb(245 243 255/var(--tw-text-opacity))}.hover\:text-violet-500:hover{--tw-text-opacity:1;color:rgb(139 92 246/var(--tw-text-opacity))}.hover\:text-violet-600:hover{--tw-text-opacity:1;color:rgb(124 58 237/var(--tw-text-opacity))}.hover\:text-violet-700:hover{--tw-text-opacity:1;color:rgb(109 40 217/var(--tw-text-opacity))}.hover\:text-violet-800:hover{--tw-text-opacity:1;color:rgb(91 33 182/var(--tw-text-opacity))}.hover\:text-violet-900:hover{--tw-text-opacity:1;color:rgb(76 29 149/var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.hover\:text-yellow-100:hover{--tw-text-opacity:1;color:rgb(254 249 195/var(--tw-text-opacity))}.hover\:text-yellow-200:hover{--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity))}.hover\:text-yellow-300:hover{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity))}.hover\:text-yellow-400:hover{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity))}.hover\:text-yellow-50:hover{--tw-text-opacity:1;color:rgb(254 252 232/var(--tw-text-opacity))}.hover\:text-yellow-500:hover{--tw-text-opacity:1;color:rgb(234 179 8/var(--tw-text-opacity))}.hover\:text-yellow-600:hover{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity))}.hover\:text-yellow-700:hover{--tw-text-opacity:1;color:rgb(161 98 7/var(--tw-text-opacity))}.hover\:text-yellow-800:hover{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity))}.hover\:text-yellow-900:hover{--tw-text-opacity:1;color:rgb(113 63 18/var(--tw-text-opacity))}.hover\:text-zinc-100:hover{--tw-text-opacity:1;color:rgb(244 244 245/var(--tw-text-opacity))}.hover\:text-zinc-200:hover{--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity))}.hover\:text-zinc-300:hover{--tw-text-opacity:1;color:rgb(212 212 216/var(--tw-text-opacity))}.hover\:text-zinc-400:hover{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity))}.hover\:text-zinc-50:hover{--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity))}.hover\:text-zinc-500:hover{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity))}.hover\:text-zinc-600:hover{--tw-text-opacity:1;color:rgb(82 82 91/var(--tw-text-opacity))}.hover\:text-zinc-700:hover{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity))}.hover\:text-zinc-800:hover{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity))}.hover\:text-zinc-900:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:opacity-70:hover{opacity:.7}.hover\:shadow:hover{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.hover\:shadow-sm:hover,.hover\:shadow:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-sm:hover{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.hover\:ring-0:hover{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.hover\:ring-amber-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(254 243 199/var(--tw-ring-opacity))}.hover\:ring-amber-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(253 230 138/var(--tw-ring-opacity))}.hover\:ring-amber-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(252 211 77/var(--tw-ring-opacity))}.hover\:ring-amber-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(251 191 36/var(--tw-ring-opacity))}.hover\:ring-amber-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(255 251 235/var(--tw-ring-opacity))}.hover\:ring-amber-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(245 158 11/var(--tw-ring-opacity))}.hover\:ring-amber-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(217 119 6/var(--tw-ring-opacity))}.hover\:ring-amber-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(180 83 9/var(--tw-ring-opacity))}.hover\:ring-amber-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(146 64 14/var(--tw-ring-opacity))}.hover\:ring-amber-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(120 53 15/var(--tw-ring-opacity))}.hover\:ring-black:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(17 17 17/var(--tw-ring-opacity))}.hover\:ring-blue-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(219 234 254/var(--tw-ring-opacity))}.hover\:ring-blue-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(191 219 254/var(--tw-ring-opacity))}.hover\:ring-blue-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(147 197 253/var(--tw-ring-opacity))}.hover\:ring-blue-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(96 165 250/var(--tw-ring-opacity))}.hover\:ring-blue-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(239 246 255/var(--tw-ring-opacity))}.hover\:ring-blue-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity))}.hover\:ring-blue-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(37 99 235/var(--tw-ring-opacity))}.hover\:ring-blue-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(29 78 216/var(--tw-ring-opacity))}.hover\:ring-blue-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(30 64 175/var(--tw-ring-opacity))}.hover\:ring-blue-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(30 58 138/var(--tw-ring-opacity))}.hover\:ring-cyan-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(207 250 254/var(--tw-ring-opacity))}.hover\:ring-cyan-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(165 243 252/var(--tw-ring-opacity))}.hover\:ring-cyan-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(103 232 249/var(--tw-ring-opacity))}.hover\:ring-cyan-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(34 211 238/var(--tw-ring-opacity))}.hover\:ring-cyan-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(236 254 255/var(--tw-ring-opacity))}.hover\:ring-cyan-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(6 182 212/var(--tw-ring-opacity))}.hover\:ring-cyan-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(8 145 178/var(--tw-ring-opacity))}.hover\:ring-cyan-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(14 116 144/var(--tw-ring-opacity))}.hover\:ring-cyan-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(21 94 117/var(--tw-ring-opacity))}.hover\:ring-cyan-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(22 78 99/var(--tw-ring-opacity))}.hover\:ring-emerald-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(209 250 229/var(--tw-ring-opacity))}.hover\:ring-emerald-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(167 243 208/var(--tw-ring-opacity))}.hover\:ring-emerald-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(110 231 183/var(--tw-ring-opacity))}.hover\:ring-emerald-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(52 211 153/var(--tw-ring-opacity))}.hover\:ring-emerald-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(236 253 245/var(--tw-ring-opacity))}.hover\:ring-emerald-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(16 185 129/var(--tw-ring-opacity))}.hover\:ring-emerald-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(5 150 105/var(--tw-ring-opacity))}.hover\:ring-emerald-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(4 120 87/var(--tw-ring-opacity))}.hover\:ring-emerald-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(6 95 70/var(--tw-ring-opacity))}.hover\:ring-emerald-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(6 78 59/var(--tw-ring-opacity))}.hover\:ring-fuchsia-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(250 232 255/var(--tw-ring-opacity))}.hover\:ring-fuchsia-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(245 208 254/var(--tw-ring-opacity))}.hover\:ring-fuchsia-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(240 171 252/var(--tw-ring-opacity))}.hover\:ring-fuchsia-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(232 121 249/var(--tw-ring-opacity))}.hover\:ring-fuchsia-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(253 244 255/var(--tw-ring-opacity))}.hover\:ring-fuchsia-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(217 70 239/var(--tw-ring-opacity))}.hover\:ring-fuchsia-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(192 38 211/var(--tw-ring-opacity))}.hover\:ring-fuchsia-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(162 28 175/var(--tw-ring-opacity))}.hover\:ring-fuchsia-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(134 25 143/var(--tw-ring-opacity))}.hover\:ring-fuchsia-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(112 26 117/var(--tw-ring-opacity))}.hover\:ring-gray-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(243 244 246/var(--tw-ring-opacity))}.hover\:ring-gray-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(229 231 235/var(--tw-ring-opacity))}.hover\:ring-gray-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity))}.hover\:ring-gray-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(156 163 175/var(--tw-ring-opacity))}.hover\:ring-gray-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(249 250 251/var(--tw-ring-opacity))}.hover\:ring-gray-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(107 114 128/var(--tw-ring-opacity))}.hover\:ring-gray-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(75 85 99/var(--tw-ring-opacity))}.hover\:ring-gray-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(55 65 81/var(--tw-ring-opacity))}.hover\:ring-gray-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(31 41 55/var(--tw-ring-opacity))}.hover\:ring-gray-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(17 24 39/var(--tw-ring-opacity))}.hover\:ring-green-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(220 252 231/var(--tw-ring-opacity))}.hover\:ring-green-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(187 247 208/var(--tw-ring-opacity))}.hover\:ring-green-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(134 239 172/var(--tw-ring-opacity))}.hover\:ring-green-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(74 222 128/var(--tw-ring-opacity))}.hover\:ring-green-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(240 253 244/var(--tw-ring-opacity))}.hover\:ring-green-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(34 197 94/var(--tw-ring-opacity))}.hover\:ring-green-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(22 163 74/var(--tw-ring-opacity))}.hover\:ring-green-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(21 128 61/var(--tw-ring-opacity))}.hover\:ring-green-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(22 101 52/var(--tw-ring-opacity))}.hover\:ring-green-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(20 83 45/var(--tw-ring-opacity))}.hover\:ring-indigo-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(224 231 255/var(--tw-ring-opacity))}.hover\:ring-indigo-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(199 210 254/var(--tw-ring-opacity))}.hover\:ring-indigo-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(165 180 252/var(--tw-ring-opacity))}.hover\:ring-indigo-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(129 140 248/var(--tw-ring-opacity))}.hover\:ring-indigo-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(238 242 255/var(--tw-ring-opacity))}.hover\:ring-indigo-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity))}.hover\:ring-indigo-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(79 70 229/var(--tw-ring-opacity))}.hover\:ring-indigo-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(67 56 202/var(--tw-ring-opacity))}.hover\:ring-indigo-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(55 48 163/var(--tw-ring-opacity))}.hover\:ring-indigo-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(49 46 129/var(--tw-ring-opacity))}.hover\:ring-lime-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(236 252 203/var(--tw-ring-opacity))}.hover\:ring-lime-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(217 249 157/var(--tw-ring-opacity))}.hover\:ring-lime-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(190 242 100/var(--tw-ring-opacity))}.hover\:ring-lime-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(163 230 53/var(--tw-ring-opacity))}.hover\:ring-lime-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(247 254 231/var(--tw-ring-opacity))}.hover\:ring-lime-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(132 204 22/var(--tw-ring-opacity))}.hover\:ring-lime-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(101 163 13/var(--tw-ring-opacity))}.hover\:ring-lime-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(77 124 15/var(--tw-ring-opacity))}.hover\:ring-lime-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(63 98 18/var(--tw-ring-opacity))}.hover\:ring-lime-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(54 83 20/var(--tw-ring-opacity))}.hover\:ring-neutral-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(245 245 245/var(--tw-ring-opacity))}.hover\:ring-neutral-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(229 229 229/var(--tw-ring-opacity))}.hover\:ring-neutral-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(212 212 212/var(--tw-ring-opacity))}.hover\:ring-neutral-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(163 163 163/var(--tw-ring-opacity))}.hover\:ring-neutral-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(250 250 250/var(--tw-ring-opacity))}.hover\:ring-neutral-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(115 115 115/var(--tw-ring-opacity))}.hover\:ring-neutral-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(82 82 82/var(--tw-ring-opacity))}.hover\:ring-neutral-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(64 64 64/var(--tw-ring-opacity))}.hover\:ring-neutral-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(38 38 38/var(--tw-ring-opacity))}.hover\:ring-neutral-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(23 23 23/var(--tw-ring-opacity))}.hover\:ring-orange-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(255 237 213/var(--tw-ring-opacity))}.hover\:ring-orange-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(254 215 170/var(--tw-ring-opacity))}.hover\:ring-orange-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(253 186 116/var(--tw-ring-opacity))}.hover\:ring-orange-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(251 146 60/var(--tw-ring-opacity))}.hover\:ring-orange-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(255 247 237/var(--tw-ring-opacity))}.hover\:ring-orange-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(249 115 22/var(--tw-ring-opacity))}.hover\:ring-orange-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(234 88 12/var(--tw-ring-opacity))}.hover\:ring-orange-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(194 65 12/var(--tw-ring-opacity))}.hover\:ring-orange-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(154 52 18/var(--tw-ring-opacity))}.hover\:ring-orange-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(124 45 18/var(--tw-ring-opacity))}.hover\:ring-pink-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(252 231 243/var(--tw-ring-opacity))}.hover\:ring-pink-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(251 207 232/var(--tw-ring-opacity))}.hover\:ring-pink-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(249 168 212/var(--tw-ring-opacity))}.hover\:ring-pink-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(244 114 182/var(--tw-ring-opacity))}.hover\:ring-pink-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(253 242 248/var(--tw-ring-opacity))}.hover\:ring-pink-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(236 72 153/var(--tw-ring-opacity))}.hover\:ring-pink-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(219 39 119/var(--tw-ring-opacity))}.hover\:ring-pink-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(190 24 93/var(--tw-ring-opacity))}.hover\:ring-pink-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(131 24 67/var(--tw-ring-opacity))}.hover\:ring-purple-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(243 232 255/var(--tw-ring-opacity))}.hover\:ring-purple-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(233 213 255/var(--tw-ring-opacity))}.hover\:ring-purple-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(216 180 254/var(--tw-ring-opacity))}.hover\:ring-purple-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(192 132 252/var(--tw-ring-opacity))}.hover\:ring-purple-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(250 245 255/var(--tw-ring-opacity))}.hover\:ring-purple-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(168 85 247/var(--tw-ring-opacity))}.hover\:ring-purple-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(147 51 234/var(--tw-ring-opacity))}.hover\:ring-purple-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(126 34 206/var(--tw-ring-opacity))}.hover\:ring-purple-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(107 33 168/var(--tw-ring-opacity))}.hover\:ring-purple-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(88 28 135/var(--tw-ring-opacity))}.hover\:ring-red-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(254 226 226/var(--tw-ring-opacity))}.hover\:ring-red-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(254 202 202/var(--tw-ring-opacity))}.hover\:ring-red-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(252 165 165/var(--tw-ring-opacity))}.hover\:ring-red-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(248 113 113/var(--tw-ring-opacity))}.hover\:ring-red-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(254 242 242/var(--tw-ring-opacity))}.hover\:ring-red-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(239 68 68/var(--tw-ring-opacity))}.hover\:ring-red-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(220 38 38/var(--tw-ring-opacity))}.hover\:ring-red-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(185 28 28/var(--tw-ring-opacity))}.hover\:ring-red-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(153 27 27/var(--tw-ring-opacity))}.hover\:ring-red-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(127 29 29/var(--tw-ring-opacity))}.hover\:ring-rose-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(255 228 230/var(--tw-ring-opacity))}.hover\:ring-rose-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(254 205 211/var(--tw-ring-opacity))}.hover\:ring-rose-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(253 164 175/var(--tw-ring-opacity))}.hover\:ring-rose-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(251 113 133/var(--tw-ring-opacity))}.hover\:ring-rose-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(255 241 242/var(--tw-ring-opacity))}.hover\:ring-rose-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(244 63 94/var(--tw-ring-opacity))}.hover\:ring-rose-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(225 29 72/var(--tw-ring-opacity))}.hover\:ring-rose-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(190 18 60/var(--tw-ring-opacity))}.hover\:ring-rose-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(159 18 57/var(--tw-ring-opacity))}.hover\:ring-rose-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(136 19 55/var(--tw-ring-opacity))}.hover\:ring-sky-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(224 242 254/var(--tw-ring-opacity))}.hover\:ring-sky-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(186 230 253/var(--tw-ring-opacity))}.hover\:ring-sky-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(125 211 252/var(--tw-ring-opacity))}.hover\:ring-sky-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(56 189 248/var(--tw-ring-opacity))}.hover\:ring-sky-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(240 249 255/var(--tw-ring-opacity))}.hover\:ring-sky-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(14 165 233/var(--tw-ring-opacity))}.hover\:ring-sky-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(2 132 199/var(--tw-ring-opacity))}.hover\:ring-sky-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(3 105 161/var(--tw-ring-opacity))}.hover\:ring-sky-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(7 89 133/var(--tw-ring-opacity))}.hover\:ring-sky-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(12 74 110/var(--tw-ring-opacity))}.hover\:ring-slate-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(241 245 249/var(--tw-ring-opacity))}.hover\:ring-slate-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(226 232 240/var(--tw-ring-opacity))}.hover\:ring-slate-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(203 213 225/var(--tw-ring-opacity))}.hover\:ring-slate-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(148 163 184/var(--tw-ring-opacity))}.hover\:ring-slate-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(248 250 252/var(--tw-ring-opacity))}.hover\:ring-slate-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(100 116 139/var(--tw-ring-opacity))}.hover\:ring-slate-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(71 85 105/var(--tw-ring-opacity))}.hover\:ring-slate-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(51 65 85/var(--tw-ring-opacity))}.hover\:ring-slate-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(30 41 59/var(--tw-ring-opacity))}.hover\:ring-slate-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(15 23 42/var(--tw-ring-opacity))}.hover\:ring-stone-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(245 245 244/var(--tw-ring-opacity))}.hover\:ring-stone-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(231 229 228/var(--tw-ring-opacity))}.hover\:ring-stone-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(214 211 209/var(--tw-ring-opacity))}.hover\:ring-stone-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(168 162 158/var(--tw-ring-opacity))}.hover\:ring-stone-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(250 250 249/var(--tw-ring-opacity))}.hover\:ring-stone-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(120 113 108/var(--tw-ring-opacity))}.hover\:ring-stone-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(87 83 78/var(--tw-ring-opacity))}.hover\:ring-stone-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(68 64 60/var(--tw-ring-opacity))}.hover\:ring-stone-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(41 37 36/var(--tw-ring-opacity))}.hover\:ring-stone-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(28 25 23/var(--tw-ring-opacity))}.hover\:ring-teal-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(204 251 241/var(--tw-ring-opacity))}.hover\:ring-teal-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(153 246 228/var(--tw-ring-opacity))}.hover\:ring-teal-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(94 234 212/var(--tw-ring-opacity))}.hover\:ring-teal-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(45 212 191/var(--tw-ring-opacity))}.hover\:ring-teal-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(240 253 250/var(--tw-ring-opacity))}.hover\:ring-teal-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(20 184 166/var(--tw-ring-opacity))}.hover\:ring-teal-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(13 148 136/var(--tw-ring-opacity))}.hover\:ring-teal-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(15 118 110/var(--tw-ring-opacity))}.hover\:ring-teal-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(17 94 89/var(--tw-ring-opacity))}.hover\:ring-teal-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(19 78 74/var(--tw-ring-opacity))}.hover\:ring-transparent:hover{--tw-ring-color:transparent}.hover\:ring-violet-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(237 233 254/var(--tw-ring-opacity))}.hover\:ring-violet-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(221 214 254/var(--tw-ring-opacity))}.hover\:ring-violet-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(196 181 253/var(--tw-ring-opacity))}.hover\:ring-violet-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(167 139 250/var(--tw-ring-opacity))}.hover\:ring-violet-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(245 243 255/var(--tw-ring-opacity))}.hover\:ring-violet-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(139 92 246/var(--tw-ring-opacity))}.hover\:ring-violet-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(124 58 237/var(--tw-ring-opacity))}.hover\:ring-violet-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(109 40 217/var(--tw-ring-opacity))}.hover\:ring-violet-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(91 33 182/var(--tw-ring-opacity))}.hover\:ring-violet-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(76 29 149/var(--tw-ring-opacity))}.hover\:ring-white:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(255 255 255/var(--tw-ring-opacity))}.hover\:ring-yellow-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(254 249 195/var(--tw-ring-opacity))}.hover\:ring-yellow-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(254 240 138/var(--tw-ring-opacity))}.hover\:ring-yellow-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(253 224 71/var(--tw-ring-opacity))}.hover\:ring-yellow-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(250 204 21/var(--tw-ring-opacity))}.hover\:ring-yellow-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(254 252 232/var(--tw-ring-opacity))}.hover\:ring-yellow-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(234 179 8/var(--tw-ring-opacity))}.hover\:ring-yellow-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(202 138 4/var(--tw-ring-opacity))}.hover\:ring-yellow-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(161 98 7/var(--tw-ring-opacity))}.hover\:ring-yellow-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(133 77 14/var(--tw-ring-opacity))}.hover\:ring-yellow-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(113 63 18/var(--tw-ring-opacity))}.hover\:ring-zinc-100:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(244 244 245/var(--tw-ring-opacity))}.hover\:ring-zinc-200:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(228 228 231/var(--tw-ring-opacity))}.hover\:ring-zinc-300:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(212 212 216/var(--tw-ring-opacity))}.hover\:ring-zinc-400:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(161 161 170/var(--tw-ring-opacity))}.hover\:ring-zinc-50:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(250 250 250/var(--tw-ring-opacity))}.hover\:ring-zinc-500:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(113 113 122/var(--tw-ring-opacity))}.hover\:ring-zinc-600:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(82 82 91/var(--tw-ring-opacity))}.hover\:ring-zinc-700:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(63 63 70/var(--tw-ring-opacity))}.hover\:ring-zinc-800:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(39 39 42/var(--tw-ring-opacity))}.hover\:ring-zinc-900:hover{--tw-ring-opacity:1;--tw-ring-color:rgb(24 24 27/var(--tw-ring-opacity))}.hover\:brightness-90:hover{--tw-brightness:brightness(.9)}.hover\:brightness-90:hover,.hover\:brightness-95:hover{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.hover\:brightness-95:hover{--tw-brightness:brightness(.95)}.hover\:grayscale-0:hover{--tw-grayscale:grayscale(0);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.hover\:before\:opacity-100:hover:before{content:var(--tw-content);opacity:1}.after\:hover\:block:hover:after{content:var(--tw-content);display:block}.hover\:after\:bg-subtle:hover:after{content:var(--tw-content);background-color:var(--cal-bg-subtle)}.checked\:hover\:border-error:hover:checked{border-color:var(--cal-border-error,#aa2e26)}.checked\:hover\:bg-brand-default:hover:checked{background-color:var(--cal-brand,#111827)}.checked\:hover\:bg-darkerror:hover:checked{background-color:var(--cal-bg-dark-error)}.checked\:hover\:bg-gray-600:hover:checked{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.hover\:checked\:bg-gray-300:checked:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.disabled\:hover\:cursor-not-allowed:hover:disabled{cursor:not-allowed}.disabled\:hover\:border-default:hover:disabled{border-color:var(--cal-border,#d1d5db)}.disabled\:hover\:border-inherit:hover:disabled{border-color:inherit}.disabled\:hover\:border-red-200:hover:disabled{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity))}.disabled\:hover\:border-subtle:hover:disabled{border-color:var(--cal-border-subtle,#e5e7eb)}.disabled\:hover\:bg-brand-subtle:hover:disabled{background-color:var(--cal-brand-subtle,#9ca3af)}.disabled\:hover\:bg-default:hover:disabled{background-color:var(--cal-bg,#fff)}.disabled\:hover\:bg-transparent:hover:disabled{background-color:transparent}.disabled\:hover\:bg-opacity-30:hover:disabled{--tw-bg-opacity:0.3}.disabled\:hover\:text-muted:hover:disabled{color:var(--cal-text-muted,#9ca3af)}.disabled\:hover\:opacity-40:hover:disabled{opacity:.4}.disabled\:hover\:shadow-none:hover:disabled{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}html.todesktop .todesktop\:hover\:\!bg-transparent:hover{background-color:transparent!important}html.todesktop .todesktop\:hover\:bg-transparent:hover{background-color:transparent}.active\:scale-95:active{--tw-scale-x:.95;--tw-scale-y:.95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:bg-default:active{background-color:var(--cal-bg,#fff)}.radix-state-open\:rotate-45[data-state=open]{--tw-rotate:45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.radix-state-open\:rounded-r-md[data-state=open]{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.radix-state-open\:border-emphasis[data-state=open]{border-color:var(--cal-border-emphasis,#9ca3af)}.radix-state-open\:outline-none[data-state=open]{outline:2px solid transparent;outline-offset:2px}.radix-state-open\:ring-0[data-state=open]{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.radix-state-open\:ring-0[data-state=open],.radix-state-open\:ring-2[data-state=open]{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.radix-state-open\:ring-2[data-state=open]{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.radix-state-open\:ring-emphasis[data-state=open]{--tw-ring-color:var(--cal-bg-emphasis)}:is([dir=ltr] .ltr\:right-0){right:0}:is([dir=ltr] .ltr\:right-2){right:.5rem}:is([dir=ltr] .ltr\:right-4){right:1rem}:is([dir=ltr] .ltr\:-ml-1){margin-left:-.25rem}:is([dir=ltr] .ltr\:-mr-1){margin-right:-.25rem}:is([dir=ltr] .ltr\:ml-1){margin-left:.25rem}:is([dir=ltr] .ltr\:ml-2){margin-left:.5rem}:is([dir=ltr] .ltr\:ml-3){margin-left:.75rem}:is([dir=ltr] .ltr\:mr-1){margin-right:.25rem}:is([dir=ltr] .ltr\:mr-2){margin-right:.5rem}:is([dir=ltr] .ltr\:mr-3){margin-right:.75rem}:is([dir=ltr] .ltr\:mr-4){margin-right:1rem}:is([dir=ltr] .ltr\:mr-5){margin-right:1.25rem}:is([dir=ltr] .ltr\:mr-\[10px\]){margin-right:10px}:is([dir=ltr] .ltr\:translate-x-\[4px\]){--tw-translate-x:4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=ltr] .ltr\:rounded-l-md){border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}:is([dir=ltr] .ltr\:rounded-r-md){border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}:is([dir=ltr] .ltr\:border-l){border-left-width:1px}:is([dir=ltr] .ltr\:border-r-0){border-right-width:0}:is([dir=ltr] .ltr\:pl-4){padding-left:1rem}:is([dir=ltr] .ltr\:pl-6){padding-left:1.5rem}:is([dir=ltr] .ltr\:pr-10){padding-right:2.5rem}:is([dir=ltr] .ltr\:pr-4){padding-right:1rem}:is([dir=ltr] .ltr\:text-left){text-align:left}:is([dir=ltr] .ltr\:last\:mr-0:last-child){margin-right:0}:is([dir=ltr] .ltr\:radix-state-open\:rounded-r-md[data-state=open]){border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}:is([dir=rtl] .rtl\:left-0){left:0}:is([dir=rtl] .rtl\:left-2){left:.5rem}:is([dir=rtl] .rtl\:left-4){left:1rem}:is([dir=rtl] .rtl\:-ml-1){margin-left:-.25rem}:is([dir=rtl] .rtl\:-mr-1){margin-right:-.25rem}:is([dir=rtl] .rtl\:-mr-14){margin-right:-3.5rem}:is([dir=rtl] .rtl\:-mr-2){margin-right:-.5rem}:is([dir=rtl] .rtl\:ml-1){margin-left:.25rem}:is([dir=rtl] .rtl\:ml-2){margin-left:.5rem}:is([dir=rtl] .rtl\:ml-3){margin-left:.75rem}:is([dir=rtl] .rtl\:ml-4){margin-left:1rem}:is([dir=rtl] .rtl\:ml-5){margin-left:1.25rem}:is([dir=rtl] .rtl\:ml-\[10px\]){margin-left:10px}:is([dir=rtl] .rtl\:mr-1){margin-right:.25rem}:is([dir=rtl] .rtl\:mr-2){margin-right:.5rem}:is([dir=rtl] .rtl\:mr-3){margin-right:.75rem}:is([dir=rtl] .rtl\:mr-4){margin-right:1rem}:is([dir=rtl] .rtl\:mr-auto){margin-right:auto}:is([dir=rtl] .rtl\:flex-grow){flex-grow:1}:is([dir=rtl] .rtl\:-translate-x-\[4px\]){--tw-translate-x:-4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .rtl\:rotate-180){--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .rtl\:flex-row-reverse){flex-direction:row-reverse}:is([dir=rtl] .rtl\:justify-end){justify-content:flex-end}:is([dir=rtl] .rtl\:space-x-reverse)>:not([hidden])~:not([hidden]){--tw-space-x-reverse:1}:is([dir=rtl] .rtl\:rounded-l-md){border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}:is([dir=rtl] .rtl\:rounded-r-md){border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}:is([dir=rtl] .rtl\:border-l-0){border-left-width:0}:is([dir=rtl] .rtl\:border-r){border-right-width:1px}:is([dir=rtl] .rtl\:border-default){border-color:var(--cal-border,#d1d5db)}:is([dir=rtl] .rtl\:border-r-default){border-right-color:var(--cal-border,#d1d5db)}:is([dir=rtl] .rtl\:pl-10){padding-left:2.5rem}:is([dir=rtl] .rtl\:pl-4){padding-left:1rem}:is([dir=rtl] .rtl\:pr-4){padding-right:1rem}:is([dir=rtl] .rtl\:pr-6){padding-right:1.5rem}:is([dir=rtl] .rtl\:text-right){text-align:right}:is([dir=rtl] .rtl\:last\:ml-0:last-child){margin-left:0}:is([dir=rtl] .rtl\:radix-state-open\:rounded-l-md[data-state=open]){border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}:is(.dark .dark\:block){display:block}:is(.dark .dark\:hidden){display:none}:is(.dark .dark\:divide-darkgray-100)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(28 28 28/var(--tw-divide-opacity))}:is(.dark .dark\:border){border-width:1px}:is(.dark .dark\:border-darkgray-200){--tw-border-opacity:1;border-color:rgb(43 43 43/var(--tw-border-opacity))}:is(.dark .dark\:border-default){border-color:var(--cal-border,#d1d5db)}:is(.dark .dark\:border-emphasis){border-color:var(--cal-border-emphasis,#9ca3af)}:is(.dark .dark\:border-gray-900){--tw-border-opacity:1;border-color:rgb(17 24 39/var(--tw-border-opacity))}:is(.dark .dark\:border-transparent){border-color:transparent}:is(.dark .dark\:bg-black){--tw-bg-opacity:1;background-color:rgb(17 17 17/var(--tw-bg-opacity))}:is(.dark .dark\:bg-blue-900){--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity))}:is(.dark .dark\:bg-blue-950){--tw-bg-opacity:1;background-color:rgb(23 37 84/var(--tw-bg-opacity))}:is(.dark .dark\:bg-brand-emphasis){background-color:var(--cal-brand-emphasis,#101010)}:is(.dark .dark\:bg-darkgray-100){--tw-bg-opacity:1;background-color:rgb(28 28 28/var(--tw-bg-opacity))}:is(.dark .dark\:bg-darkgray-200){--tw-bg-opacity:1;background-color:rgb(43 43 43/var(--tw-bg-opacity))}:is(.dark .dark\:bg-darkgray-400){--tw-bg-opacity:1;background-color:rgb(87 87 87/var(--tw-bg-opacity))}:is(.dark .dark\:bg-darkgray-50){--tw-bg-opacity:1;background-color:rgb(16 16 16/var(--tw-bg-opacity))}:is(.dark .dark\:bg-darkgray-900){--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}:is(.dark .dark\:bg-default){background-color:var(--cal-bg,#fff)}:is(.dark .dark\:bg-gray-600){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}:is(.dark .dark\:bg-inherit){background-color:inherit}:is(.dark .dark\:bg-muted){background-color:var(--cal-bg-muted)}:is(.dark .dark\:bg-orange-900){--tw-bg-opacity:1;background-color:rgb(124 45 18/var(--tw-bg-opacity))}:is(.dark .dark\:bg-red-900){--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity))}:is(.dark .dark\:bg-transparent){background-color:transparent}:is(.dark .dark\:bg-white){--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}:is(.dark .dark\:bg-white\/5){background-color:hsla(0,0%,100%,.05)}:is(.dark .dark\:bg-opacity-5){--tw-bg-opacity:0.05}:is(.dark .dark\:bg-opacity-70){--tw-bg-opacity:0.7}:is(.dark .dark\:bg-gradient-to-tr){background-image:linear-gradient(to top right,var(--tw-gradient-stops))}:is(.dark .dark\:bg-none){background-image:none}:is(.dark .dark\:from-subtle){--tw-gradient-from:var(--cal-bg-subtle) var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}:is(.dark .dark\:to-muted){--tw-gradient-to:var(--cal-bg-muted) var(--tw-gradient-to-position)}:is(.dark .dark\:\!text-darkgray-900){--tw-text-opacity:1!important;color:rgb(243 244 246/var(--tw-text-opacity))!important}:is(.dark .dark\:text-black){--tw-text-opacity:1;color:rgb(17 17 17/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-200){--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-300){--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-400){--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}:is(.dark .dark\:text-darkgray-50){--tw-text-opacity:1;color:rgb(16 16 16/var(--tw-text-opacity))}:is(.dark .dark\:text-darkgray-800){--tw-text-opacity:1;color:rgb(232 232 232/var(--tw-text-opacity))}:is(.dark .dark\:text-darkgray-900){--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}:is(.dark .dark\:text-default){color:var(--cal-text,#374151)}:is(.dark .dark\:text-emphasis){color:var(--cal-text-emphasis,#111827)}:is(.dark .dark\:text-gray-300){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}:is(.dark .dark\:text-green-400){--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity))}:is(.dark .dark\:text-inverted){color:var(--cal-text-inverted,#fff)}:is(.dark .dark\:text-muted){color:var(--cal-text-muted,#9ca3af)}:is(.dark .dark\:text-orange-200){--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity))}:is(.dark .dark\:text-red-100){--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity))}:is(.dark .dark\:text-red-200){--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity))}:is(.dark .dark\:text-red-700\/30){color:rgba(185,28,28,.3)}:is(.dark .dark\:text-subtle){color:var(--cal-text-subtle,#6b7280)}:is(.dark .dark\:text-white){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .dark\:opacity-100){opacity:1}:is(.dark .dark\:invert){--tw-invert:invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}:is(.dark .dark\:invert-\[\.65\]){--tw-invert:invert(.65);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}:is(.dark .dark\:\[--cal-brand-emphasis\:\#e1e1e1\]){--cal-brand-emphasis:#e1e1e1}:is(.dark .dark\:\[--cal-brand-text\:\#000000\]){--cal-brand-text:#000}:is(.dark .dark\:\[--cal-brand-text\:black\]){--cal-brand-text:#000}:is(.dark .dark\:\[--cal-brand\:\#FFFFFF\]){--cal-brand:#fff}:is(.dark .dark\:\[--cal-brand\:white\]){--cal-brand:#fff}:is(.dark .dark\:\[--disabled-gradient-background\:\#262626\]){--disabled-gradient-background:#262626}:is(.dark .dark\:\[--disabled-gradient-foreground\:\#393939\]){--disabled-gradient-foreground:#393939}:is(.dark .dark\:\[color-scheme\:dark\]){color-scheme:dark}:is(.dark .dark\:\[filter\:invert\(0\.65\)_brightness\(0\.9\)\]){filter:invert(.65) brightness(.9)}:is(.dark .dark\:selection\:bg-green-500 *)::-moz-selection{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}:is(.dark .dark\:selection\:bg-green-500 *)::selection{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}:is(.dark .dark\:selection\:bg-green-500)::-moz-selection{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}:is(.dark .dark\:selection\:bg-green-500)::selection{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}:is(.dark .dark\:placeholder\:text-darkgray-600)::-moz-placeholder{--tw-text-opacity:1;color:rgb(165 165 165/var(--tw-text-opacity))}:is(.dark .dark\:placeholder\:text-darkgray-600)::placeholder{--tw-text-opacity:1;color:rgb(165 165 165/var(--tw-text-opacity))}:is(.dark .dark\:placeholder\:text-muted)::-moz-placeholder{color:var(--cal-text-muted,#9ca3af)}:is(.dark .dark\:placeholder\:text-muted)::placeholder{color:var(--cal-text-muted,#9ca3af)}:is(.dark .dark\:before\:bg-muted):before{content:var(--tw-content);background-color:var(--cal-bg-muted)}:is(.dark .dark\:after\:bg-brand-accent):after{content:var(--tw-content);background-color:var(--cal-brand-accent,#fff)}:is(.dark .dark\:after\:bg-inverted):after{content:var(--tw-content);background-color:var(--cal-bg-inverted)}:is(.dark .dark\:checked\:bg-brand-default:checked){background-color:var(--cal-brand,#111827)}:is(.dark .dark\:checked\:after\:bg-brand-accent:checked):after{content:var(--tw-content);background-color:var(--cal-brand-accent,#fff)}:is(.dark .dark\:focus\:border-emphasis:focus){border-color:var(--cal-border-emphasis,#9ca3af)}:is(.dark .disabled\:dark\:text-gray-500):disabled{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}:is(.dark .disabled\:dark\:text-subtle):disabled{color:var(--cal-text-subtle,#6b7280)}:is(.dark .dark\:hover\:bg-darkgray-200:hover){--tw-bg-opacity:1;background-color:rgb(43 43 43/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-emphasis:hover){background-color:var(--cal-bg-emphasis)}:is(.dark .dark\:hover\:bg-subtle:hover){background-color:var(--cal-bg-subtle)}:is(.dark .dark\:hover\:bg-transparent:hover){background-color:transparent}:is(.dark .dark\:hover\:text-red-100:hover){--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity))}:is(.dark .dark\:checked\:hover\:bg-brand-default:hover:checked){background-color:var(--cal-brand,#111827)}:is(.dark .dark\:checked\:hover\:text-\[--cal-brand\]:hover:checked){color:var(--cal-brand)}@media print{.print\:hidden{display:none}.print\:h-full{height:100%}.print\:w-full{width:100%}}@media(min-width:640px){.sm\:pointer-events-auto{pointer-events:auto}.sm\:absolute{position:absolute}.sm\:relative{position:relative}.sm\:order-3{order:3}.sm\:col-span-1{grid-column:span 1/span 1}.sm\:col-span-10{grid-column:span 10/span 10}.sm\:col-span-11{grid-column:span 11/span 11}.sm\:col-span-12{grid-column:span 12/span 12}.sm\:col-span-2{grid-column:span 2/span 2}.sm\:col-span-3{grid-column:span 3/span 3}.sm\:col-span-4{grid-column:span 4/span 4}.sm\:col-span-5{grid-column:span 5/span 5}.sm\:col-span-6{grid-column:span 6/span 6}.sm\:col-span-7{grid-column:span 7/span 7}.sm\:col-span-8{grid-column:span 8/span 8}.sm\:col-span-9{grid-column:span 9/span 9}.sm\:-mx-4{margin-left:-1rem;margin-right:-1rem}.sm\:mx-0{margin-left:0;margin-right:0}.sm\:mx-auto{margin-left:auto;margin-right:auto}.sm\:my-0{margin-top:0;margin-bottom:0}.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:-mt-4{margin-top:-1rem}.sm\:mb-0{margin-bottom:0}.sm\:mb-4{margin-bottom:1rem}.sm\:mb-6{margin-bottom:1.5rem}.sm\:ml-0{margin-left:0}.sm\:ml-16{margin-left:4rem}.sm\:ml-2{margin-left:.5rem}.sm\:ml-4{margin-left:1rem}.sm\:ml-5{margin-left:1.25rem}.sm\:ml-auto{margin-left:auto}.sm\:mr-36{margin-right:9rem}.sm\:mr-4{margin-right:1rem}.sm\:mt-0{margin-top:0}.sm\:mt-4{margin-top:1rem}.sm\:mt-5{margin-top:1.25rem}.sm\:mt-6{margin-top:1.5rem}.sm\:mt-8{margin-top:2rem}.sm\:block{display:block}.sm\:inline-block{display:inline-block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:inline-flex{display:inline-flex}.sm\:table-cell{display:table-cell}.sm\:grid{display:grid}.sm\:hidden{display:none}.sm\:h-10{height:2.5rem}.sm\:h-4{height:1rem}.sm\:h-screen{height:100vh}.sm\:min-h-screen{min-height:100vh}.sm\:w-1\/2{width:50%}.sm\:w-10{width:2.5rem}.sm\:w-16{width:4rem}.sm\:w-24{width:6rem}.sm\:w-28{width:7rem}.sm\:w-32{width:8rem}.sm\:w-36{width:9rem}.sm\:w-4{width:1rem}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:min-w-80{min-width:20rem}.sm\:min-w-\[12rem\]{min-width:12rem}.sm\:max-w-32{max-width:8rem}.sm\:max-w-4xl{max-width:56rem}.sm\:max-w-56{max-width:14rem}.sm\:max-w-72{max-width:18rem}.sm\:max-w-80{max-width:20rem}.sm\:max-w-\[35rem\]{max-width:35rem}.sm\:max-w-\[48rem\]{max-width:48rem}.sm\:max-w-\[500px\]{max-width:500px}.sm\:max-w-\[520px\]{max-width:520px}.sm\:max-w-\[600px\]{max-width:600px}.sm\:max-w-\[610px\]{max-width:610px}.sm\:max-w-\[650px\]{max-width:650px}.sm\:max-w-\[70rem\]{max-width:70rem}.sm\:max-w-\[80rem\]{max-width:80rem}.sm\:max-w-\[90rem\]{max-width:90rem}.sm\:max-w-lg{max-width:32rem}.sm\:max-w-md{max-width:28rem}.sm\:max-w-none{max-width:none}.sm\:max-w-sm{max-width:24rem}.sm\:max-w-xl{max-width:36rem}.sm\:flex-none{flex:none}.sm\:translate-y-\[12px\]{--tw-translate-y:12px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.sm\:grid-cols-11{grid-template-columns:repeat(11,minmax(0,1fr))}.sm\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.sm\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.sm\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.sm\:grid-cols-9{grid-template-columns:repeat(9,minmax(0,1fr))}.sm\:grid-cols-none{grid-template-columns:none}.sm\:flex-row{flex-direction:row}.sm\:flex-row-reverse{flex-direction:row-reverse}.sm\:flex-wrap{flex-wrap:wrap}.sm\:flex-nowrap{flex-wrap:nowrap}.sm\:items-start{align-items:flex-start}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-end{justify-content:flex-end}.sm\:justify-between{justify-content:space-between}.sm\:gap-0{gap:0}.sm\:gap-3{gap:.75rem}.sm\:gap-4{gap:1rem}.sm\:gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px * var(--tw-space-y-reverse))}.sm\:space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.sm\:divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.sm\:divide-subtle>:not([hidden])~:not([hidden]){border-color:var(--cal-border-subtle,#e5e7eb)}.sm\:self-center{align-self:center}.sm\:self-baseline{align-self:baseline}.sm\:overflow-hidden{overflow:hidden}.sm\:rounded-lg{border-radius:.5rem}.sm\:rounded-md{border-radius:.375rem}.sm\:rounded-l-none{border-top-left-radius:0;border-bottom-left-radius:0}.sm\:rounded-r-none{border-top-right-radius:0;border-bottom-right-radius:0}.sm\:rounded-bl-md{border-bottom-left-radius:.375rem}.sm\:rounded-bl-none{border-bottom-left-radius:0}.sm\:rounded-tl-md{border-top-left-radius:.375rem}.sm\:rounded-tl-none{border-top-left-radius:0}.sm\:border-booker-width{border-width:var(--cal-border-booker-width,1px)}.sm\:border-r{border-right-width:1px}.sm\:border-subtle{border-color:var(--cal-border-subtle,#e5e7eb)}.sm\:p-0{padding:0}.sm\:p-3{padding:.75rem}.sm\:p-4{padding:1rem}.sm\:p-6{padding:1.5rem}.sm\:p-8{padding:2rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-10{padding-left:2.5rem;padding-right:2.5rem}.sm\:px-14{padding-left:3.5rem;padding-right:3.5rem}.sm\:px-16{padding-left:4rem;padding-right:4rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-5{padding-left:1.25rem;padding-right:1.25rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}.sm\:py-5{padding-top:1.25rem;padding-bottom:1.25rem}.sm\:py-6{padding-top:1.5rem;padding-bottom:1.5rem}.sm\:py-8{padding-top:2rem;padding-bottom:2rem}.sm\:pb-5{padding-bottom:1.25rem}.sm\:pl-0{padding-left:0}.sm\:pl-3{padding-left:.75rem}.sm\:pr-5{padding-right:1.25rem}.sm\:pr-6{padding-right:1.5rem}.sm\:pr-8{padding-right:2rem}.sm\:pt-0{padding-top:0}.sm\:pt-24{padding-top:6rem}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}.sm\:align-middle{vertical-align:middle}.sm\:text-5xl{font-size:3rem;line-height:1}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}.sm\:text-xl{font-size:1.25rem;line-height:1.75rem}.sm\:text-xs{font-size:.75rem}.sm\:leading-4,.sm\:text-xs{line-height:1rem}.sm\:transition-\[width\]{transition-property:width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.sm\:duration-300{transition-duration:.3s;animation-duration:.3s}.sm\:hover\:bg-muted:hover{background-color:var(--cal-bg-muted)}.sm\:hover\:bg-subtle:hover{background-color:var(--cal-bg-subtle)}:is([dir=ltr] .sm\:ltr\:pl-0){padding-left:0}:is([dir=rtl] .sm\:rtl\:pr-0){padding-right:0}@media(prefers-reduced-motion:reduce){.sm\:motion-reduce\:transition-none{transition-property:none}}}@media(min-width:768px){.md\:visible{visibility:visible}.md\:relative{position:relative}.md\:sticky{position:sticky}.md\:-top-1{top:-.25rem}.md\:bottom-auto{bottom:auto}.md\:left-1\/2{left:50%}.md\:right-auto{right:auto}.md\:top-6{top:1.5rem}.md\:top-\[4\.25rem\]{top:4.25rem}.md\:z-10{z-index:10}.md\:z-auto{z-index:auto}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-span-10{grid-column:span 10/span 10}.md\:col-span-11{grid-column:span 11/span 11}.md\:col-span-12{grid-column:span 12/span 12}.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-3{grid-column:span 3/span 3}.md\:col-span-4{grid-column:span 4/span 4}.md\:col-span-5{grid-column:span 5/span 5}.md\:col-span-6{grid-column:span 6/span 6}.md\:col-span-7{grid-column:span 7/span 7}.md\:col-span-8{grid-column:span 8/span 8}.md\:col-span-9{grid-column:span 9/span 9}.md\:-mx-8{margin-left:-2rem;margin-right:-2rem}.md\:mx-0{margin-left:0;margin-right:0}.md\:mx-auto{margin-left:auto;margin-right:auto}.md\:my-24{margin-top:6rem;margin-bottom:6rem}.md\:-ml-8{margin-left:-2rem}.md\:-mr-8{margin-right:-2rem}.md\:mb-0{margin-bottom:0}.md\:mb-2{margin-bottom:.5rem}.md\:mb-6{margin-bottom:1.5rem}.md\:mb-8{margin-bottom:2rem}.md\:ml-3{margin-left:.75rem}.md\:ml-5{margin-left:1.25rem}.md\:mr-6{margin-right:1.5rem}.md\:mt-0{margin-top:0}.md\:mt-px{margin-top:1px}.md\:block{display:block}.md\:inline{display:inline}.md\:flex{display:flex}.md\:inline-flex{display:inline-flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:h-0{height:0}.md\:h-80{height:20rem}.md\:h-9{height:2.25rem}.md\:h-\[400px\]{height:400px}.md\:h-screen{height:100vh}.md\:max-h-min{max-height:-moz-min-content;max-height:min-content}.md\:w-1\/5{width:20%}.md\:w-64{width:16rem}.md\:w-\[520px\]{width:520px}.md\:w-\[560px\]{width:560px}.md\:w-\[700px\]{width:700px}.md\:w-\[var\(--booker-main-width\)\]{width:var(--booker-main-width)}.md\:w-\[var\(--booker-meta-width\)\]{width:var(--booker-meta-width)}.md\:w-\[var\(--booker-timeslots-width\)\]{width:var(--booker-timeslots-width)}.md\:w-auto{width:auto}.md\:w-full{width:100%}.md\:min-w-\[120px\]{min-width:120px}.md\:min-w-\[150px\]{min-width:150px}.md\:max-w-52{max-width:13rem}.md\:max-w-80{max-width:20rem}.md\:max-w-\[145px\]{max-width:145px}.md\:max-w-\[200px\]{max-width:200px}.md\:max-w-full{max-width:100%}.md\:max-w-max{max-width:-moz-max-content;max-width:max-content}.md\:max-w-sm{max-width:24rem}.md\:flex-none{flex:none}.md\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.md\:grid-cols-11{grid-template-columns:repeat(11,minmax(0,1fr))}.md\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.md\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.md\:grid-cols-9{grid-template-columns:repeat(9,minmax(0,1fr))}.md\:grid-cols-none{grid-template-columns:none}.md\:grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:items-start{align-items:flex-start}.md\:justify-between{justify-content:space-between}.md\:gap-4{gap:1rem}.md\:space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.25rem * var(--tw-space-x-reverse));margin-left:calc(1.25rem * calc(1 - var(--tw-space-x-reverse)))}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px * var(--tw-space-y-reverse))}.md\:overflow-visible{overflow:visible}.md\:rounded-md{border-radius:.375rem}.md\:rounded-none{border-radius:0}.md\:border{border-width:1px}.md\:border-0{border-width:0}.md\:border-booker-width{border-width:var(--cal-border-booker-width,1px)}.md\:border-l{border-left-width:1px}.md\:border-t{border-top-width:1px}.md\:border-none{border-style:none}.md\:border-subtle{border-color:var(--cal-border-subtle,#e5e7eb)}.md\:object-center{-o-object-position:center;object-position:center}.md\:p-0{padding:0}.md\:p-10{padding:2.5rem}.md\:p-4{padding:1rem}.md\:p-6{padding:1.5rem}.md\:p-8{padding:2rem}.md\:px-2{padding-left:.5rem;padding-right:.5rem}.md\:px-20{padding-left:5rem;padding-right:5rem}.md\:px-4{padding-left:1rem;padding-right:1rem}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:py-2{padding-top:.5rem;padding-bottom:.5rem}.md\:py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.md\:py-24{padding-top:6rem;padding-bottom:6rem}.md\:pl-0{padding-left:0}.md\:pl-10{padding-left:2.5rem}.md\:pr-1{padding-right:.25rem}.md\:pr-10{padding-right:2.5rem}.md\:pt-0{padding-top:0}.md\:pt-10{padding-top:2.5rem}.md\:pt-4{padding-top:1rem}.md\:radix-state-open\:rotate-0[data-state=open]{--tw-rotate:0deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=ltr] .md\:ltr\:right-0){right:0}:is([dir=ltr] .md\:ltr\:mx-auto){margin-left:auto;margin-right:auto}:is([dir=ltr] .ltr\:md\:border-l){border-left-width:1px}:is([dir=rtl] .md\:rtl\:left-0){left:0}}@media(min-width:1024px){.lg\:pointer-events-auto{pointer-events:auto}.lg\:sticky{position:sticky}.lg\:left-1\/2{left:50%}.lg\:left-3{left:.75rem}.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-span-10{grid-column:span 10/span 10}.lg\:col-span-11{grid-column:span 11/span 11}.lg\:col-span-12{grid-column:span 12/span 12}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-5{grid-column:span 5/span 5}.lg\:col-span-6{grid-column:span 6/span 6}.lg\:col-span-7{grid-column:span 7/span 7}.lg\:col-span-8{grid-column:span 8/span 8}.lg\:col-span-9{grid-column:span 9/span 9}.lg\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.lg\:mx-0{margin-left:0;margin-right:0}.lg\:mb-0{margin-bottom:0}.lg\:mb-4{margin-bottom:1rem}.lg\:mb-5{margin-bottom:1.25rem}.lg\:mb-7{margin-bottom:1.75rem}.lg\:mb-8{margin-bottom:2rem}.lg\:ml-0{margin-left:0}.lg\:ml-14{margin-left:3.5rem}.lg\:ml-8{margin-left:2rem}.lg\:mr-0{margin-right:0}.lg\:mt-0{margin-top:0}.lg\:mt-12{margin-top:3rem}.lg\:mt-4{margin-top:1rem}.lg\:block{display:block}.lg\:inline{display:inline}.lg\:flex{display:flex}.lg\:inline-flex{display:inline-flex}.lg\:grid{display:grid}.lg\:hidden{display:none}.lg\:w-1\/3{width:33.333333%}.lg\:w-1\/5{width:20%}.lg\:w-56{width:14rem}.lg\:w-\[calc\(100\%\+48px\)\]{width:calc(100% + 48px)}.lg\:w-\[var\(--booker-main-width\)\]{width:var(--booker-main-width)}.lg\:w-auto{width:auto}.lg\:min-w-72{min-width:18rem}.lg\:max-w-2xl{max-width:42rem}.lg\:max-w-4xl{max-width:56rem}.lg\:max-w-72{max-width:18rem}.lg\:max-w-\[50\%\]{max-width:50%}.lg\:max-w-full{max-width:100%}.lg\:translate-x-0{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.lg\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:grid-cols-11{grid-template-columns:repeat(11,minmax(0,1fr))}.lg\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.lg\:grid-cols-9{grid-template-columns:repeat(9,minmax(0,1fr))}.lg\:grid-cols-none{grid-template-columns:none}.lg\:flex-row{flex-direction:row}.lg\:flex-col{flex-direction:column}.lg\:items-center{align-items:center}.lg\:justify-center{justify-content:center}.lg\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.lg\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.lg\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px * var(--tw-space-y-reverse))}.lg\:rounded{border-radius:.25rem}.lg\:rounded-md{border-radius:.375rem}.lg\:border{border-width:1px}.lg\:bg-subtle{background-color:var(--cal-bg-subtle)}.lg\:p-20{padding:5rem}.lg\:px-0{padding-left:0;padding-right:0}.lg\:px-2{padding-left:.5rem;padding-right:.5rem}.lg\:px-3{padding-left:.75rem;padding-right:.75rem}.lg\:px-6{padding-left:1.5rem;padding-right:1.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-12{padding-top:3rem;padding-bottom:3rem}.lg\:py-\[6px\]{padding-top:6px;padding-bottom:6px}.lg\:pb-0{padding-bottom:0}.lg\:pb-5{padding-bottom:1.25rem}.lg\:pl-11{padding-left:2.75rem}.lg\:pl-12{padding-left:3rem}.lg\:pl-\[6px\]{padding-left:6px}.lg\:pt-0{padding-top:0}.lg\:pt-4{padding-top:1rem}.lg\:opacity-100{opacity:1}.lg\:\[--booker-meta-width\:280px\]{--booker-meta-width:280px}.lg\:\[--booker-meta-width\:340px\]{--booker-meta-width:340px}.lg\:\[--booker-meta-width\:424px\]{--booker-meta-width:424px}.lg\:\[--booker-timeslots-width\:280px\]{--booker-timeslots-width:280px}.lg\:\[--troubleshooter-meta-width\:430px\]{--troubleshooter-meta-width:430px}.lg\:hover\:bg-emphasis:hover{background-color:var(--cal-bg-emphasis)}.lg\:hover\:text-emphasis:hover{color:var(--cal-text-emphasis,#111827)}:is([dir=ltr] .lg\:ltr\:mr-2){margin-right:.5rem}:is([dir=rtl] .lg\:rtl\:ml-2){margin-left:.5rem}}@media(min-width:1280px){.xl\:mx-20{margin-left:5rem;margin-right:5rem}.xl\:mr-0{margin-right:0}.xl\:mt-0{margin-top:0}.xl\:block{display:block}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:w-full{width:100%}.xl\:max-w-80{max-width:20rem}.xl\:max-w-full{max-width:100%}.xl\:flex-row{flex-direction:row}.xl\:justify-between{justify-content:space-between}.xl\:space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.25rem * var(--tw-space-x-reverse));margin-left:calc(1.25rem * calc(1 - var(--tw-space-x-reverse)))}.xl\:space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.5rem * var(--tw-space-x-reverse));margin-left:calc(1.5rem * calc(1 - var(--tw-space-x-reverse)))}.xl\:pt-0{padding-top:0}}@media(min-width:1536px){.\32xl\:rounded-\[20px\]{border-radius:20px}.\32xl\:border{border-width:1px}.\32xl\:border-subtle{border-color:var(--cal-border-subtle,#e5e7eb)}.\32xl\:bg-default{background-color:var(--cal-bg,#fff)}.\32xl\:px-28{padding-left:7rem;padding-right:7rem}.\32xl\:py-6{padding-top:1.5rem;padding-bottom:1.5rem}}@media(display-mode:standalone){.pwa\:bottom-\[max\(7rem\2c _calc\(5rem_\+_env\(safe-area-inset-bottom\)\)\)\]{bottom:max(7rem,calc(5rem + env(safe-area-inset-bottom)))}.pwa\:-mx-2{margin-left:-.5rem;margin-right:-.5rem}.pwa\:pb-\[max\(0\.625rem\2c env\(safe-area-inset-bottom\)\)\]{padding-bottom:max(.625rem,env(safe-area-inset-bottom))}}.\[\&\:has\(\+_input\:hover\)\]\:border-emphasis:has(+input:hover){border-color:var(--cal-border-emphasis,#9ca3af)}.\[\&\:has\(\+_input\:hover\)\]\:border-r-default:has(+input:hover){border-right-color:var(--cal-border,#d1d5db)}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:0}.\[\&\:has\(input\:checked\)\]\:border-emphasis:has(input:checked){border-color:var(--cal-border-emphasis,#9ca3af)}.focus\:\[\&\:not\(\:disabled\)\]\:bg-subtle:not(:disabled):focus,.hover\:\[\&\:not\(\:disabled\)\]\:bg-subtle:not(:disabled):hover{background-color:var(--cal-bg-subtle)}.\[\&\>\*\:first-child\]\:mt-1>:first-child{margin-top:.25rem}.\[\&\>\*\:first-child\]\:rounded-l-md>:first-child{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.\[\&\>\*\:first-child\]\:rounded-t-md>:first-child{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.\[\&\>\*\:first-child\]\:border>:first-child{border-width:1px}.\[\&\>\*\:first-child\]\:border-l>:first-child{border-left-width:1px}.\[\&\>\*\:first-child\]\:border-default>:first-child{border-color:var(--cal-border,#d1d5db)}.\[\&\>\*\:first-child\]\:\!p-0>:first-child{padding:0!important}.hover\:\[\&\>\*\:first-child\]\:border-gray-400>:first-child:hover{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}:is([dir=ltr] .ltr\:\[\&\>\*\:first-child\]\:ml-0>:first-child){margin-left:0}:is([dir=ltr] .ltr\:\[\&\>\*\:first-child\]\:rounded-l-md>:first-child){border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}:is([dir=ltr] .ltr\:\[\&\>\*\:first-child\]\:border-l>:first-child){border-left-width:1px}:is([dir=rtl] .rtl\:\[\&\>\*\:first-child\]\:rounded-r-md>:first-child){border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}:is([dir=rtl] .rtl\:\[\&\>\*\:first-child\]\:border-r>:first-child){border-right-width:1px}.\[\&\>\*\:last-child\]\:mb-1>:last-child{margin-bottom:.25rem}.\[\&\>\*\:last-child\]\:rotate-180>:last-child{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&\>\*\:last-child\]\:rounded-b-md>:last-child{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.\[\&\>\*\:last-child\]\:rounded-b-xl>:last-child{border-bottom-right-radius:.75rem;border-bottom-left-radius:.75rem}.\[\&\>\*\:last-child\]\:rounded-r-md>:last-child{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.\[\&\>\*\:last-child\]\:border-b>:last-child{border-bottom-width:1px}.\[\&\>\*\:last-child\]\:transition-transform>:last-child{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}:is([dir=ltr] .ltr\:\[\&\>\*\:last-child\]\:rounded-r-md>:last-child){border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}:is([dir=rtl] .rtl\:\[\&\>\*\:last-child\]\:rounded-l-md>:last-child){border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.\[\&\>\*\:not\(\:first-child\)\]\:hidden>:not(:first-child){display:none}.\[\&\>\*\]\:flex-1>*{flex:1 1 0%}@keyframes pulse{50%{opacity:.5}}.\[\&\>\*\]\:animate-pulse>*{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.\[\&\>\*\]\:border>*{border-width:1px}.\[\&\>\*\]\:border-t-0>*{border-top-width:0}.\[\&\>\*\]\:border-subtle>*{border-color:var(--cal-border-subtle,#e5e7eb)}.\[\&\>\*\]\:bg-emphasis>*{background-color:var(--cal-bg-emphasis)}.\[\&\>\*\]\:px-4>*{padding-left:1rem;padding-right:1rem}.\[\&\>\*\]\:py-6>*{padding-top:1.5rem;padding-bottom:1.5rem}.\[\&\>\*\]\:text-emphasis>*{color:var(--cal-text-emphasis,#111827)}@media(min-width:640px){.\[\&\>\*\]\:sm\:px-6>*{padding-left:1.5rem;padding-right:1.5rem}}.\[\&\>a\]\:-ml-\[1px\]>a{margin-left:-1px}.hover\:\[\&\>a\]\:z-\[1\]>a:hover{z-index:1}.\[\&\>button\]\:-ml-\[1px\]>button{margin-left:-1px}.hover\:\[\&\>button\]\:z-\[1\]>button:hover{z-index:1}.\[\&\[aria-checked\=\'false\'\]\]\:hover\:text-emphasis:hover[aria-checked=false]{color:var(--cal-text-emphasis,#111827)}.\[\&\[aria-current\=\'page\'\]\]\:\!bg-transparent[aria-current=page]{background-color:transparent!important}.\[\&\[aria-current\=\'page\'\]\]\:bg-emphasis[aria-current=page]{background-color:var(--cal-bg-emphasis)}.\[\&\[aria-current\=\'page\'\]\]\:text-emphasis[aria-current=page]{color:var(--cal-text-emphasis,#111827)}.\[\&\[aria-current\=\'page\'\]\]\:text-inherit[aria-current=page]{color:inherit}html.todesktop .todesktop\:\[\&\[aria-current\=\'page\'\]\]\:bg-emphasis[aria-current=page]{background-color:var(--cal-bg-emphasis)}:is([dir=ltr] .ltr\:\[\&\[data-state\=\'checked\'\]\]\:translate-x-\[17px\][data-state=checked]){--tw-translate-x:17px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .rtl\:\[\&\[data-state\=\'checked\'\]\]\:-translate-x-\[17px\][data-state=checked]){--tw-translate-x:-17px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&_\.current-timezone\:before\]\:focus-within\:opacity-100:focus-within .current-timezone:before{opacity:1}.\[\&_\.current-timezone\:before\]\:hover\:opacity-100:hover .current-timezone:before{opacity:1}.\[\&_\.modalsticky\]\:sticky .modalsticky{position:sticky}.\[\&_\.modalsticky\]\:bottom-0 .modalsticky{bottom:0}.\[\&_\.modalsticky\]\:left-0 .modalsticky{left:0}.\[\&_\.modalsticky\]\:right-0 .modalsticky{right:0}.\[\&_\.modalsticky\]\:-mx-8 .modalsticky{margin-left:-2rem;margin-right:-2rem}.\[\&_\.modalsticky\]\:border-t .modalsticky{border-top-width:1px}.\[\&_\.modalsticky\]\:border-t-subtle .modalsticky{border-top-color:var(--cal-border-subtle,#e5e7eb)}.\[\&_\.modalsticky\]\:bg-default .modalsticky{background-color:var(--cal-bg,#fff)}.\[\&_\.modalsticky\]\:px-8 .modalsticky{padding-left:2rem;padding-right:2rem}.\[\&_\.modalsticky\]\:py-4 .modalsticky{padding-top:1rem;padding-bottom:1rem}.\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading]{padding-top:.375rem;padding-bottom:.375rem}.\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading]{font-size:.75rem;line-height:1rem}.\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading]{font-weight:500}.\[\&_\[cmdk-group-heading\]\]\:text-muted [cmdk-group-heading]{color:var(--cal-text-muted,#9ca3af)}.\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 [cmdk-group]:not([hidden])~[cmdk-group]{padding-top:0}.\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg{height:1.25rem}.\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg{width:1.25rem}.\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input]{height:3rem}.\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item]{padding-top:.75rem;padding-bottom:.75rem}.\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg{height:1.25rem}.\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg{width:1.25rem}.\[\&_a\]\:rounded-none a{border-radius:0}.\[\&_a\]\:border-l-0 a{border-left-width:0}.\[\&_a\]\:text-blue-500 a{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.\[\&_a\]\:underline a{text-decoration-line:underline}.\[\&_a\]\:hover\:text-blue-600:hover a{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.\[\&_button\]\:rounded-none button{border-radius:0}.\[\&_button\]\:border-l-0 button{border-left-width:0}.\[\&_iframe\]\:h-full iframe{height:100%}.\[\&_iframe\]\:min-h-\[315px\] iframe{min-height:315px}.\[\&_iframe\]\:w-full iframe{width:100%}.\[\&_img\]\:h-\[15px\] img{height:15px}.\[\&_input\]\:mb-0 input{margin-bottom:0}.\[\&_input\]\:leading-4 input{line-height:1rem}.\[\&_label\]\:my-1 label{margin-top:.25rem;margin-bottom:.25rem}.\[\&_label\]\:font-normal label{font-weight:400}.\[\&_svg\]\:invisible svg{visibility:hidden}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-width:0}.\[\&_tr\]\:border-b tr{border-bottom-width:1px}.\[\&_tr\]\:bg-subtle tr{background-color:var(--cal-bg-subtle)}@media(max-width:1270px){.\[\@media\(max-width\:1270px\)\]\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(max-width:260px){.\[\@media\(max-width\:260px\)\]\:w-full{width:100%}.\[\@media\(max-width\:260px\)\]\:justify-center{justify-content:center}}@media(max-width:500px){.\[\@media\(max-width\:500px\)\]\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}}@media(max-width:730px){.\[\@media\(max-width\:730px\)\]\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}}input:hover+.\[input\:hover_\+_\&\]\:border-emphasis{border-color:var(--cal-border-emphasis,#9ca3af)}input:hover+.\[input\:hover_\+_\&\]\:border-l-default{border-left-color:var(--cal-border,#d1d5db)}
\ No newline at end of file
diff --git a/packages/platform/atoms/hooks/useApiKeys.ts b/packages/platform/atoms/hooks/useApiKeys.ts
new file mode 100644
index 00000000000000..f3288a4e30f193
--- /dev/null
+++ b/packages/platform/atoms/hooks/useApiKeys.ts
@@ -0,0 +1,5 @@
+import { createContext, useContext } from "react";
+
+export const ApiKeyContext = createContext({ key: "", error: "" });
+
+export const useApiKey = () => useContext(ApiKeyContext);
diff --git a/packages/platform/atoms/hooks/useAtomsContext.ts b/packages/platform/atoms/hooks/useAtomsContext.ts
new file mode 100644
index 00000000000000..d963230a0868cf
--- /dev/null
+++ b/packages/platform/atoms/hooks/useAtomsContext.ts
@@ -0,0 +1,43 @@
+import { createContext, useContext } from "react";
+
+import type enTranslations from "@calcom/web/public/static/locales/en/common.json";
+
+import type http from "../lib/http";
+
+type translationKeys = keyof typeof enTranslations;
+export interface IAtomsContextOptions {
+ refreshUrl?: string;
+ apiUrl: string;
+}
+
+export interface IAtomsContext {
+ clientId: string;
+ accessToken?: string;
+ options: IAtomsContextOptions;
+ error?: string;
+ getClient: () => typeof http | void;
+ refreshToken?: string;
+ isRefreshing?: boolean;
+ isAuth: boolean;
+ isValidClient: boolean;
+ isInit: boolean;
+ t: (key: string, values: Record) => string;
+ i18n: {
+ language: string;
+ defaultLocale: string;
+ locales: [string];
+ exists: (key: translationKeys | string) => boolean;
+ };
+}
+
+export const AtomsContext = createContext({
+ clientId: "",
+ accessToken: "",
+ options: { refreshUrl: "", apiUrl: "" },
+ error: "",
+ getClient: () => {
+ return;
+ },
+} as IAtomsContext);
+
+export const useAtomsContext = () => useContext(AtomsContext);
diff --git a/packages/platform/atoms/hooks/useAvailableSlots.ts b/packages/platform/atoms/hooks/useAvailableSlots.ts
new file mode 100644
index 00000000000000..50eb3b43caa2ea
--- /dev/null
+++ b/packages/platform/atoms/hooks/useAvailableSlots.ts
@@ -0,0 +1,29 @@
+import { useQuery } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { AvailableSlotsType } from "@calcom/platform-libraries";
+import type { GetAvailableSlotsInput, ApiResponse, ApiSuccessResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+export const QUERY_KEY = "get-available-slots";
+
+export const useAvailableSlots = ({ enabled, ...rest }: GetAvailableSlotsInput & { enabled: boolean }) => {
+ const availableSlots = useQuery({
+ queryKey: [QUERY_KEY, rest.startTime, rest.endTime, rest.eventTypeId, rest.eventTypeSlug],
+ queryFn: () => {
+ return http
+ .get>("/slots/available", {
+ params: rest,
+ })
+ .then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return (res.data as ApiSuccessResponse).data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ enabled: enabled,
+ });
+ return availableSlots;
+};
diff --git a/packages/platform/atoms/hooks/useBookings.tsx b/packages/platform/atoms/hooks/useBookings.tsx
new file mode 100644
index 00000000000000..673754bc6cd362
--- /dev/null
+++ b/packages/platform/atoms/hooks/useBookings.tsx
@@ -0,0 +1,18 @@
+import { useState } from "react";
+
+import type { IUseBookings } from "@calcom/features/bookings/Booker/components/hooks/useBookings";
+import { useBookerStore } from "@calcom/features/bookings/Booker/store";
+
+export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBookings) => {
+ const eventSlug = useBookerStore((state) => state.eventSlug);
+ const setFormValues = useBookerStore((state) => state.setFormValues);
+ const rescheduleUid = useBookerStore((state) => state.rescheduleUid);
+ const bookingData = useBookerStore((state) => state.bookingData);
+ const timeslot = useBookerStore((state) => state.selectedTimeslot);
+ const seatedEventData = useBookerStore((state) => state.seatedEventData);
+ const [expiryTime, setExpiryTime] = useState();
+ const recurringEventCount = useBookerStore((state) => state.recurringEventCount);
+ const isInstantMeeting = useBookerStore((state) => state.isInstantMeeting);
+ const duration = useBookerStore((state) => state.selectedDuration);
+ const hasInstantMeetingTokenExpired = expiryTime && new Date(expiryTime) < new Date();
+};
diff --git a/packages/platform/atoms/hooks/useCalendarsBusyTimes.ts b/packages/platform/atoms/hooks/useCalendarsBusyTimes.ts
new file mode 100644
index 00000000000000..09015379e829f0
--- /dev/null
+++ b/packages/platform/atoms/hooks/useCalendarsBusyTimes.ts
@@ -0,0 +1,38 @@
+import { useQuery } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { ApiResponse, CalendarBusyTimesInput } from "@calcom/platform-types";
+import type { EventBusyDate } from "@calcom/types/Calendar";
+
+import http from "../lib/http";
+
+export const QUERY_KEY = "get-calendars-busy-times";
+
+type UseCalendarsBusyTimesProps = CalendarBusyTimesInput & { onError?: () => void; enabled: boolean };
+
+export const useCalendarsBusyTimes = ({ onError, enabled, ...rest }: UseCalendarsBusyTimesProps) => {
+ const availableSlots = useQuery({
+ queryKey: [
+ QUERY_KEY,
+ rest?.calendarsToLoad?.toString() ?? "",
+ rest.dateFrom ?? "",
+ rest.dateTo ?? "",
+ rest.loggedInUsersTz,
+ ],
+ queryFn: () => {
+ return http
+ .get>("/ee/calendars/busy-times", {
+ params: rest,
+ })
+ .then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data;
+ }
+ onError?.();
+ throw new Error(res.data.error.message);
+ });
+ },
+ enabled,
+ });
+ return availableSlots;
+};
diff --git a/packages/platform/atoms/hooks/useClientSchedule.ts b/packages/platform/atoms/hooks/useClientSchedule.ts
new file mode 100644
index 00000000000000..b099e25b14f9ae
--- /dev/null
+++ b/packages/platform/atoms/hooks/useClientSchedule.ts
@@ -0,0 +1,35 @@
+import { useQuery } from "@tanstack/react-query";
+
+import { BASE_URL, API_VERSION, V2_ENDPOINTS, SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { ScheduleWithAvailabilitiesForWeb } from "@calcom/platform-libraries";
+import type { ApiResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+export const QUERY_KEY = "user-schedule";
+
+const useClientSchedule = (id?: string) => {
+ const endpoint = new URL(BASE_URL);
+
+ endpoint.pathname = id
+ ? `api/${API_VERSION}/${V2_ENDPOINTS.availability}/${id}`
+ : `api/${API_VERSION}/${V2_ENDPOINTS.availability}/default`;
+
+ endpoint.searchParams.set("for", "atom");
+
+ const { isLoading, error, data } = useQuery({
+ queryKey: [QUERY_KEY, id],
+ queryFn: () => {
+ return http.get>(endpoint.toString()).then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ });
+
+ return { isLoading, error, data };
+};
+
+export default useClientSchedule;
diff --git a/packages/platform/atoms/hooks/useConnectedCalendars.tsx b/packages/platform/atoms/hooks/useConnectedCalendars.tsx
new file mode 100644
index 00000000000000..01eda5256e3fcf
--- /dev/null
+++ b/packages/platform/atoms/hooks/useConnectedCalendars.tsx
@@ -0,0 +1,31 @@
+import { useQuery } from "@tanstack/react-query";
+import { shallow } from "zustand/shallow";
+
+import { useOverlayCalendarStore } from "@calcom/features/bookings/Booker/components/OverlayCalendar/store";
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { ConnectedDestinationCalendars } from "@calcom/platform-libraries";
+import type { ApiResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+export const QUERY_KEY = "get-connected-calendars";
+export const useConnectedCalendars = () => {
+ const [calendarSettingsOverlay] = useOverlayCalendarStore(
+ (state) => [state.calendarSettingsOverlayModal, state.setCalendarSettingsOverlayModal],
+ shallow
+ );
+ const calendars = useQuery({
+ queryKey: [QUERY_KEY],
+ queryFn: () => {
+ return http.get>("/ee/calendars").then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ enabled: !!calendarSettingsOverlay,
+ });
+
+ return calendars;
+};
diff --git a/packages/platform/atoms/hooks/useCreateBooking.ts b/packages/platform/atoms/hooks/useCreateBooking.ts
new file mode 100644
index 00000000000000..521423dad0b316
--- /dev/null
+++ b/packages/platform/atoms/hooks/useCreateBooking.ts
@@ -0,0 +1,45 @@
+import { useMutation } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { BookingResponse } from "@calcom/platform-libraries";
+import type { ApiResponse, ApiErrorResponse, ApiSuccessResponse } from "@calcom/platform-types";
+import type { BookingCreateBody } from "@calcom/prisma/zod-utils";
+
+import http from "../lib/http";
+
+interface IUseCreateBooking {
+ onSuccess?: (res: ApiSuccessResponse) => void;
+ onError?: (err: ApiErrorResponse | Error) => void;
+}
+export const useCreateBooking = (
+ { onSuccess, onError }: IUseCreateBooking = {
+ onSuccess: () => {
+ return;
+ },
+ onError: () => {
+ return;
+ },
+ }
+) => {
+ const createBooking = useMutation, Error, BookingCreateBody>({
+ mutationFn: (data) => {
+ return http.post>("/ee/bookings", data).then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ onSuccess: (data) => {
+ if (data.status === SUCCESS_STATUS) {
+ onSuccess?.(data);
+ } else {
+ onError?.(data);
+ }
+ },
+ onError: (err) => {
+ onError?.(err);
+ },
+ });
+ return createBooking;
+};
diff --git a/packages/platform/atoms/hooks/useCreateInstantBooking.ts b/packages/platform/atoms/hooks/useCreateInstantBooking.ts
new file mode 100644
index 00000000000000..303b6f2e7fc066
--- /dev/null
+++ b/packages/platform/atoms/hooks/useCreateInstantBooking.ts
@@ -0,0 +1,45 @@
+import { useMutation } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { BookingResponse } from "@calcom/platform-libraries";
+import type { ApiResponse, ApiErrorResponse, ApiSuccessResponse } from "@calcom/platform-types";
+import type { BookingCreateBody } from "@calcom/prisma/zod-utils";
+
+import http from "../lib/http";
+
+interface IUseCreateInstantBooking {
+ onSuccess?: (res: ApiSuccessResponse) => void;
+ onError?: (err: ApiErrorResponse | Error) => void;
+}
+export const useCreateInstantBooking = (
+ { onSuccess, onError }: IUseCreateInstantBooking = {
+ onSuccess: () => {
+ return;
+ },
+ onError: () => {
+ return;
+ },
+ }
+) => {
+ const createInstantBooking = useMutation, Error, BookingCreateBody>({
+ mutationFn: (data) => {
+ return http.post>("/ee/bookings/instant", data).then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ onSuccess: (data) => {
+ if (data.status === SUCCESS_STATUS) {
+ onSuccess?.(data);
+ } else {
+ onError?.(data);
+ }
+ },
+ onError: (err) => {
+ onError?.(err);
+ },
+ });
+ return createInstantBooking;
+};
diff --git a/packages/platform/atoms/hooks/useCreateRecurringBooking.ts b/packages/platform/atoms/hooks/useCreateRecurringBooking.ts
new file mode 100644
index 00000000000000..87b537a47ccbcd
--- /dev/null
+++ b/packages/platform/atoms/hooks/useCreateRecurringBooking.ts
@@ -0,0 +1,46 @@
+import { useMutation } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { BookingResponse, RecurringBookingCreateBody } from "@calcom/platform-libraries";
+import type { ApiResponse, ApiErrorResponse, ApiSuccessResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+interface IUseCreateRecurringBooking {
+ onSuccess?: (res: ApiSuccessResponse) => void;
+ onError?: (err: ApiErrorResponse | Error) => void;
+}
+
+export const useCreateRecurringBooking = (
+ { onSuccess, onError }: IUseCreateRecurringBooking = {
+ onSuccess: () => {
+ return;
+ },
+ onError: () => {
+ return;
+ },
+ }
+) => {
+ const createRecurringBooking = useMutation<
+ ApiResponse,
+ Error,
+ RecurringBookingCreateBody[]
+ >({
+ mutationFn: (data) => {
+ return http
+ .post>("/ee/bookings/recurring", data)
+ .then((res) => res.data);
+ },
+ onSuccess: (data) => {
+ if (data.status === SUCCESS_STATUS) {
+ onSuccess?.(data);
+ } else {
+ onError?.(data);
+ }
+ },
+ onError: (err) => {
+ onError?.(err);
+ },
+ });
+ return createRecurringBooking;
+};
diff --git a/packages/platform/atoms/hooks/useDeleteSchedule.ts b/packages/platform/atoms/hooks/useDeleteSchedule.ts
new file mode 100644
index 00000000000000..b177849d304b20
--- /dev/null
+++ b/packages/platform/atoms/hooks/useDeleteSchedule.ts
@@ -0,0 +1,58 @@
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { BASE_URL, API_VERSION, V2_ENDPOINTS } from "@calcom/platform-constants";
+import type { ApiResponse, ApiErrorResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+import { QUERY_KEY } from "./useClientSchedule";
+
+interface IUseDeleteScheduleProps {
+ onSuccess?: (res: ApiResponse) => void;
+ onError?: (err: ApiErrorResponse) => void;
+}
+
+type DeleteScheduleInput = {
+ id: number;
+};
+
+const useDeleteSchedule = (
+ { onSuccess, onError }: IUseDeleteScheduleProps = {
+ onSuccess: () => {
+ return;
+ },
+ onError: () => {
+ return;
+ },
+ }
+) => {
+ const endpoint = new URL(BASE_URL);
+ const queryClient = useQueryClient();
+
+ const mutation = useMutation, unknown, DeleteScheduleInput>({
+ mutationFn: (data) => {
+ const { id } = data;
+ endpoint.pathname = `api/${API_VERSION}/${V2_ENDPOINTS.availability}/${id}`;
+ endpoint.searchParams.set("for", "atom");
+
+ return http?.delete(endpoint.toString()).then((res) => res.data);
+ },
+ onSuccess: (data) => {
+ if (data.status === SUCCESS_STATUS) {
+ onSuccess?.(data);
+ } else {
+ onError?.(data);
+ }
+ },
+ onError: (err) => {
+ onError?.(err as ApiErrorResponse);
+ },
+ onSettled: () => {
+ queryClient.invalidateQueries({ queryKey: [QUERY_KEY] });
+ },
+ });
+
+ return mutation;
+};
+
+export default useDeleteSchedule;
diff --git a/packages/platform/atoms/hooks/useDeleteSelectedSlot.ts b/packages/platform/atoms/hooks/useDeleteSelectedSlot.ts
new file mode 100644
index 00000000000000..d24877b85d48dc
--- /dev/null
+++ b/packages/platform/atoms/hooks/useDeleteSelectedSlot.ts
@@ -0,0 +1,52 @@
+import { useMutation } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type {
+ RemoveSelectedSlotInput,
+ ApiResponse,
+ ApiSuccessResponseWithoutData,
+ ApiErrorResponse,
+} from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+interface IUseDeleteSelectedSlot {
+ onSuccess?: (res: ApiSuccessResponseWithoutData) => void;
+ onError?: (err: ApiErrorResponse) => void;
+}
+export const useDeleteSelectedSlot = (
+ { onSuccess, onError }: IUseDeleteSelectedSlot = {
+ onSuccess: () => {
+ return;
+ },
+ onError: () => {
+ return;
+ },
+ }
+) => {
+ const deletedSlot = useMutation({
+ mutationFn: (props: RemoveSelectedSlotInput) => {
+ return http
+ .delete("/slots/selected-slot", {
+ params: props,
+ })
+ .then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ onSuccess: (data) => {
+ if (data.status === SUCCESS_STATUS) {
+ onSuccess?.(data);
+ } else {
+ onError?.(data);
+ }
+ },
+ onError: (err) => {
+ onError?.(err as ApiErrorResponse);
+ },
+ });
+ return deletedSlot;
+};
diff --git a/packages/platform/atoms/hooks/useGcal.ts b/packages/platform/atoms/hooks/useGcal.ts
new file mode 100644
index 00000000000000..fc7a8b5b8d7be3
--- /dev/null
+++ b/packages/platform/atoms/hooks/useGcal.ts
@@ -0,0 +1,35 @@
+import { useState, useEffect } from "react";
+
+import http from "../lib/http";
+
+export interface useGcalProps {
+ isAuth: boolean;
+}
+
+export const useGcal = ({ isAuth }: useGcalProps) => {
+ const [allowConnect, setAllowConnect] = useState(false);
+ const [checked, setChecked] = useState(false);
+
+ const redirectToGcalOAuth = () => {
+ http
+ ?.get("/platform/gcal/oauth/auth-url")
+ .then(({ data: responseBody }) => {
+ if (responseBody.data?.authUrl) {
+ window.location.href = responseBody.data.authUrl;
+ }
+ })
+ .catch(console.error);
+ };
+
+ useEffect(() => {
+ if (isAuth) {
+ http
+ ?.get("/platform/gcal/check")
+ .then(() => setAllowConnect(false))
+ .catch(() => setAllowConnect(true))
+ .finally(() => setChecked(true));
+ }
+ }, [isAuth]);
+
+ return { allowConnect, checked, redirectToGcalOAuth };
+};
diff --git a/packages/platform/atoms/hooks/useGetCityTimezones.ts b/packages/platform/atoms/hooks/useGetCityTimezones.ts
new file mode 100644
index 00000000000000..fe4f6753f4bac3
--- /dev/null
+++ b/packages/platform/atoms/hooks/useGetCityTimezones.ts
@@ -0,0 +1,22 @@
+import { useQuery } from "@tanstack/react-query";
+
+import { BASE_URL, API_VERSION } from "@calcom/platform-constants";
+
+import http from "../lib/http";
+
+const useGetCityTimezones = () => {
+ const endpoint = new URL(BASE_URL);
+
+ endpoint.pathname = `api/${API_VERSION}/schedules/time-zones`;
+
+ const { isLoading, data } = useQuery({
+ queryKey: ["city-timezones"],
+ queryFn: () => {
+ return http?.get(endpoint.toString()).then((res) => res.data);
+ },
+ });
+
+ return { isLoading, data };
+};
+
+export default useGetCityTimezones;
diff --git a/packages/platform/atoms/hooks/useHandleBookEvent.ts b/packages/platform/atoms/hooks/useHandleBookEvent.ts
new file mode 100644
index 00000000000000..52299ddd166071
--- /dev/null
+++ b/packages/platform/atoms/hooks/useHandleBookEvent.ts
@@ -0,0 +1,92 @@
+import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/components/hooks/useBookingForm";
+import { useBookerStore } from "@calcom/features/bookings/Booker/store";
+import type { useEventReturnType } from "@calcom/features/bookings/Booker/utils/event";
+import {
+ useTimePreferences,
+ mapBookingToMutationInput,
+ mapRecurringBookingToMutationInput,
+} from "@calcom/features/bookings/lib";
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import type { BookingCreateBody } from "@calcom/prisma/zod-utils";
+
+type UseHandleBookingProps = {
+ bookingForm: UseBookingFormReturnType["bookingForm"];
+ event: useEventReturnType;
+ metadata: Record;
+ hashedLink?: string | null;
+ handleBooking: (input: BookingCreateBody) => void;
+ handleInstantBooking: (input: BookingCreateBody) => void;
+ handleRecBooking: (input: BookingCreateBody[]) => void;
+};
+
+export const useHandleBookEvent = ({
+ bookingForm,
+ event,
+ metadata,
+ hashedLink,
+ handleBooking,
+ handleInstantBooking,
+ handleRecBooking,
+}: UseHandleBookingProps) => {
+ const setFormValues = useBookerStore((state) => state.setFormValues);
+ const timeslot = useBookerStore((state) => state.selectedTimeslot);
+ const duration = useBookerStore((state) => state.selectedDuration);
+ const { timezone } = useTimePreferences();
+ const rescheduleUid = useBookerStore((state) => state.rescheduleUid);
+ const { t, i18n } = useLocale();
+ const username = useBookerStore((state) => state.username);
+ const recurringEventCount = useBookerStore((state) => state.recurringEventCount);
+ const bookingData = useBookerStore((state) => state.bookingData);
+ const seatedEventData = useBookerStore((state) => state.seatedEventData);
+ const isInstantMeeting = useBookerStore((state) => state.isInstantMeeting);
+ const handleBookEvent = () => {
+ const values = bookingForm.getValues();
+ if (timeslot) {
+ // Clears form values stored in store, so old values won't stick around.
+ setFormValues({});
+ bookingForm.clearErrors();
+
+ // It shouldn't be possible that this method is fired without having event data,
+ // but since in theory (looking at the types) it is possible, we still handle that case.
+ if (!event?.data) {
+ bookingForm.setError("globalError", { message: t("error_booking_event") });
+ return;
+ }
+
+ // Ensures that duration is an allowed value, if not it defaults to the
+ // default event duration.
+ const validDuration = event.data.isDynamic
+ ? duration || event.data.length
+ : duration && event.data.metadata?.multipleDuration?.includes(duration)
+ ? duration
+ : event.data.length;
+
+ const bookingInput = {
+ values,
+ duration: validDuration,
+ event: event.data,
+ date: timeslot,
+ timeZone: timezone,
+ language: i18n.language,
+ rescheduleUid: rescheduleUid || undefined,
+ bookingUid: (bookingData && bookingData.uid) || seatedEventData?.bookingUid || undefined,
+ username: username || "",
+ metadata: metadata,
+ hashedLink,
+ };
+
+ if (isInstantMeeting) {
+ handleInstantBooking(mapBookingToMutationInput(bookingInput));
+ } else if (event.data?.recurringEvent?.freq && recurringEventCount && !rescheduleUid) {
+ handleRecBooking(mapRecurringBookingToMutationInput(bookingInput, recurringEventCount));
+ } else {
+ handleBooking(mapBookingToMutationInput(bookingInput));
+ }
+ // Clears form values stored in store, so old values won't stick around.
+ setFormValues({});
+ bookingForm.clearErrors();
+ }
+ };
+
+ return handleBookEvent;
+};
diff --git a/packages/platform/atoms/hooks/useIsPlatform.ts b/packages/platform/atoms/hooks/useIsPlatform.ts
new file mode 100644
index 00000000000000..259e7788665543
--- /dev/null
+++ b/packages/platform/atoms/hooks/useIsPlatform.ts
@@ -0,0 +1,6 @@
+import { useAtomsContext } from "@calcom/platform-atoms";
+
+export const useIsPlatform = () => {
+ const context = useAtomsContext();
+ return Boolean(context?.clientId);
+};
diff --git a/packages/platform/atoms/hooks/useMe.ts b/packages/platform/atoms/hooks/useMe.ts
new file mode 100644
index 00000000000000..663026d58e0982
--- /dev/null
+++ b/packages/platform/atoms/hooks/useMe.ts
@@ -0,0 +1,27 @@
+import { useQuery } from "@tanstack/react-query";
+
+import { BASE_URL, API_VERSION, V2_ENDPOINTS, SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { ApiResponse, UserResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+export const QUERY_KEY = "get-me";
+export const useMe = () => {
+ const endpoint = new URL(BASE_URL);
+
+ endpoint.pathname = `api/${API_VERSION}/${V2_ENDPOINTS.me}`;
+
+ const me = useQuery({
+ queryKey: [QUERY_KEY],
+ queryFn: () => {
+ return http?.get>(endpoint.toString()).then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ });
+
+ return me;
+};
diff --git a/packages/platform/atoms/hooks/useOAuthClient.ts b/packages/platform/atoms/hooks/useOAuthClient.ts
new file mode 100644
index 00000000000000..da8940c992cbf3
--- /dev/null
+++ b/packages/platform/atoms/hooks/useOAuthClient.ts
@@ -0,0 +1,47 @@
+import type { AxiosError } from "axios";
+import { useState, useEffect } from "react";
+import { usePrevious } from "react-use";
+
+import type { ApiResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+export interface useOAuthClientProps {
+ clientId: string;
+ apiUrl?: string;
+ refreshUrl?: string;
+ onError: (error: string) => void;
+ onSuccess: () => void;
+}
+export const useOAuthClient = ({ clientId, apiUrl, refreshUrl, onError, onSuccess }: useOAuthClientProps) => {
+ const prevClientId = usePrevious(clientId);
+ const [isInit, setIsInit] = useState(false);
+
+ useEffect(() => {
+ if (apiUrl && http.getUrl() !== apiUrl) {
+ http.setUrl(apiUrl);
+ setIsInit(true);
+ }
+ if (refreshUrl && http.getRefreshUrl() !== refreshUrl) {
+ http.setRefreshUrl(refreshUrl);
+ }
+ }, [apiUrl, refreshUrl]);
+
+ useEffect(() => {
+ if (clientId && http.getUrl() && prevClientId !== clientId) {
+ try {
+ http.get(`/platform/provider/${clientId}`).catch((err: AxiosError) => {
+ if (err.response?.status === 401) {
+ onError("Invalid oAuth Client.");
+ } else {
+ onSuccess();
+ }
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ }
+ }, [clientId, onError, prevClientId, onSuccess]);
+
+ return { isInit };
+};
diff --git a/packages/platform/atoms/hooks/useOAuthFlow.ts b/packages/platform/atoms/hooks/useOAuthFlow.ts
new file mode 100644
index 00000000000000..efd5f120b40271
--- /dev/null
+++ b/packages/platform/atoms/hooks/useOAuthFlow.ts
@@ -0,0 +1,83 @@
+import type { AxiosError, AxiosRequestConfig } from "axios";
+// eslint-disable-next-line no-restricted-imports
+import { debounce } from "lodash";
+import { useEffect, useState } from "react";
+import usePrevious from "react-use/lib/usePrevious";
+
+import type { ApiResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+export interface useOAuthProps {
+ accessToken?: string;
+ refreshUrl?: string;
+ onError?: (error: string) => void;
+ onSuccess?: () => void;
+ clientId: string;
+}
+
+const debouncedRefresh = debounce(http.refreshTokens, 10000, { leading: true, trailing: false });
+export const useOAuthFlow = ({ accessToken, refreshUrl, clientId, onError, onSuccess }: useOAuthProps) => {
+ const [isRefreshing, setIsRefreshing] = useState(false);
+ const [clientAccessToken, setClientAccessToken] = useState("");
+ const prevAccessToken = usePrevious(accessToken);
+ useEffect(() => {
+ const interceptorId =
+ clientAccessToken && http.getAuthorizationHeader()
+ ? http.responseInterceptor.use(undefined, async (err: AxiosError) => {
+ const originalRequest = err.config as AxiosRequestConfig;
+ if (refreshUrl && err.response?.status === 498 && !isRefreshing) {
+ setIsRefreshing(true);
+ const refreshedToken = await debouncedRefresh(refreshUrl);
+ if (refreshedToken) {
+ setClientAccessToken(refreshedToken);
+ onSuccess?.();
+ return http.instance({
+ ...originalRequest,
+ headers: { ...originalRequest.headers, Authorization: `Bearer ${refreshedToken}` },
+ });
+ } else {
+ onError?.("Invalid Refresh Token.");
+ }
+
+ setIsRefreshing(false);
+ }
+ return Promise.reject(err.response);
+ })
+ : "";
+
+ return () => {
+ if (interceptorId) {
+ http.responseInterceptor.eject(interceptorId);
+ }
+ };
+ }, [clientAccessToken, isRefreshing, refreshUrl, onError, onSuccess]);
+
+ useEffect(() => {
+ if (accessToken && http.getUrl() && prevAccessToken !== accessToken) {
+ http.setAuthorizationHeader(accessToken);
+ try {
+ http
+ .get(`/platform/provider/${clientId}/access-token`)
+ .catch(async (err: AxiosError) => {
+ if ((err.response?.status === 498 || err.response?.status === 401) && refreshUrl) {
+ setIsRefreshing(true);
+ const refreshedToken = await http.refreshTokens(refreshUrl);
+ if (refreshedToken) {
+ setClientAccessToken(refreshedToken);
+ onSuccess?.();
+ } else {
+ onError?.("Invalid Refresh Token.");
+ }
+ setIsRefreshing(false);
+ }
+ })
+ .finally(() => {
+ setClientAccessToken(accessToken);
+ });
+ } catch (err) {}
+ }
+ }, [accessToken, clientId, refreshUrl, prevAccessToken, onError, onSuccess]);
+
+ return { isRefreshing, currentAccessToken: clientAccessToken };
+};
diff --git a/packages/platform/atoms/hooks/usePublicEvent.tsx b/packages/platform/atoms/hooks/usePublicEvent.tsx
new file mode 100644
index 00000000000000..39182f78b9ab8d
--- /dev/null
+++ b/packages/platform/atoms/hooks/usePublicEvent.tsx
@@ -0,0 +1,41 @@
+import { useQuery } from "@tanstack/react-query";
+import { shallow } from "zustand/shallow";
+
+import { useBookerStore } from "@calcom/features/bookings/Booker/store";
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { PublicEventType } from "@calcom/platform-libraries";
+import type { ApiResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+export const QUERY_KEY = "get-public-event";
+export type UsePublicEventReturnType = ReturnType;
+
+export const usePublicEvent = (props: { username: string; eventSlug: string }) => {
+ const [username, eventSlug] = useBookerStore((state) => [state.username, state.eventSlug], shallow);
+ const isTeamEvent = useBookerStore((state) => state.isTeamEvent);
+ const org = useBookerStore((state) => state.org);
+
+ const event = useQuery({
+ queryKey: [QUERY_KEY, username ?? props.username, eventSlug ?? props.eventSlug],
+ queryFn: () => {
+ return http
+ .get>("/events/public", {
+ params: {
+ username: username ?? props.username,
+ eventSlug: eventSlug ?? props.eventSlug,
+ isTeamEvent,
+ org: org ?? null,
+ },
+ })
+ .then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data.data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ });
+
+ return event;
+};
diff --git a/packages/platform/atoms/hooks/useReserveSlot.ts b/packages/platform/atoms/hooks/useReserveSlot.ts
new file mode 100644
index 00000000000000..07d9a0bc0e02b1
--- /dev/null
+++ b/packages/platform/atoms/hooks/useReserveSlot.ts
@@ -0,0 +1,48 @@
+import { useMutation } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type {
+ ApiResponse,
+ ApiErrorResponse,
+ ApiSuccessResponse,
+ ReserveSlotInput,
+} from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+interface IUseReserveSlot {
+ onSuccess?: (res: ApiSuccessResponse) => void;
+ onError?: (err: ApiErrorResponse) => void;
+}
+export const useReserveSlot = (
+ { onSuccess, onError }: IUseReserveSlot = {
+ onSuccess: () => {
+ return;
+ },
+ onError: () => {
+ return;
+ },
+ }
+) => {
+ const reserveSlot = useMutation, unknown, ReserveSlotInput>({
+ mutationFn: (props: ReserveSlotInput) => {
+ return http.post>("/slots/reserve", props).then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ onSuccess: (data) => {
+ if (data.status === SUCCESS_STATUS) {
+ onSuccess?.(data);
+ } else {
+ onError?.(data);
+ }
+ },
+ onError: (err) => {
+ onError?.(err as ApiErrorResponse);
+ },
+ });
+ return reserveSlot;
+};
diff --git a/packages/platform/atoms/hooks/useSlots.ts b/packages/platform/atoms/hooks/useSlots.ts
new file mode 100644
index 00000000000000..0a1104d86ce3ff
--- /dev/null
+++ b/packages/platform/atoms/hooks/useSlots.ts
@@ -0,0 +1,71 @@
+import { useEffect } from "react";
+import { shallow } from "zustand/shallow";
+
+import dayjs from "@calcom/dayjs";
+import { useBookerStore } from "@calcom/features/bookings/Booker/store";
+import { useSlotReservationId } from "@calcom/features/bookings/Booker/useSlotReservationId";
+import type { useEventReturnType } from "@calcom/features/bookings/Booker/utils/event";
+import { MINUTES_TO_BOOK } from "@calcom/lib/constants";
+
+import { useDeleteSelectedSlot } from "./useDeleteSelectedSlot";
+import { useReserveSlot } from "./useReserveSlot";
+
+export type UseSlotsReturnType = ReturnType;
+
+export const useSlots = (event: useEventReturnType) => {
+ const selectedDuration = useBookerStore((state) => state.selectedDuration);
+ const [selectedTimeslot, setSelectedTimeslot] = useBookerStore(
+ (state) => [state.selectedTimeslot, state.setSelectedTimeslot],
+ shallow
+ );
+ const [slotReservationId, setSlotReservationId] = useSlotReservationId();
+ const reserveSlotMutation = useReserveSlot({
+ onSuccess: (res) => {
+ setSlotReservationId(res.data);
+ },
+ });
+
+ const removeSelectedSlot = useDeleteSelectedSlot();
+ const handleRemoveSlot = () => {
+ if (event?.data) {
+ removeSelectedSlot.mutate({ uid: slotReservationId ?? undefined });
+ }
+ };
+ const handleReserveSlot = () => {
+ if (event?.data?.id && selectedTimeslot && (selectedDuration || event?.data?.length)) {
+ reserveSlotMutation.mutate({
+ slotUtcStartDate: dayjs(selectedTimeslot).utc().format(),
+ eventTypeId: event.data.id,
+ slotUtcEndDate: dayjs(selectedTimeslot)
+ .utc()
+ .add(selectedDuration || event.data.length, "minutes")
+ .format(),
+ });
+ }
+ };
+
+ const timeslot = useBookerStore((state) => state.selectedTimeslot);
+
+ useEffect(() => {
+ handleReserveSlot();
+
+ const interval = setInterval(() => {
+ handleReserveSlot();
+ }, parseInt(MINUTES_TO_BOOK) * 60 * 1000 - 2000);
+
+ return () => {
+ handleRemoveSlot();
+ clearInterval(interval);
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [event?.data?.id, timeslot]);
+
+ return {
+ selectedTimeslot,
+ setSelectedTimeslot,
+ setSlotReservationId,
+ slotReservationId,
+ handleReserveSlot,
+ handleRemoveSlot,
+ };
+};
diff --git a/packages/platform/atoms/hooks/useTimezone.ts b/packages/platform/atoms/hooks/useTimezone.ts
new file mode 100644
index 00000000000000..742a2870c98a7c
--- /dev/null
+++ b/packages/platform/atoms/hooks/useTimezone.ts
@@ -0,0 +1,19 @@
+import { useEffect } from "react";
+
+import dayjs from "@calcom/dayjs";
+
+import { useMe } from "./useMe";
+
+export const useTimezone = (
+ onTimeZoneChange?: (currentTimezone: string) => void,
+ currentTimezone: string = dayjs.tz.guess()
+) => {
+ const { data: me, isLoading } = useMe();
+ const preferredTimezone = me?.data?.timeZone ?? currentTimezone;
+
+ useEffect(() => {
+ if (!isLoading && preferredTimezone && onTimeZoneChange && preferredTimezone !== currentTimezone) {
+ onTimeZoneChange(currentTimezone);
+ }
+ }, [currentTimezone, preferredTimezone, onTimeZoneChange, isLoading]);
+};
diff --git a/packages/platform/atoms/hooks/useUpdateSchedule.ts b/packages/platform/atoms/hooks/useUpdateSchedule.ts
new file mode 100644
index 00000000000000..c0d44f597c9fdc
--- /dev/null
+++ b/packages/platform/atoms/hooks/useUpdateSchedule.ts
@@ -0,0 +1,54 @@
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import { BASE_URL, API_VERSION, V2_ENDPOINTS } from "@calcom/platform-constants";
+import type { UpdateScheduleOutputType } from "@calcom/platform-libraries";
+import type { ApiResponse, UpdateScheduleInput, ApiErrorResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+import { QUERY_KEY as ScheduleQueryKey } from "./useClientSchedule";
+
+interface IPUpdateOAuthClient {
+ onSuccess?: (res: ApiResponse) => void;
+ onError?: (err: ApiErrorResponse) => void;
+}
+
+const useUpdateSchedule = (
+ { onSuccess, onError }: IPUpdateOAuthClient = {
+ onSuccess: () => {
+ return;
+ },
+ onError: () => {
+ return;
+ },
+ }
+) => {
+ const endpoint = new URL(BASE_URL);
+ const queryClient = useQueryClient();
+
+ const mutation = useMutation, unknown, UpdateScheduleInput>({
+ mutationFn: (data) => {
+ endpoint.pathname = `api/${API_VERSION}/${V2_ENDPOINTS.availability}/${data.scheduleId}`;
+ endpoint.searchParams.set("for", "atom");
+
+ return http.patch>(endpoint.toString(), data).then((res) => {
+ return res.data;
+ });
+ },
+ onSuccess: (data) => {
+ if (data.status === SUCCESS_STATUS) {
+ onSuccess?.(data);
+ queryClient.invalidateQueries({ queryKey: [ScheduleQueryKey] });
+ } else {
+ onError?.(data);
+ }
+ },
+ onError: (err) => {
+ onError?.(err as ApiErrorResponse);
+ },
+ });
+
+ return mutation;
+};
+
+export default useUpdateSchedule;
diff --git a/packages/platform/atoms/hooks/useUpdateUserTimezone.ts b/packages/platform/atoms/hooks/useUpdateUserTimezone.ts
new file mode 100644
index 00000000000000..597d7c4d6abdaa
--- /dev/null
+++ b/packages/platform/atoms/hooks/useUpdateUserTimezone.ts
@@ -0,0 +1,32 @@
+import { useMutation } from "@tanstack/react-query";
+
+import { BASE_URL, API_VERSION, V2_ENDPOINTS } from "@calcom/platform-constants";
+import { SUCCESS_STATUS } from "@calcom/platform-constants";
+import type { ApiResponse, UserResponse } from "@calcom/platform-types";
+
+import http from "../lib/http";
+
+type updateTimezoneInput = {
+ timeZone: string;
+};
+
+export const useUpdateUserTimezone = () => {
+ const endpoint = new URL(BASE_URL);
+
+ endpoint.pathname = `api/${API_VERSION}/${V2_ENDPOINTS.me}`;
+
+ const mutation = useMutation, unknown, updateTimezoneInput>({
+ mutationFn: (data) => {
+ const { timeZone } = data;
+
+ return http?.patch(endpoint.toString(), { timeZone }).then((res) => {
+ if (res.data.status === SUCCESS_STATUS) {
+ return res.data;
+ }
+ throw new Error(res.data.error.message);
+ });
+ },
+ });
+
+ return mutation;
+};
diff --git a/packages/platform/atoms/index.ts b/packages/platform/atoms/index.ts
new file mode 100644
index 00000000000000..3223d621d0d89f
--- /dev/null
+++ b/packages/platform/atoms/index.ts
@@ -0,0 +1,8 @@
+export { BookerWebWrapper as Booker } from "./booker";
+export { CalProvider } from "./cal-provider/CalProvider";
+export { AvailabilitySettings } from "./availability";
+export { useIsPlatform } from "./hooks/useIsPlatform";
+export { useAtomsContext } from "./hooks/useAtomsContext";
+export { useHandleBookEvent } from "./hooks/useHandleBookEvent";
+export * as Dialog from "./src/components/ui/dialog";
+export { Timezone } from "./timezone";
diff --git a/packages/platform/atoms/lib/getQueryParam.ts b/packages/platform/atoms/lib/getQueryParam.ts
new file mode 100644
index 00000000000000..e43357c3b3c683
--- /dev/null
+++ b/packages/platform/atoms/lib/getQueryParam.ts
@@ -0,0 +1,14 @@
+const getQueryParam = (paramName: string) => {
+ if (typeof window !== "undefined") {
+ const currentUrl = new URL(window.location.href);
+ const searchParams = currentUrl.searchParams;
+
+ const paramater = searchParams.get(paramName);
+
+ return paramater;
+ }
+
+ return undefined;
+};
+
+export default getQueryParam;
diff --git a/packages/platform/atoms/lib/http.ts b/packages/platform/atoms/lib/http.ts
new file mode 100644
index 00000000000000..bb089d9803b0fe
--- /dev/null
+++ b/packages/platform/atoms/lib/http.ts
@@ -0,0 +1,55 @@
+import axios from "axios";
+
+// Immediately Invoked Function Expression to create simple singleton class like
+
+const http = (function () {
+ const instance = axios.create({
+ timeout: 10000,
+ headers: {},
+ });
+ let refreshUrl = "";
+ return {
+ instance: instance,
+ get: instance.get,
+ post: instance.post,
+ put: instance.put,
+ patch: instance.patch,
+ delete: instance.delete,
+ responseInterceptor: instance.interceptors.response,
+ setRefreshUrl: (url: string) => {
+ refreshUrl = url;
+ },
+ getRefreshUrl: () => {
+ return refreshUrl;
+ },
+ setUrl: (url: string) => {
+ instance.defaults.baseURL = url;
+ },
+ getUrl: () => {
+ return instance.defaults.baseURL;
+ },
+ setAuthorizationHeader: (accessToken: string) => {
+ instance.defaults.headers.common["Authorization"] = `Bearer ${accessToken}`;
+ },
+ getAuthorizationHeader: () => {
+ return instance.defaults.headers.common?.["Authorization"]?.toString() ?? "";
+ },
+ refreshTokens: async (refreshUrl: string): Promise => {
+ const response = await fetch(`${refreshUrl}`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: http.getAuthorizationHeader(),
+ },
+ });
+ const res = await response.json();
+ if (res.accessToken) {
+ http.setAuthorizationHeader(res.accessToken);
+ return res.accessToken;
+ }
+ return "";
+ },
+ };
+})();
+
+export default http;
diff --git a/packages/platform/atoms/lib/setQueryParam.ts b/packages/platform/atoms/lib/setQueryParam.ts
new file mode 100644
index 00000000000000..2540bda73b12cd
--- /dev/null
+++ b/packages/platform/atoms/lib/setQueryParam.ts
@@ -0,0 +1,13 @@
+const setQueryParam = (paramName: string, paramValue: string, onParamChange?: () => void) => {
+ const currentUrl = new URL(window.location.href);
+ const params = new URLSearchParams(currentUrl.search);
+
+ params.set(paramName, paramValue);
+
+ currentUrl.search = params.toString();
+ window.history.replaceState({}, "", currentUrl.href);
+
+ !!onParamChange && onParamChange();
+};
+
+export default setQueryParam;
diff --git a/packages/platform/atoms/package.json b/packages/platform/atoms/package.json
new file mode 100644
index 00000000000000..f38ab32ebcc268
--- /dev/null
+++ b/packages/platform/atoms/package.json
@@ -0,0 +1,57 @@
+{
+ "name": "@calcom/platform-atoms",
+ "private": true,
+ "sideEffects": false,
+ "type": "module",
+ "description": "Cal.com Atoms",
+ "authors": "Cal.com, Inc.",
+ "version": "0.0.0",
+ "scripts": {
+ "build": "node build.mjs",
+ "vite-dev": "yarn vite build --watch & npx tailwindcss -i ./globals.css -o ./globals.min.css --postcss --minify --watch",
+ "vite-build": "yarn vite build && npx tailwindcss -i ./globals.css -o ./globals.min.css --postcss --minify"
+ },
+ "devDependencies": {
+ "@rollup/plugin-node-resolve": "^15.0.1",
+ "@types/node": "^20.3.1",
+ "@types/react": "18.0.26",
+ "@types/react-dom": "^18.0.9",
+ "@vitejs/plugin-react": "^2.2.0",
+ "rollup-plugin-node-builtins": "^2.1.2",
+ "typescript": "^4.9.4",
+ "vite": "^5.0.10",
+ "vite-plugin-dts": "^3.7.3"
+ },
+ "files": [
+ "dist"
+ ],
+ "main": "index.ts",
+ "module": "index.ts",
+ "exports": {
+ ".": {
+ "import": "./index.ts",
+ "require": "./index.ts"
+ },
+ "./components": {
+ "import": "./dist/cal-atoms.js",
+ "require": "./dist/cal-atoms.umd.cjs"
+ },
+ "./dist/globals.min.css": "./globals.min.css",
+ "./dist/index.ts": "./index.ts"
+ },
+ "dependencies": {
+ "@calcom/platform-constants": "*",
+ "@calcom/ui": "*",
+ "@radix-ui/react-dialog": "^1.0.4",
+ "@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-toast": "^1.1.5",
+ "@tanstack/react-query": "^5.17.15",
+ "class-variance-authority": "^0.4.0",
+ "clsx": "^2.0.0",
+ "lucide-react": "^0.171.0",
+ "react-use": "^17.4.2",
+ "tailwind-merge": "^1.13.2",
+ "tailwindcss": "^3.3.3",
+ "tailwindcss-animate": "^1.0.6"
+ }
+}
diff --git a/packages/atoms/postcss.config.js b/packages/platform/atoms/postcss.config.js
similarity index 100%
rename from packages/atoms/postcss.config.js
rename to packages/platform/atoms/postcss.config.js
diff --git a/packages/platform/atoms/src/components/ui/button.tsx b/packages/platform/atoms/src/components/ui/button.tsx
new file mode 100644
index 00000000000000..f44261ad80c240
--- /dev/null
+++ b/packages/platform/atoms/src/components/ui/button.tsx
@@ -0,0 +1,47 @@
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
+import * as React from "react";
+
+import { cn } from "../../lib/utils";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-10 px-4 py-2",
+ sm: "h-9 rounded-md px-3",
+ lg: "h-11 rounded-md px-8",
+ icon: "h-10 w-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+);
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean;
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button";
+ return ;
+ }
+);
+Button.displayName = "Button";
+
+export { Button, buttonVariants };
diff --git a/packages/platform/atoms/src/components/ui/confirmation-dialog-content.tsx b/packages/platform/atoms/src/components/ui/confirmation-dialog-content.tsx
new file mode 100644
index 00000000000000..0b23c94de7679f
--- /dev/null
+++ b/packages/platform/atoms/src/components/ui/confirmation-dialog-content.tsx
@@ -0,0 +1,69 @@
+import type { PropsWithChildren } from "react";
+
+import type { ConfirmationDialogContentProps } from "@calcom/ui";
+import { Button } from "@calcom/ui";
+import { AlertCircle, Check } from "@calcom/ui/components/icon";
+
+import { DialogContent, DialogClose, DialogTitle, DialogDescription, DialogHeader } from "./dialog";
+
+export function ConfirmationDialogContent(props: PropsWithChildren) {
+ const {
+ title,
+ variety,
+ confirmBtn = null,
+ confirmBtnText = "Confirm",
+ cancelBtnText = "Cancel",
+ loadingText = "Loading",
+ isPending = false,
+ onConfirm,
+ children,
+ } = props;
+ return (
+
+ <>
+
+ {variety && (
+
+ {variety === "danger" && (
+
+ )}
+ {variety === "warning" && (
+
+ )}
+ {variety === "success" && (
+
+
+
+ )}
+
+ )}
+
+ {title}
+ {children}
+
+
+
+ {confirmBtn ? (
+ confirmBtn
+ ) : (
+
+ onConfirm && onConfirm(e)}
+ data-testid="dialog-confirmation">
+ {isPending ? loadingText : confirmBtnText}
+
+
+ )}
+
+ {cancelBtnText}
+
+
+ >
+
+ );
+}
diff --git a/packages/platform/atoms/src/components/ui/dialog.tsx b/packages/platform/atoms/src/components/ui/dialog.tsx
new file mode 100644
index 00000000000000..763b73f183cd13
--- /dev/null
+++ b/packages/platform/atoms/src/components/ui/dialog.tsx
@@ -0,0 +1,101 @@
+import * as DialogPrimitive from "@radix-ui/react-dialog";
+import { X } from "lucide-react";
+import * as React from "react";
+
+import { cn } from "../../lib/utils";
+
+const Dialog = DialogPrimitive.Root;
+
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = DialogPrimitive.Portal;
+
+const DialogClose = DialogPrimitive.Close;
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ <>
+
+
+ {children}
+